Going ahead with the switch to tabs. This is a major coding standards change! If any unusual parser bugs show up, check this changeset. Converted all .php, .js, .tpl, .css, and .json files and did basic testing.
authorDan
Sun, 28 Mar 2010 23:10:46 -0400
changeset 1227 bdac73ed481e
parent 1226 de56132c008d
child 1228 9e9334417dbc
Going ahead with the switch to tabs. This is a major coding standards change! If any unusual parser bugs show up, check this changeset. Converted all .php, .js, .tpl, .css, and .json files and did basic testing.
ajax.php
cron.php
includes/cache.php
includes/captcha.php
includes/captcha/engine_default.php
includes/captcha/engine_failsafe.php
includes/captcha/engine_freecap.php
includes/captcha/engine_potpourri.php
includes/clientside/css/enano-shared-ie.css
includes/clientside/css/enano-shared.css
includes/clientside/jscompress.php
includes/clientside/jsres.php
includes/clientside/static/acl.js
includes/clientside/static/admin-menu.js
includes/clientside/static/ajax.js
includes/clientside/static/autofill.js
includes/clientside/static/comments.js
includes/clientside/static/crypto.js
includes/clientside/static/dropdown.js
includes/clientside/static/dynano.js
includes/clientside/static/editor.js
includes/clientside/static/enano-lib-basic.js
includes/clientside/static/expander.js
includes/clientside/static/fadefilter.js
includes/clientside/static/fat.js
includes/clientside/static/flyin.js
includes/clientside/static/functions.js
includes/clientside/static/grippy.js
includes/clientside/static/json.js
includes/clientside/static/l10n.js
includes/clientside/static/loader.js
includes/clientside/static/login.js
includes/clientside/static/messagebox.js
includes/clientside/static/paginate.js
includes/clientside/static/pwstrength.js
includes/clientside/static/rank-manager.js
includes/clientside/static/sliders.js
includes/clientside/static/template-compiler.js
includes/clientside/static/theme-manager.js
includes/clientside/static/tinymce-init.js
includes/clientside/static/toolbar.js
includes/clientside/static/userpage.js
includes/clientside/static/windows.js
includes/comment.php
includes/common.php
includes/common_cli.php
includes/constants.php
includes/dbal.php
includes/diffengine/Engine/native.php
includes/diffengine/Engine/string.php
includes/diffengine/Engine/xdiff.php
includes/diffengine/Renderer.php
includes/diffengine/Renderer/inline.php
includes/diffengine/Renderer/unified.php
includes/diffengine/Renderer/xhtml.php
includes/diffiehellman.php
includes/email.php
includes/functions.php
includes/hmac.php
includes/http.php
includes/js-compressor.php
includes/json.php
includes/json2.php
includes/lang.php
includes/log.php
includes/math.php
includes/namespaces/api.php
includes/namespaces/default.php
includes/namespaces/file.php
includes/namespaces/special.php
includes/namespaces/template.php
includes/namespaces/user.php
includes/output.php
includes/pageprocess.php
includes/pageutils.php
includes/paths.php
includes/plugins.php
includes/render.php
includes/rijndael.php
includes/search.php
includes/sessions.php
includes/sql_parse.php
includes/stats.php
includes/tagcloud.php
includes/template.php
includes/wikiengine/Tables.php
includes/wikiengine/TagSanitizer.php
includes/wikiengine/parse_mediawiki.php
includes/wikiengine/render_xhtml.php
includes/wikiformat.php
index.php
install/images/css/installer.css
install/includes/cli-core.php
install/includes/common.php
install/includes/libenanoinstall.php
install/includes/libenanoinstallcli.php
install/includes/payload.php
install/includes/stages/confirm.php
install/includes/stages/database.php
install/includes/stages/database_mysql.php
install/includes/stages/database_post.php
install/includes/stages/database_postgresql.php
install/includes/stages/finish.php
install/includes/stages/install.php
install/includes/stages/license.php
install/includes/stages/login.php
install/includes/stages/sysreqs.php
install/includes/stages/website.php
install/includes/ui.php
install/index.php
install/install.php
install/readme.php
install/schemas/upgrade/1.1.5-1.1.6.php
install/schemas/upgrade/1.1.6-1.1.7.php
install/schemas/upgrade/migration/1.0-1.1.php
install/upgrade.php
language/english/admin.json
language/english/core.json
language/english/install.json
language/english/meta.json
language/english/tools.json
language/english/user.json
plugins/PrivateMessages.php
plugins/SpecialAdmin.php
plugins/SpecialCSS.php
plugins/SpecialGroups.php
plugins/SpecialLog.php
plugins/SpecialPageFuncs.php
plugins/SpecialSearch.php
plugins/SpecialUpdownload.php
plugins/SpecialUserFuncs.php
plugins/SpecialUserPrefs.php
plugins/admin/CacheManager.php
plugins/admin/GroupManager.php
plugins/admin/Home.php
plugins/admin/LangManager.php
plugins/admin/PageEditor.php
plugins/admin/PageGroups.php
plugins/admin/PageManager.php
plugins/admin/PluginManager.php
plugins/admin/SecurityLog.php
plugins/admin/ThemeManager.php
plugins/admin/UserManager.php
plugins/admin/UserRanks.php
themes/admin/acledit.tpl
themes/admin/comment.tpl
themes/admin/css/default.css
themes/admin/elements.tpl
themes/admin/footer.tpl
themes/admin/header.tpl
themes/admin/simple-footer.tpl
themes/admin/simple-header.tpl
themes/enanium/acledit.tpl
themes/enanium/comment.tpl
themes/enanium/css-extra/ie6.css
themes/enanium/css/babygrand.css
themes/enanium/elements.tpl
themes/enanium/footer.tpl
themes/enanium/header.tpl
themes/enanium/simple-footer.tpl
themes/enanium/simple-header.tpl
themes/enanium/toolbar.tpl
themes/oxygen/acledit.tpl
themes/oxygen/comment.tpl
themes/oxygen/css/bleu.css
themes/oxygen/css/mint.css
themes/oxygen/elements.tpl
themes/oxygen/footer.tpl
themes/oxygen/header.tpl
themes/oxygen/simple-footer.tpl
themes/oxygen/simple-header.tpl
themes/oxygen/toolbar.tpl
themes/printable/acledit.tpl
themes/printable/comment.tpl
themes/printable/css-simple/bleu.css
themes/printable/css/default.css
themes/printable/elements.tpl
themes/printable/footer.tpl
themes/printable/header.tpl
themes/printable/simple-footer.tpl
themes/printable/simple-header.tpl
themes/printable/toolbar.tpl
themes/stpatty/acledit.tpl
themes/stpatty/comment.tpl
themes/stpatty/css-extra/ie-fixes-shamrock.css
themes/stpatty/css-extra/structure.css
themes/stpatty/css-simple/shamrock.css
themes/stpatty/css/shamrock.css
themes/stpatty/elements.tpl
themes/stpatty/footer.tpl
themes/stpatty/header.tpl
themes/stpatty/simple-footer.tpl
themes/stpatty/simple-header.tpl
themes/stpatty/toolbar.tpl
--- a/ajax.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/ajax.php	Sun Mar 28 23:10:46 2010 -0400
@@ -11,775 +11,775 @@
  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
  */
  
-  define('ENANO_INTERFACE_AJAX', '');
- 
-  require('includes/common.php');
-  
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if(!isset($_GET['_mode'])) die('This script cannot be accessed directly.');
-  
-  $_ob = '';
-  
-  switch($_GET['_mode']) {
-    case "checkusername":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::checkusername($_GET['name']);
-      break;
-    case "getsource":
-      header('Content-type: text/plain');
-      $password = ( isset($_GET['pagepass']) ) ? $_GET['pagepass'] : false;
-      $revid = ( isset($_GET['revid']) ) ? intval($_GET['revid']) : 0;
-      $page = new PageProcessor($paths->page_id, $paths->namespace, $revid);
-      $page->password = $password;
-      
-      $have_draft = false;
-      // Kinda hacky fix for issue 7: draft restore not offered for nonexistent pages
-      if ( ($src = $page->fetch_source()) || !$page->exists() )
-      {
-        $allowed = true;
-        $q = $db->sql_query('SELECT author, time_id, page_text, edit_summary, page_format FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\'
-                               AND page_id = \'' . $db->escape($paths->page_id) . '\'
-                               AND namespace = \'' . $db->escape($paths->namespace) . '\'
-                               AND is_draft = 1;');
-        if ( !$q )
-          $db->die_json();
-        
-        if ( $db->numrows() > 0 )
-        {
-          $have_draft = true;
-          $draft_row = $db->fetchrow($q);
-        }
-      }
-      else if ( $src !== false )
-      {
-        $allowed = true;
-        $src = '';
-      }
-      else
-      {
-        $allowed = false;
-        $src = '';
-      }
-      
-      $auth_edit = ( $session->get_permissions('edit_page') && ( $session->get_permissions('even_when_protected') || !$page->ns->page_protected ) );
-      $auth_wysiwyg = ( $session->get_permissions('edit_wysiwyg') );
-      
-      $return = array(
-          'mode' => 'editor',
-          'src' => $src,
-          'auth_view_source' => $allowed,
-          'auth_edit' => $auth_edit,
-          'time' => time(),
-          'require_captcha' => false,
-          'allow_wysiwyg' => $auth_wysiwyg,
-          'revid' => $revid,
-          'have_draft' => false
-        );
-      
-      $return['page_format'] = $page->ns->cdata['page_format'];
-      if ( $return['page_format'] == 'xhtml' )
-      {
-        // gently process headings to make tinymce format them correctly
-        if ( preg_match_all('/^ *?(={1,6}) *(.+?) *\\1 *$/m', $return['src'], $matches) )
-        {
-          foreach ( $matches[0] as $i => $match )
-          {
-            $hi = strlen($matches[1][$i]);
-            $heading = "<h{$hi}>{$matches[2][$i]}</h{$hi}>";
-            $return['src'] = str_replace_once($match, $heading, $return['src']);
-          }
-        }
-      }
-      
-      if ( $have_draft )
-      {
-        $row =& $draft_row;
-        $return['have_draft'] = true;
-        $return['draft_author'] = $row['author'];
-        $return['draft_time'] = enano_date(ED_DATE | ED_TIME, intval($row['time_id']));
-        if ( isset($_GET['get_draft']) && @$_GET['get_draft'] === '1' )
-        {
-          $return['src'] = $row['page_text'];
-          $return['edit_summary'] = $row['edit_summary'];
-          $return['page_format'] = $row['page_format'];
-        }
-      }
-      
-      $return['undo_info'] = array();
-      
-      if ( $revid > 0 )
-      {
-        // Retrieve information about this revision and the current one
-        $q = $db->sql_query('SELECT l1.author AS currentrev_author, l2.author AS oldrev_author FROM ' . table_prefix . 'logs AS l1
-  LEFT JOIN ' . table_prefix . 'logs AS l2
-    ON ( l2.log_id = ' . $revid . '
-         AND l2.log_type  = \'page\'
-         AND l2.action    = \'edit\'
-         AND l2.page_id   = \'' . $db->escape($paths->page_id)   . '\'
-         AND l2.namespace = \'' . $db->escape($paths->namespace) . '\'
-         AND l2.is_draft != 1
-        )
-  WHERE l1.log_type  = \'page\'
-    AND l1.action    = \'edit\'
-    AND l1.page_id   = \'' . $db->escape($paths->page_id)   . '\'
-    AND l1.namespace = \'' . $db->escape($paths->namespace) . '\'
-    AND l1.time_id   > ' . $page->revision_time . '
-    AND l1.is_draft != 1
-  ORDER BY l1.time_id DESC;');
-        if ( !$q )
-          $db->die_json();
-        
-        if ( $db->numrows() > 0 )
-        {
-          $rev_count = $db->numrows() - 1;
-          if ( $rev_count == -1 )
-          {
-            $return = array(
-                'mode' => 'error',
-                'error' => '[Internal] No rows returned by revision info query. SQL:<pre>' . $db->latest_query . '</pre>'
-              );
-          }
-          else
-          {
-            $row = $db->fetchrow();
-            $return['undo_info'] = array(
-              'old_author'     => $row['oldrev_author'],
-              'current_author' => $row['currentrev_author'],
-              'undo_count'     => $rev_count
-            );
-          }
-        }
-        else
-        {
-          $return['revid'] = $revid = 0;
-        }
-      }
-      
-      if ( $auth_edit && !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
-      {
-        $return['require_captcha'] = true;
-        $return['captcha_id'] = $session->make_captcha();
-      }
-      
-      $template->load_theme();
-      $return['toolbar_templates'] = $template->extract_vars('toolbar.tpl');
-      $return['edit_notice'] = $template->get_wiki_edit_notice();
-      
-      echo enano_json_encode($return);
-      break;
-    case "getpage":
-      // echo PageUtils::getpage($paths->page, false, ( (isset($_GET['oldid'])) ? $_GET['oldid'] : false ));
-      $output = new Output_Striptease();
-      
-      $revision_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
-      $page = new PageProcessor( $paths->page_id, $paths->namespace, $revision_id );
-      
-      $pagepass = ( isset($_REQUEST['pagepass']) ) ? $_REQUEST['pagepass'] : '';
-      $page->password = $pagepass;
-      $page->allow_redir = ( !isset($_GET['redirect']) || (isset($_GET['redirect']) && $_GET['redirect'] !== 'no') );
-            
-      $page->send();
-      break;
-    case "savepage":
-      /* **** OBSOLETE **** */
-      
-      break;
-    case "savepage_json":
-      header('Content-type: application/json');
-      if ( !isset($_POST['r']) )
-        die('Invalid request');
-      
-      try
-      {
-        $request = enano_json_decode($_POST['r']);
-        if ( !isset($request['src']) || !isset($request['summary']) || !isset($request['minor_edit']) || !isset($request['time']) || !isset($request['draft']) )
-          die('Invalid request');
-      }
-      catch(Zend_Json_Exception $e)
-      {
-        die("JSON parsing failed. View as HTML to see full report.\n<br /><br />\n<pre>" . htmlspecialchars(strval($e)) . "</pre><br />Request: <pre>" . htmlspecialchars($_POST['r']) . "</pre>");
-      }
-      
-      $time = intval($request['time']);
-      
-      if ( $request['draft'] )
-      {
-        //
-        // The user wants to save a draft version of the page.
-        //
-        
-        // Validate permissions
-        if ( !$session->get_permissions('edit_page') )
-        {
-          $return = array(
-            'mode' => 'error',
-            'error' => 'access_denied'
-          );
-        }
-        else
-        {
-          // Delete any draft copies if they exist
-          $q = $db->sql_query('DELETE FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\'
-                                 AND page_id = \'' . $db->escape($paths->page_id) . '\'
-                                 AND namespace = \'' . $db->escape($paths->namespace) . '\'
-                                 AND is_draft = 1;');
-          if ( !$q )
-            $db->die_json();
-          
-          // are we just supposed to delete the draft?
-          if ( $request['src'] === -1 )
-          {
-            $return = array(
-              'mode' => 'success',
-              'is_draft' => 'delete'
-            );
-          }
-          else
-          {
-            $src = RenderMan::preprocess_text($request['src'], false, false);
-            $draft_format = $request['format'];
-            if ( !in_array($draft_format, array('xhtml', 'wikitext')) )
-            {
-              $return = array(
-                'mode' => 'error',
-                'error' => 'invalid_format'
-              );
-            }
-            else
-            {
-              // Save the draft
-              $q = $db->sql_query('INSERT INTO ' . table_prefix . 'logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, page_text, is_draft, time_id, page_format )
-                                     VALUES (
-                                       \'page\',
-                                       \'edit\',
-                                       \'' . $db->escape($paths->page_id) . '\',
-                                       \'' . $db->escape($paths->namespace) . '\',
-                                       \'' . $db->escape($session->username) . '\',
-                                       ' . $session->user_id . ',
-                                       \'' . $db->escape($request['summary']) . '\',
-                                       \'' . $db->escape($src) . '\',
-                                       1,
-                                       ' . time() . ',
-                                       \'' . $draft_format . '\'
-                                     );');
-              
-              // Done!
-              $return = array(
-                  'mode' => 'success',
-                  'is_draft' => true
-                );
-            }
-          }
-        }
-      }
-      else
-      {
-        // Verify that no edits have been made since the editor was requested
-        $q = $db->sql_query('SELECT time_id, author FROM ' . table_prefix . "logs WHERE log_type = 'page' AND action = 'edit' AND page_id = '{$paths->page_id}' AND namespace = '{$paths->namespace}' AND is_draft != 1 ORDER BY time_id DESC LIMIT 1;");
-        if ( !$q )
-          $db->die_json();
-        
-        $row = $db->fetchrow();
-        $db->free_result();
-        
-        if ( $row['time_id'] > $time )
-        {
-          $return = array(
-            'mode' => 'obsolete',
-            'author' => $row['author'],
-            'date_string' => enano_date(ED_DATE | ED_TIME, $row['time_id']),
-            'time' => $row['time_id'] // time() ???
-            );
-          echo enano_json_encode($return);
-          break;
-        }
-        
-        // Verify captcha, if needed
-        if ( false && !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
-        {
-          if ( !isset($request['captcha_id']) || !isset($request['captcha_code']) )
-          {
-            die('Invalid request, need captcha metadata');
-          }
-          $code_correct = strtolower($session->get_captcha($request['captcha_id']));
-          $code_input = strtolower($request['captcha_code']);
-          if ( $code_correct !== $code_input )
-          {
-            $return = array(
-              'mode' => 'errors',
-              'errors' => array($lang->get('editor_err_captcha_wrong')),
-              'new_captcha' => $session->make_captcha()
-            );
-            echo enano_json_encode($return);
-            break;
-          }
-        }
-        
-        // Verification complete. Start the PageProcessor and let it do the dirty work for us.
-        $page = new PageProcessor($paths->page_id, $paths->namespace);
-        if ( $page->update_page($request['src'], $request['summary'], ( $request['minor_edit'] == 1 ), $request['format']) )
-        {
-          $return = array(
-              'mode' => 'success',
-              'is_draft' => false
-            );
-        }
-        else
-        {
-          $errors = array();
-          while ( $err = $page->pop_error() )
-          {
-            $errors[] = $err;
-          }
-          $return = array(
-            'mode' => 'errors',
-            'errors' => array_values($errors)
-            );
-          if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
-          {
-            $return['new_captcha'] = $session->make_captcha();
-          }
-        }
-      }
-      
-      // If this is based on a draft version, delete the draft - we no longer need it.
-      if ( @$request['used_draft'] && !$request['draft'] )
-      {
-        $q = $db->sql_query('DELETE FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\'
-                               AND page_id = \'' . $db->escape($paths->page_id) . '\'
-                               AND namespace = \'' . $db->escape($paths->namespace) . '\'
-                               AND is_draft = 1;');
-      }
-      
-      echo enano_json_encode($return);
-      
-      break;
-    case "diff_cur":
-      
-      // Lie about our content type to fool ad scripts
-      header('Content-type: application/xhtml+xml');
-      
-      if ( !isset($_POST['text']) )
-        die('Invalid request');
-      
-      $page = new PageProcessor($paths->page_id, $paths->namespace);
-      if ( !($src = $page->fetch_source()) )
-      {
-        die('Access denied');
-      }
-      
-      $diff = RenderMan::diff($src, $_POST['text']);
-      if ( $diff == '<table class="diff"></table>' )
-      {
-        $diff = '<p>' . $lang->get('editor_msg_diff_empty') . '</p>';
-      }
-      
-      echo '<div class="info-box">' . $lang->get('editor_msg_diff') . '</div>';
-      echo $diff;
-      
-      break;
-    case "protect":
-      // echo PageUtils::protect($paths->page_id, $paths->namespace, (int)$_POST['level'], $_POST['reason']);
-      
-      if ( @$_POST['reason'] === '__ROLLBACK__' )
-      {
-        // __ROLLBACK__ is a keyword for log entries.
-        die('"__ROLLBACK__" ain\'t gonna do it, buddy. Try to _not_ use reserved keywords next time, ok?');
-      }
-      
-      $page = new PageProcessor($paths->page_id, $paths->namespace);
-      header('Content-type: application/json');
-      
-      $result = $page->protect_page(intval($_POST['level']), $_POST['reason']);
-      echo enano_json_encode($result);
-      break;
-    case "histlist":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::histlist($paths->page_id, $paths->namespace);
-      break;
-    case "rollback":
-      $id = intval(@$_GET['id']);
-      $page = new PageProcessor($paths->page_id, $paths->namespace);
-      header('Content-type: application/json');
-      
-      $result = $page->rollback_log_entry($id);
-      echo enano_json_encode($result);
-      break;
-    case "comments":
-      require_once(ENANO_ROOT.'/includes/comment.php');
-      $comments = new Comments($paths->page_id, $paths->namespace);
-      if ( isset($_POST['data']) )
-      {
-        $comments->process_json($_POST['data']);
-      }
-      else
-      {
-        die('{ "mode" : "error", "error" : "No input" }');
-      }
-      break;
-    case "rename":
-      $page = new PageProcessor($paths->page_id, $paths->namespace);
-      header('Content-type: application/json');
-      
-      $result = $page->rename_page($_POST['newtitle']);
-      echo enano_json_encode($result);
-      break;
-    case "flushlogs":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::flushlogs($paths->page_id, $paths->namespace);
-      break;
-    case "deletepage":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      $reason = ( isset($_POST['reason']) ) ? $_POST['reason'] : false;
-      if ( empty($reason) )
-        die($lang->get('page_err_need_reason'));
-      echo PageUtils::deletepage($paths->page_id, $paths->namespace, $reason);
-      break;
-    case "delvote":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::delvote($paths->page_id, $paths->namespace);
-      break;
-    case "resetdelvotes":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::resetdelvotes($paths->page_id, $paths->namespace);
-      break;
-    case "getstyles":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::getstyles($_GET['id']);
-      break;
-    case "catedit":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::catedit($paths->page_id, $paths->namespace);
-      break;
-    case "catsave":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::catsave($paths->page_id, $paths->namespace, $_POST);
-      break;
-    case "setwikimode":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::setwikimode($paths->page_id, $paths->namespace, (int)$_GET['mode']);
-      break;
-    case "setpass":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      echo PageUtils::setpass($paths->page_id, $paths->namespace, $_POST['password']);
-      break;
-    case "fillusername":
-      break;
-    case "fillpagename":
-      break;
-    case "preview":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      $template->init_vars();
-      echo PageUtils::genPreview($_POST['text']);
-      break;
-    case "transform":
-      header('Content-type: text/javascript');
-      if ( !isset($_GET['to']) )
-      {
-        echo enano_json_encode(array(
-            'mode' => 'error',
-            'error' => '"to" not specified'
-          ));
-        break;
-      }
-      if ( !isset($_POST['text']) )
-      {
-        echo enano_json_encode(array(
-            'mode' => 'error',
-            'error' => '"text" not specified (must be on POST)'
-          ));
-        break;
-      }
-      switch($_GET['to'])
-      {
-        case 'xhtml':
-          $result = RenderMan::render($_POST['text'], RENDER_BLOCK | RENDER_NOSMILIES, false);
-          break;
-        case 'wikitext':
-          $result = RenderMan::reverse_render($_POST['text']);
-          break;
-        default:
-          $text =& $_POST['text'];
-          $result = false;
-          $code = $plugins->setHook('ajax_transform');
-          foreach ( $code as $cmd )
-          {
-            eval($cmd);
-          }
-          if ( !$result )
-          {
-            echo enano_json_encode(array(
-                'mode' => 'error',
-                'error' => 'Invalid target format'
-              ));
-            break;
-          }
-          break;
-      }
-      
-      // mostly for debugging, but I suppose this could be useful elsewhere.
-      if ( isset($_POST['plaintext']) )
-        die($result);
-      
-      echo enano_json_encode(array(
-          'mode' => 'transformed_text',
-          'text' => $result
-        ));
-      break;
-    case "pagediff":
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      $id1 = ( isset($_GET['diff1']) ) ? (int)$_GET['diff1'] : false;
-      $id2 = ( isset($_GET['diff2']) ) ? (int)$_GET['diff2'] : false;
-      if(!$id1 || !$id2) { echo '<p>Invalid request.</p>'; $template->footer(); break; }
-      if(!preg_match('#^([0-9]+)$#', (string)$_GET['diff1']) ||
-         !preg_match('#^([0-9]+)$#', (string)$_GET['diff2']  )) { echo '<p>SQL injection attempt</p>'; $template->footer(); break; }
-      echo PageUtils::pagediff($paths->page_id, $paths->namespace, $id1, $id2);
-      break;
-    case "jsres":
-      die('// ERROR: this section is deprecated and has moved to includes/clientside/static/enano-lib-basic.js.');
-      break;
-    case "rdns":
-      if(!$session->get_permissions('mod_misc')) die('Go somewhere else for your reverse DNS info!');
-      $ip = $_GET['ip'];
-      if ( !is_valid_ip($ip) )
-      {
-        echo $lang->get('acpsl_err_invalid_ip');
-      }
-      $rdns = gethostbyaddr($ip);
-      if ( $rdns == $ip )
-        echo $lang->get('acpsl_err_ptr_no_resolve');
-      else echo $rdns;
-      break;
-    case 'acljson':
-      require_once(ENANO_ROOT.'/includes/pageutils.php');
-      $parms = ( isset($_POST['acl_params']) ) ? rawurldecode($_POST['acl_params']) : false;
-      echo PageUtils::acl_json($parms);
-      break;
-    case 'theme_list':
-      header('Content-type: application/json');
-      
-      $return = array();
-      foreach ( $template->theme_list as $theme )
-      {
-        if ( $theme['enabled'] != 1 )
-          continue;
-        
-        $return[] = array(
-            'theme_name' => $theme['theme_name'],
-            'theme_id' => $theme['theme_id'],
-            'have_thumb' => file_exists(ENANO_ROOT . "/themes/{$theme['theme_id']}/preview.png")
-          );
-      }
-      
-      echo enano_json_encode($return);
-      
-      break;
-    case "get_styles":
-      if ( !preg_match('/^[a-z0-9_-]+$/', $_GET['theme_id']) )
-        die(enano_json_encode(array()));
-      
-      $theme_id = $_GET['theme_id'];
-      $return = array();
-      
-      if ( $dr = @opendir(ENANO_ROOT . "/themes/$theme_id/css/") )
-      {
-        while ( $dh = @readdir($dr) )
-        {
-          if ( preg_match('/\.css$/', $dh) && $dh != '_printable.css' )
-          {
-            $return[] = preg_replace('/\.css$/', '', $dh);
-          }
-        }
-      }
-      else
-      {
-        $return = array(
-            'mode' => 'error',
-            'error' => 'Could not open directory.'
-          );
-      }
-      echo enano_json_encode($return);
-      break;
-    case "change_theme":
-      if ( !isset($_POST['theme_id']) || !isset($_POST['style_id']) )
-      {
-        die(enano_json_encode(array('mode' => 'error', 'error' => 'Invalid parameter')));
-      }
-      if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['theme_id']) || !preg_match('/^([a-z0-9_-]+)$/i', $_POST['style_id']) )
-      {
-        die(enano_json_encode(array('mode' => 'error', 'error' => 'Invalid parameter')));
-      }
-      if ( !file_exists(ENANO_ROOT . '/themes/' . $_POST['theme_id'] . '/css/' . $_POST['style_id'] . '.css') )
-      {
-        die(enano_json_encode(array('mode' => 'error', 'error' => 'Can\'t find theme file: ' . ENANO_ROOT . '/themes/' . $_POST['theme_id'] . '/css/' . $_POST['style_id'] . '.css')));;
-      }
-      if ( !$session->user_logged_in )
-      {
-        die(enano_json_encode(array('mode' => 'error', 'error' => 'You must be logged in to change your theme')));
-      }
-      // Just in case something slipped through...
-      $theme_id = $db->escape($_POST['theme_id']);
-      $style_id = $db->escape($_POST['style_id']);
-      $e = $db->sql_query('UPDATE ' . table_prefix . "users SET theme = '$theme_id', style = '$style_id' WHERE user_id = $session->user_id;");
-      if ( !$e )
-        die( $db->get_error() );
-      
-      echo enano_json_encode(array(
-          'success' => true
-        ));
-      break;
-    case 'get_tags':
-      
-      $ret = array('tags' => array(), 'user_level' => $session->user_level, 'can_add' => $session->get_permissions('tag_create'));
-      $q = $db->sql_query('SELECT t.tag_id, t.tag_name, pg.pg_target IS NOT NULL AS used_in_acl, t.user_id FROM '.table_prefix.'tags AS t
-        LEFT JOIN '.table_prefix.'page_groups AS pg
-          ON ( ( pg.pg_type = ' . PAGE_GRP_TAGGED . ' AND pg.pg_target=t.tag_name ) OR ( pg.pg_type IS NULL AND pg.pg_target IS NULL ) )
-        WHERE t.page_id=\'' . $db->escape($paths->page_id) . '\' AND t.namespace=\'' . $db->escape($paths->namespace) . '\';');
-      if ( !$q )
-        $db->_die();
-      
-      while ( $row = $db->fetchrow() )
-      {
-        $can_del = true;
-        
-        $perm = ( $row['user_id'] != $session->user_id ) ?
-                'tag_delete_other' :
-                'tag_delete_own';
-        
-        if ( $row['user_id'] == 1 && !$session->user_logged_in )
-          // anonymous user trying to delete tag (hardcode blacklisted)
-          $can_del = false;
-          
-        if ( !$session->get_permissions($perm) )
-          $can_del = false;
-        
-        if ( $row['used_in_acl'] == 1 && !$session->get_permissions('edit_acl') && $session->user_level < USER_LEVEL_ADMIN )
-          $can_del = false;
-        
-        $ret['tags'][] = array(
-          'id' => $row['tag_id'],
-          'name' => $row['tag_name'],
-          'can_del' => $can_del,
-          'acl' => ( $row['used_in_acl'] == 1 )
-        );
-      }
-      
-      echo enano_json_encode($ret);
-      
-      break;
-    case 'addtag':
-      $resp = array(
-          'success' => false,
-          'error' => 'No error',
-          'can_del' => ( $session->get_permissions('tag_delete_own') && $session->user_logged_in ),
-          'in_acl' => false
-        );
-      
-      // first of course, are we allowed to tag pages?
-      if ( !$session->get_permissions('tag_create') )
-      {
-        $resp['error'] = 'You are not permitted to tag pages.';
-        die(enano_json_encode($resp));
-      }
-      
-      // sanitize the tag name
-      $tag = sanitize_tag($_POST['tag']);
-      $tag = $db->escape($tag);
-      
-      if ( strlen($tag) < 2 )
-      {
-        $resp['error'] = 'Tags must consist of at least 2 alphanumeric characters.';
-        die(enano_json_encode($resp));
-      }
-      
-      // check if tag is already on page
-      $q = $db->sql_query('SELECT 1 FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $db->escape($paths->namespace) . '\' AND tag_name=\'' . $tag . '\';');
-      if ( !$q )
-        $db->_die();
-      if ( $db->numrows() > 0 )
-      {
-        $resp['error'] = 'This page already has this tag.';
-        die(enano_json_encode($resp));
-      }
-      $db->free_result();
-      
-      // tricky: make sure this tag isn't being used in some page group, and thus adding it could affect page access
-      $can_edit_acl = ( $session->get_permissions('edit_acl') || $session->user_level >= USER_LEVEL_ADMIN );
-      $q = $db->sql_query('SELECT 1 FROM '.table_prefix.'page_groups WHERE pg_type=' . PAGE_GRP_TAGGED . ' AND pg_target=\'' . $tag . '\';');
-      if ( !$q )
-        $db->_die();
-      if ( $db->numrows() > 0 && !$can_edit_acl )
-      {
-        $resp['error'] = 'This tag is used in an ACL page group, and thus can\'t be added to a page by people without administrator privileges.';
-        die(enano_json_encode($resp));
-      }
-      $resp['in_acl'] = ( $db->numrows() > 0 );
-      $db->free_result();
-      
-      // we're good
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'tags(tag_name,page_id,namespace,user_id) VALUES(\'' . $tag . '\', \'' . $db->escape($paths->page_id) . '\', \'' . $db->escape($paths->namespace) . '\', ' . $session->user_id . ');');
-      if ( !$q )
-        $db->_die();
-      
-      $resp['success'] = true;
-      $resp['tag'] = $tag;
-      $resp['tag_id'] = $db->insert_id();
-      
-      echo enano_json_encode($resp);
-      break;
-    case 'deltag':
-      
-      $tag_id = intval($_POST['tag_id']);
-      if ( empty($tag_id) )
-        die('Invalid tag ID');
-      
-      $q = $db->sql_query('SELECT t.tag_id, t.user_id, t.page_id, t.namespace, pg.pg_target IS NOT NULL AS used_in_acl FROM '.table_prefix.'tags AS t
-  LEFT JOIN '.table_prefix.'page_groups AS pg
-    ON ( pg.pg_id IS NULL OR ( pg.pg_target = t.tag_name AND pg.pg_type = ' . PAGE_GRP_TAGGED . ' ) )
-  WHERE t.tag_id=' . $tag_id . ';');
-      
-      if ( !$q )
-        $db->_die();
-      
-      if ( $db->numrows() < 1 )
-        die('Could not find a tag with that ID');
-      
-      $row = $db->fetchrow();
-      $db->free_result();
-      
-      if ( $row['page_id'] == $paths->page_id && $row['namespace'] == $paths->namespace )
-        $perms =& $session;
-      else
-        $perms = $session->fetch_page_acl($row['page_id'], $row['namespace']);
-        
-      $perm = ( $row['user_id'] != $session->user_id ) ?
-                'tag_delete_other' :
-                'tag_delete_own';
-      
-      if ( $row['user_id'] == 1 && !$session->user_logged_in )
-        // anonymous user trying to delete tag (hardcode blacklisted)
-        die('You are not authorized to delete this tag.');
-        
-      if ( !$perms->get_permissions($perm) )
-        die('You are not authorized to delete this tag.');
-      
-      if ( $row['used_in_acl'] == 1 && !$perms->get_permissions('edit_acl') && $session->user_level < USER_LEVEL_ADMIN )
-        die('You are not authorized to delete this tag.');
-      
-      // We're good
-      $q = $db->sql_query('DELETE FROM '.table_prefix.'tags WHERE tag_id = ' . $tag_id . ';');
-      if ( !$q )
-        $db->_die();
-      
-      echo 'success';
-      
-      break;
-    case 'ping':
-      echo 'pong';
-      break;
-    default:
-      die('Hacking attempt');
-      break;
-  }
-  
+define('ENANO_INTERFACE_AJAX', '');
+
+require('includes/common.php');
+
+global $db, $session, $paths, $template, $plugins; // Common objects
+if(!isset($_GET['_mode'])) die('This script cannot be accessed directly.');
+
+$_ob = '';
+
+switch($_GET['_mode']) {
+	case "checkusername":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::checkusername($_GET['name']);
+		break;
+	case "getsource":
+		header('Content-type: text/plain');
+		$password = ( isset($_GET['pagepass']) ) ? $_GET['pagepass'] : false;
+		$revid = ( isset($_GET['revid']) ) ? intval($_GET['revid']) : 0;
+		$page = new PageProcessor($paths->page_id, $paths->namespace, $revid);
+		$page->password = $password;
+		
+		$have_draft = false;
+		// Kinda hacky fix for issue 7: draft restore not offered for nonexistent pages
+		if ( ($src = $page->fetch_source()) || !$page->exists() )
+		{
+			$allowed = true;
+			$q = $db->sql_query('SELECT author, time_id, page_text, edit_summary, page_format FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\'
+														AND page_id = \'' . $db->escape($paths->page_id) . '\'
+														AND namespace = \'' . $db->escape($paths->namespace) . '\'
+														AND is_draft = 1;');
+			if ( !$q )
+				$db->die_json();
+			
+			if ( $db->numrows() > 0 )
+			{
+				$have_draft = true;
+				$draft_row = $db->fetchrow($q);
+			}
+		}
+		else if ( $src !== false )
+		{
+			$allowed = true;
+			$src = '';
+		}
+		else
+		{
+			$allowed = false;
+			$src = '';
+		}
+		
+		$auth_edit = ( $session->get_permissions('edit_page') && ( $session->get_permissions('even_when_protected') || !$page->ns->page_protected ) );
+		$auth_wysiwyg = ( $session->get_permissions('edit_wysiwyg') );
+		
+		$return = array(
+				'mode' => 'editor',
+				'src' => $src,
+				'auth_view_source' => $allowed,
+				'auth_edit' => $auth_edit,
+				'time' => time(),
+				'require_captcha' => false,
+				'allow_wysiwyg' => $auth_wysiwyg,
+				'revid' => $revid,
+				'have_draft' => false
+			);
+		
+		$return['page_format'] = $page->ns->cdata['page_format'];
+		if ( $return['page_format'] == 'xhtml' )
+		{
+			// gently process headings to make tinymce format them correctly
+			if ( preg_match_all('/^ *?(={1,6}) *(.+?) *\\1 *$/m', $return['src'], $matches) )
+			{
+				foreach ( $matches[0] as $i => $match )
+				{
+					$hi = strlen($matches[1][$i]);
+					$heading = "<h{$hi}>{$matches[2][$i]}</h{$hi}>";
+					$return['src'] = str_replace_once($match, $heading, $return['src']);
+				}
+			}
+		}
+		
+		if ( $have_draft )
+		{
+			$row =& $draft_row;
+			$return['have_draft'] = true;
+			$return['draft_author'] = $row['author'];
+			$return['draft_time'] = enano_date(ED_DATE | ED_TIME, intval($row['time_id']));
+			if ( isset($_GET['get_draft']) && @$_GET['get_draft'] === '1' )
+			{
+				$return['src'] = $row['page_text'];
+				$return['edit_summary'] = $row['edit_summary'];
+				$return['page_format'] = $row['page_format'];
+			}
+		}
+		
+		$return['undo_info'] = array();
+		
+		if ( $revid > 0 )
+		{
+			// Retrieve information about this revision and the current one
+			$q = $db->sql_query('SELECT l1.author AS currentrev_author, l2.author AS oldrev_author FROM ' . table_prefix . 'logs AS l1
+LEFT JOIN ' . table_prefix . 'logs AS l2
+	ON ( l2.log_id = ' . $revid . '
+			AND l2.log_type  = \'page\'
+			AND l2.action    = \'edit\'
+			AND l2.page_id   = \'' . $db->escape($paths->page_id)   . '\'
+			AND l2.namespace = \'' . $db->escape($paths->namespace) . '\'
+			AND l2.is_draft != 1
+			)
+WHERE l1.log_type  = \'page\'
+	AND l1.action    = \'edit\'
+	AND l1.page_id   = \'' . $db->escape($paths->page_id)   . '\'
+	AND l1.namespace = \'' . $db->escape($paths->namespace) . '\'
+	AND l1.time_id   > ' . $page->revision_time . '
+	AND l1.is_draft != 1
+ORDER BY l1.time_id DESC;');
+			if ( !$q )
+				$db->die_json();
+			
+			if ( $db->numrows() > 0 )
+			{
+				$rev_count = $db->numrows() - 1;
+				if ( $rev_count == -1 )
+				{
+					$return = array(
+							'mode' => 'error',
+							'error' => '[Internal] No rows returned by revision info query. SQL:<pre>' . $db->latest_query . '</pre>'
+						);
+				}
+				else
+				{
+					$row = $db->fetchrow();
+					$return['undo_info'] = array(
+						'old_author'     => $row['oldrev_author'],
+						'current_author' => $row['currentrev_author'],
+						'undo_count'     => $rev_count
+					);
+				}
+			}
+			else
+			{
+				$return['revid'] = $revid = 0;
+			}
+		}
+		
+		if ( $auth_edit && !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
+		{
+			$return['require_captcha'] = true;
+			$return['captcha_id'] = $session->make_captcha();
+		}
+		
+		$template->load_theme();
+		$return['toolbar_templates'] = $template->extract_vars('toolbar.tpl');
+		$return['edit_notice'] = $template->get_wiki_edit_notice();
+		
+		echo enano_json_encode($return);
+		break;
+	case "getpage":
+		// echo PageUtils::getpage($paths->page, false, ( (isset($_GET['oldid'])) ? $_GET['oldid'] : false ));
+		$output = new Output_Striptease();
+		
+		$revision_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
+		$page = new PageProcessor( $paths->page_id, $paths->namespace, $revision_id );
+		
+		$pagepass = ( isset($_REQUEST['pagepass']) ) ? $_REQUEST['pagepass'] : '';
+		$page->password = $pagepass;
+		$page->allow_redir = ( !isset($_GET['redirect']) || (isset($_GET['redirect']) && $_GET['redirect'] !== 'no') );
+					
+		$page->send();
+		break;
+	case "savepage":
+		/* **** OBSOLETE **** */
+		
+		break;
+	case "savepage_json":
+		header('Content-type: application/json');
+		if ( !isset($_POST['r']) )
+			die('Invalid request');
+		
+		try
+		{
+			$request = enano_json_decode($_POST['r']);
+			if ( !isset($request['src']) || !isset($request['summary']) || !isset($request['minor_edit']) || !isset($request['time']) || !isset($request['draft']) )
+				die('Invalid request');
+		}
+		catch(Zend_Json_Exception $e)
+		{
+			die("JSON parsing failed. View as HTML to see full report.\n<br /><br />\n<pre>" . htmlspecialchars(strval($e)) . "</pre><br />Request: <pre>" . htmlspecialchars($_POST['r']) . "</pre>");
+		}
+		
+		$time = intval($request['time']);
+		
+		if ( $request['draft'] )
+		{
+			//
+			// The user wants to save a draft version of the page.
+			//
+			
+			// Validate permissions
+			if ( !$session->get_permissions('edit_page') )
+			{
+				$return = array(
+					'mode' => 'error',
+					'error' => 'access_denied'
+				);
+			}
+			else
+			{
+				// Delete any draft copies if they exist
+				$q = $db->sql_query('DELETE FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\'
+															AND page_id = \'' . $db->escape($paths->page_id) . '\'
+															AND namespace = \'' . $db->escape($paths->namespace) . '\'
+															AND is_draft = 1;');
+				if ( !$q )
+					$db->die_json();
+				
+				// are we just supposed to delete the draft?
+				if ( $request['src'] === -1 )
+				{
+					$return = array(
+						'mode' => 'success',
+						'is_draft' => 'delete'
+					);
+				}
+				else
+				{
+					$src = RenderMan::preprocess_text($request['src'], false, false);
+					$draft_format = $request['format'];
+					if ( !in_array($draft_format, array('xhtml', 'wikitext')) )
+					{
+						$return = array(
+							'mode' => 'error',
+							'error' => 'invalid_format'
+						);
+					}
+					else
+					{
+						// Save the draft
+						$q = $db->sql_query('INSERT INTO ' . table_prefix . 'logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, page_text, is_draft, time_id, page_format )
+												VALUES (
+													\'page\',
+													\'edit\',
+													\'' . $db->escape($paths->page_id) . '\',
+													\'' . $db->escape($paths->namespace) . '\',
+													\'' . $db->escape($session->username) . '\',
+													' . $session->user_id . ',
+													\'' . $db->escape($request['summary']) . '\',
+													\'' . $db->escape($src) . '\',
+													1,
+													' . time() . ',
+													\'' . $draft_format . '\'
+												);');
+						
+						// Done!
+						$return = array(
+								'mode' => 'success',
+								'is_draft' => true
+							);
+					}
+				}
+			}
+		}
+		else
+		{
+			// Verify that no edits have been made since the editor was requested
+			$q = $db->sql_query('SELECT time_id, author FROM ' . table_prefix . "logs WHERE log_type = 'page' AND action = 'edit' AND page_id = '{$paths->page_id}' AND namespace = '{$paths->namespace}' AND is_draft != 1 ORDER BY time_id DESC LIMIT 1;");
+			if ( !$q )
+				$db->die_json();
+			
+			$row = $db->fetchrow();
+			$db->free_result();
+			
+			if ( $row['time_id'] > $time )
+			{
+				$return = array(
+					'mode' => 'obsolete',
+					'author' => $row['author'],
+					'date_string' => enano_date(ED_DATE | ED_TIME, $row['time_id']),
+					'time' => $row['time_id'] // time() ???
+					);
+				echo enano_json_encode($return);
+				break;
+			}
+			
+			// Verify captcha, if needed
+			if ( false && !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
+			{
+				if ( !isset($request['captcha_id']) || !isset($request['captcha_code']) )
+				{
+					die('Invalid request, need captcha metadata');
+				}
+				$code_correct = strtolower($session->get_captcha($request['captcha_id']));
+				$code_input = strtolower($request['captcha_code']);
+				if ( $code_correct !== $code_input )
+				{
+					$return = array(
+						'mode' => 'errors',
+						'errors' => array($lang->get('editor_err_captcha_wrong')),
+						'new_captcha' => $session->make_captcha()
+					);
+					echo enano_json_encode($return);
+					break;
+				}
+			}
+			
+			// Verification complete. Start the PageProcessor and let it do the dirty work for us.
+			$page = new PageProcessor($paths->page_id, $paths->namespace);
+			if ( $page->update_page($request['src'], $request['summary'], ( $request['minor_edit'] == 1 ), $request['format']) )
+			{
+				$return = array(
+						'mode' => 'success',
+						'is_draft' => false
+					);
+			}
+			else
+			{
+				$errors = array();
+				while ( $err = $page->pop_error() )
+				{
+					$errors[] = $err;
+				}
+				$return = array(
+					'mode' => 'errors',
+					'errors' => array_values($errors)
+					);
+				if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
+				{
+					$return['new_captcha'] = $session->make_captcha();
+				}
+			}
+		}
+		
+		// If this is based on a draft version, delete the draft - we no longer need it.
+		if ( @$request['used_draft'] && !$request['draft'] )
+		{
+			$q = $db->sql_query('DELETE FROM ' . table_prefix . 'logs WHERE log_type = \'page\' AND action = \'edit\'
+														AND page_id = \'' . $db->escape($paths->page_id) . '\'
+														AND namespace = \'' . $db->escape($paths->namespace) . '\'
+														AND is_draft = 1;');
+		}
+		
+		echo enano_json_encode($return);
+		
+		break;
+	case "diff_cur":
+		
+		// Lie about our content type to fool ad scripts
+		header('Content-type: application/xhtml+xml');
+		
+		if ( !isset($_POST['text']) )
+			die('Invalid request');
+		
+		$page = new PageProcessor($paths->page_id, $paths->namespace);
+		if ( !($src = $page->fetch_source()) )
+		{
+			die('Access denied');
+		}
+		
+		$diff = RenderMan::diff($src, $_POST['text']);
+		if ( $diff == '<table class="diff"></table>' )
+		{
+			$diff = '<p>' . $lang->get('editor_msg_diff_empty') . '</p>';
+		}
+		
+		echo '<div class="info-box">' . $lang->get('editor_msg_diff') . '</div>';
+		echo $diff;
+		
+		break;
+	case "protect":
+		// echo PageUtils::protect($paths->page_id, $paths->namespace, (int)$_POST['level'], $_POST['reason']);
+		
+		if ( @$_POST['reason'] === '__ROLLBACK__' )
+		{
+			// __ROLLBACK__ is a keyword for log entries.
+			die('"__ROLLBACK__" ain\'t gonna do it, buddy. Try to _not_ use reserved keywords next time, ok?');
+		}
+		
+		$page = new PageProcessor($paths->page_id, $paths->namespace);
+		header('Content-type: application/json');
+		
+		$result = $page->protect_page(intval($_POST['level']), $_POST['reason']);
+		echo enano_json_encode($result);
+		break;
+	case "histlist":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::histlist($paths->page_id, $paths->namespace);
+		break;
+	case "rollback":
+		$id = intval(@$_GET['id']);
+		$page = new PageProcessor($paths->page_id, $paths->namespace);
+		header('Content-type: application/json');
+		
+		$result = $page->rollback_log_entry($id);
+		echo enano_json_encode($result);
+		break;
+	case "comments":
+		require_once(ENANO_ROOT.'/includes/comment.php');
+		$comments = new Comments($paths->page_id, $paths->namespace);
+		if ( isset($_POST['data']) )
+		{
+			$comments->process_json($_POST['data']);
+		}
+		else
+		{
+			die('{ "mode" : "error", "error" : "No input" }');
+		}
+		break;
+	case "rename":
+		$page = new PageProcessor($paths->page_id, $paths->namespace);
+		header('Content-type: application/json');
+		
+		$result = $page->rename_page($_POST['newtitle']);
+		echo enano_json_encode($result);
+		break;
+	case "flushlogs":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::flushlogs($paths->page_id, $paths->namespace);
+		break;
+	case "deletepage":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		$reason = ( isset($_POST['reason']) ) ? $_POST['reason'] : false;
+		if ( empty($reason) )
+			die($lang->get('page_err_need_reason'));
+		echo PageUtils::deletepage($paths->page_id, $paths->namespace, $reason);
+		break;
+	case "delvote":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::delvote($paths->page_id, $paths->namespace);
+		break;
+	case "resetdelvotes":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::resetdelvotes($paths->page_id, $paths->namespace);
+		break;
+	case "getstyles":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::getstyles($_GET['id']);
+		break;
+	case "catedit":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::catedit($paths->page_id, $paths->namespace);
+		break;
+	case "catsave":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::catsave($paths->page_id, $paths->namespace, $_POST);
+		break;
+	case "setwikimode":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::setwikimode($paths->page_id, $paths->namespace, (int)$_GET['mode']);
+		break;
+	case "setpass":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		echo PageUtils::setpass($paths->page_id, $paths->namespace, $_POST['password']);
+		break;
+	case "fillusername":
+		break;
+	case "fillpagename":
+		break;
+	case "preview":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		$template->init_vars();
+		echo PageUtils::genPreview($_POST['text']);
+		break;
+	case "transform":
+		header('Content-type: text/javascript');
+		if ( !isset($_GET['to']) )
+		{
+			echo enano_json_encode(array(
+					'mode' => 'error',
+					'error' => '"to" not specified'
+				));
+			break;
+		}
+		if ( !isset($_POST['text']) )
+		{
+			echo enano_json_encode(array(
+					'mode' => 'error',
+					'error' => '"text" not specified (must be on POST)'
+				));
+			break;
+		}
+		switch($_GET['to'])
+		{
+			case 'xhtml':
+				$result = RenderMan::render($_POST['text'], RENDER_BLOCK | RENDER_NOSMILIES, false);
+				break;
+			case 'wikitext':
+				$result = RenderMan::reverse_render($_POST['text']);
+				break;
+			default:
+				$text =& $_POST['text'];
+				$result = false;
+				$code = $plugins->setHook('ajax_transform');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				if ( !$result )
+				{
+					echo enano_json_encode(array(
+							'mode' => 'error',
+							'error' => 'Invalid target format'
+						));
+					break;
+				}
+				break;
+		}
+		
+		// mostly for debugging, but I suppose this could be useful elsewhere.
+		if ( isset($_POST['plaintext']) )
+			die($result);
+		
+		echo enano_json_encode(array(
+				'mode' => 'transformed_text',
+				'text' => $result
+			));
+		break;
+	case "pagediff":
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		$id1 = ( isset($_GET['diff1']) ) ? (int)$_GET['diff1'] : false;
+		$id2 = ( isset($_GET['diff2']) ) ? (int)$_GET['diff2'] : false;
+		if(!$id1 || !$id2) { echo '<p>Invalid request.</p>'; $template->footer(); break; }
+		if(!preg_match('#^([0-9]+)$#', (string)$_GET['diff1']) ||
+			!preg_match('#^([0-9]+)$#', (string)$_GET['diff2']  )) { echo '<p>SQL injection attempt</p>'; $template->footer(); break; }
+		echo PageUtils::pagediff($paths->page_id, $paths->namespace, $id1, $id2);
+		break;
+	case "jsres":
+		die('// ERROR: this section is deprecated and has moved to includes/clientside/static/enano-lib-basic.js.');
+		break;
+	case "rdns":
+		if(!$session->get_permissions('mod_misc')) die('Go somewhere else for your reverse DNS info!');
+		$ip = $_GET['ip'];
+		if ( !is_valid_ip($ip) )
+		{
+			echo $lang->get('acpsl_err_invalid_ip');
+		}
+		$rdns = gethostbyaddr($ip);
+		if ( $rdns == $ip )
+			echo $lang->get('acpsl_err_ptr_no_resolve');
+		else echo $rdns;
+		break;
+	case 'acljson':
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		$parms = ( isset($_POST['acl_params']) ) ? rawurldecode($_POST['acl_params']) : false;
+		echo PageUtils::acl_json($parms);
+		break;
+	case 'theme_list':
+		header('Content-type: application/json');
+		
+		$return = array();
+		foreach ( $template->theme_list as $theme )
+		{
+			if ( $theme['enabled'] != 1 )
+				continue;
+			
+			$return[] = array(
+					'theme_name' => $theme['theme_name'],
+					'theme_id' => $theme['theme_id'],
+					'have_thumb' => file_exists(ENANO_ROOT . "/themes/{$theme['theme_id']}/preview.png")
+				);
+		}
+		
+		echo enano_json_encode($return);
+		
+		break;
+	case "get_styles":
+		if ( !preg_match('/^[a-z0-9_-]+$/', $_GET['theme_id']) )
+			die(enano_json_encode(array()));
+		
+		$theme_id = $_GET['theme_id'];
+		$return = array();
+		
+		if ( $dr = @opendir(ENANO_ROOT . "/themes/$theme_id/css/") )
+		{
+			while ( $dh = @readdir($dr) )
+			{
+				if ( preg_match('/\.css$/', $dh) && $dh != '_printable.css' )
+				{
+					$return[] = preg_replace('/\.css$/', '', $dh);
+				}
+			}
+		}
+		else
+		{
+			$return = array(
+					'mode' => 'error',
+					'error' => 'Could not open directory.'
+				);
+		}
+		echo enano_json_encode($return);
+		break;
+	case "change_theme":
+		if ( !isset($_POST['theme_id']) || !isset($_POST['style_id']) )
+		{
+			die(enano_json_encode(array('mode' => 'error', 'error' => 'Invalid parameter')));
+		}
+		if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['theme_id']) || !preg_match('/^([a-z0-9_-]+)$/i', $_POST['style_id']) )
+		{
+			die(enano_json_encode(array('mode' => 'error', 'error' => 'Invalid parameter')));
+		}
+		if ( !file_exists(ENANO_ROOT . '/themes/' . $_POST['theme_id'] . '/css/' . $_POST['style_id'] . '.css') )
+		{
+			die(enano_json_encode(array('mode' => 'error', 'error' => 'Can\'t find theme file: ' . ENANO_ROOT . '/themes/' . $_POST['theme_id'] . '/css/' . $_POST['style_id'] . '.css')));;
+		}
+		if ( !$session->user_logged_in )
+		{
+			die(enano_json_encode(array('mode' => 'error', 'error' => 'You must be logged in to change your theme')));
+		}
+		// Just in case something slipped through...
+		$theme_id = $db->escape($_POST['theme_id']);
+		$style_id = $db->escape($_POST['style_id']);
+		$e = $db->sql_query('UPDATE ' . table_prefix . "users SET theme = '$theme_id', style = '$style_id' WHERE user_id = $session->user_id;");
+		if ( !$e )
+			die( $db->get_error() );
+		
+		echo enano_json_encode(array(
+				'success' => true
+			));
+		break;
+	case 'get_tags':
+		
+		$ret = array('tags' => array(), 'user_level' => $session->user_level, 'can_add' => $session->get_permissions('tag_create'));
+		$q = $db->sql_query('SELECT t.tag_id, t.tag_name, pg.pg_target IS NOT NULL AS used_in_acl, t.user_id FROM '.table_prefix.'tags AS t
+			LEFT JOIN '.table_prefix.'page_groups AS pg
+				ON ( ( pg.pg_type = ' . PAGE_GRP_TAGGED . ' AND pg.pg_target=t.tag_name ) OR ( pg.pg_type IS NULL AND pg.pg_target IS NULL ) )
+			WHERE t.page_id=\'' . $db->escape($paths->page_id) . '\' AND t.namespace=\'' . $db->escape($paths->namespace) . '\';');
+		if ( !$q )
+			$db->_die();
+		
+		while ( $row = $db->fetchrow() )
+		{
+			$can_del = true;
+			
+			$perm = ( $row['user_id'] != $session->user_id ) ?
+							'tag_delete_other' :
+							'tag_delete_own';
+			
+			if ( $row['user_id'] == 1 && !$session->user_logged_in )
+				// anonymous user trying to delete tag (hardcode blacklisted)
+				$can_del = false;
+				
+			if ( !$session->get_permissions($perm) )
+				$can_del = false;
+			
+			if ( $row['used_in_acl'] == 1 && !$session->get_permissions('edit_acl') && $session->user_level < USER_LEVEL_ADMIN )
+				$can_del = false;
+			
+			$ret['tags'][] = array(
+				'id' => $row['tag_id'],
+				'name' => $row['tag_name'],
+				'can_del' => $can_del,
+				'acl' => ( $row['used_in_acl'] == 1 )
+			);
+		}
+		
+		echo enano_json_encode($ret);
+		
+		break;
+	case 'addtag':
+		$resp = array(
+				'success' => false,
+				'error' => 'No error',
+				'can_del' => ( $session->get_permissions('tag_delete_own') && $session->user_logged_in ),
+				'in_acl' => false
+			);
+		
+		// first of course, are we allowed to tag pages?
+		if ( !$session->get_permissions('tag_create') )
+		{
+			$resp['error'] = 'You are not permitted to tag pages.';
+			die(enano_json_encode($resp));
+		}
+		
+		// sanitize the tag name
+		$tag = sanitize_tag($_POST['tag']);
+		$tag = $db->escape($tag);
+		
+		if ( strlen($tag) < 2 )
+		{
+			$resp['error'] = 'Tags must consist of at least 2 alphanumeric characters.';
+			die(enano_json_encode($resp));
+		}
+		
+		// check if tag is already on page
+		$q = $db->sql_query('SELECT 1 FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $db->escape($paths->namespace) . '\' AND tag_name=\'' . $tag . '\';');
+		if ( !$q )
+			$db->_die();
+		if ( $db->numrows() > 0 )
+		{
+			$resp['error'] = 'This page already has this tag.';
+			die(enano_json_encode($resp));
+		}
+		$db->free_result();
+		
+		// tricky: make sure this tag isn't being used in some page group, and thus adding it could affect page access
+		$can_edit_acl = ( $session->get_permissions('edit_acl') || $session->user_level >= USER_LEVEL_ADMIN );
+		$q = $db->sql_query('SELECT 1 FROM '.table_prefix.'page_groups WHERE pg_type=' . PAGE_GRP_TAGGED . ' AND pg_target=\'' . $tag . '\';');
+		if ( !$q )
+			$db->_die();
+		if ( $db->numrows() > 0 && !$can_edit_acl )
+		{
+			$resp['error'] = 'This tag is used in an ACL page group, and thus can\'t be added to a page by people without administrator privileges.';
+			die(enano_json_encode($resp));
+		}
+		$resp['in_acl'] = ( $db->numrows() > 0 );
+		$db->free_result();
+		
+		// we're good
+		$q = $db->sql_query('INSERT INTO '.table_prefix.'tags(tag_name,page_id,namespace,user_id) VALUES(\'' . $tag . '\', \'' . $db->escape($paths->page_id) . '\', \'' . $db->escape($paths->namespace) . '\', ' . $session->user_id . ');');
+		if ( !$q )
+			$db->_die();
+		
+		$resp['success'] = true;
+		$resp['tag'] = $tag;
+		$resp['tag_id'] = $db->insert_id();
+		
+		echo enano_json_encode($resp);
+		break;
+	case 'deltag':
+		
+		$tag_id = intval($_POST['tag_id']);
+		if ( empty($tag_id) )
+			die('Invalid tag ID');
+		
+		$q = $db->sql_query('SELECT t.tag_id, t.user_id, t.page_id, t.namespace, pg.pg_target IS NOT NULL AS used_in_acl FROM '.table_prefix.'tags AS t
+LEFT JOIN '.table_prefix.'page_groups AS pg
+	ON ( pg.pg_id IS NULL OR ( pg.pg_target = t.tag_name AND pg.pg_type = ' . PAGE_GRP_TAGGED . ' ) )
+WHERE t.tag_id=' . $tag_id . ';');
+		
+		if ( !$q )
+			$db->_die();
+		
+		if ( $db->numrows() < 1 )
+			die('Could not find a tag with that ID');
+		
+		$row = $db->fetchrow();
+		$db->free_result();
+		
+		if ( $row['page_id'] == $paths->page_id && $row['namespace'] == $paths->namespace )
+			$perms =& $session;
+		else
+			$perms = $session->fetch_page_acl($row['page_id'], $row['namespace']);
+			
+		$perm = ( $row['user_id'] != $session->user_id ) ?
+							'tag_delete_other' :
+							'tag_delete_own';
+		
+		if ( $row['user_id'] == 1 && !$session->user_logged_in )
+			// anonymous user trying to delete tag (hardcode blacklisted)
+			die('You are not authorized to delete this tag.');
+			
+		if ( !$perms->get_permissions($perm) )
+			die('You are not authorized to delete this tag.');
+		
+		if ( $row['used_in_acl'] == 1 && !$perms->get_permissions('edit_acl') && $session->user_level < USER_LEVEL_ADMIN )
+			die('You are not authorized to delete this tag.');
+		
+		// We're good
+		$q = $db->sql_query('DELETE FROM '.table_prefix.'tags WHERE tag_id = ' . $tag_id . ';');
+		if ( !$q )
+			$db->_die();
+		
+		echo 'success';
+		
+		break;
+	case 'ping':
+		echo 'pong';
+		break;
+	default:
+		die('Hacking attempt');
+		break;
+}
+
 ?>
\ No newline at end of file
--- a/cron.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/cron.php	Sun Mar 28 23:10:46 2010 -0400
@@ -28,17 +28,17 @@
 
 foreach ( $cron_tasks as $interval => $tasks )
 {
-  $interval = doubleval($interval);
-  $last_run = intval(getConfig("cron_lastrun_ivl_$interval"));
-  $last_run_threshold = doubleval(time()) - ( 3600.0 * $interval );
-  if ( $last_run_threshold >= $last_run )
-  {
-    foreach ( $tasks as $task )
-    {
-      call_user_func($task);
-    }
-    setConfig("cron_lastrun_ivl_$interval", strval(time()));
-  }
+	$interval = doubleval($interval);
+	$last_run = intval(getConfig("cron_lastrun_ivl_$interval"));
+	$last_run_threshold = doubleval(time()) - ( 3600.0 * $interval );
+	if ( $last_run_threshold >= $last_run )
+	{
+		foreach ( $tasks as $task )
+		{
+			call_user_func($task);
+		}
+		setConfig("cron_lastrun_ivl_$interval", strval(time()));
+	}
 }
 
 $expiry_date = date('r', get_cron_next_run());
@@ -47,11 +47,11 @@
 
 if ( isset($_SERVER['HTTP_IF_NONE_MATCH']) )
 {
-  if ( "\"$etag\"" == $_SERVER['HTTP_IF_NONE_MATCH'] )
-  {
-    header('HTTP/1.1 304 Not Modified');
-    exit();
-  }
+	if ( "\"$etag\"" == $_SERVER['HTTP_IF_NONE_MATCH'] )
+	{
+		header('HTTP/1.1 304 Not Modified');
+		exit();
+	}
 }
 
 header("ETag: $etag");
--- a/includes/cache.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/cache.php	Sun Mar 28 23:10:46 2010 -0400
@@ -21,70 +21,70 @@
 
 class CacheManager
 {
-  /**
-   * Fetch a cached piece of data.
-   * @param string Cache ID. The timestamp is checked automatically.
-   */
-  
-  public function fetch($cache_id)
-  {
-    if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
-    {
-      throw new Exception('Cache ID must be letters, numbers, and underscores.');
-    }
-    $cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
-    if ( file_exists($cache_file) )
-    {
-      require($cache_file);
-      if ( isset($_cache_data) && isset($_cache_ttl) && isset($_cache_timestamp) )
-      {
-        $cache_expire = $_cache_timestamp + ( 60 * $_cache_ttl);
-        if ( $cache_expire >= time() || $_cache_ttl === -1 )
-        {
-          return $_cache_data;
-        }
-      }
-    }
-    return false;
-  }
-  
-  /**
-   * Stores an array by var_export()ing it and saving it to disk.
-   * @param string Cache ID - human readable name for this store (letters, numbers, underscores)
-   * @param array Data to store
-   * @param int TTL for the cached array, in minutes. Defaults to 20. If set to -1, caches indefinitely.
-   * @return bool True on success, false on failure
-   */
-  
-  public function store($cache_id, $data, $ttl = 20)
-  {
-    if ( getConfig('cache_thumbs') != '1' )
-    {
-      // caching disabled
-      return false;
-    }
-    if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
-    {
-      throw new Exception('Cache ID must be letters, numbers, and underscores.');
-    }
-    if ( !is_int($ttl) )
-    {
-      throw new Exception('TTL must be an integer');
-    }
-    
-    $cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
-    if ( file_exists($cache_file) )
-    {
-      @unlink($cache_file);
-    }
-    $fh = @fopen($cache_file, 'w');
-    if ( !$fh )
-    {
-      throw new Exception('Failed to open file for writing.');
-    }
-    $exported = Language::var_export_string($data);
-    $now = time();
-    $content = <<<EOF
+	/**
+ 	* Fetch a cached piece of data.
+ 	* @param string Cache ID. The timestamp is checked automatically.
+ 	*/
+	
+	public function fetch($cache_id)
+	{
+		if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
+		{
+			throw new Exception('Cache ID must be letters, numbers, and underscores.');
+		}
+		$cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
+		if ( file_exists($cache_file) )
+		{
+			require($cache_file);
+			if ( isset($_cache_data) && isset($_cache_ttl) && isset($_cache_timestamp) )
+			{
+				$cache_expire = $_cache_timestamp + ( 60 * $_cache_ttl);
+				if ( $cache_expire >= time() || $_cache_ttl === -1 )
+				{
+					return $_cache_data;
+				}
+			}
+		}
+		return false;
+	}
+	
+	/**
+ 	* Stores an array by var_export()ing it and saving it to disk.
+ 	* @param string Cache ID - human readable name for this store (letters, numbers, underscores)
+ 	* @param array Data to store
+ 	* @param int TTL for the cached array, in minutes. Defaults to 20. If set to -1, caches indefinitely.
+ 	* @return bool True on success, false on failure
+ 	*/
+	
+	public function store($cache_id, $data, $ttl = 20)
+	{
+		if ( getConfig('cache_thumbs') != '1' )
+		{
+			// caching disabled
+			return false;
+		}
+		if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
+		{
+			throw new Exception('Cache ID must be letters, numbers, and underscores.');
+		}
+		if ( !is_int($ttl) )
+		{
+			throw new Exception('TTL must be an integer');
+		}
+		
+		$cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
+		if ( file_exists($cache_file) )
+		{
+			@unlink($cache_file);
+		}
+		$fh = @fopen($cache_file, 'w');
+		if ( !$fh )
+		{
+			throw new Exception('Failed to open file for writing.');
+		}
+		$exported = Language::var_export_string($data);
+		$now = time();
+		$content = <<<EOF
 <?php
 /**
  * Automatically generated cache file.
@@ -96,35 +96,35 @@
 \$_cache_data = $exported;
 
 EOF;
-    fwrite($fh, $content);
-    fclose($fh);
-    
-    return true;
-  }
-  
-  /**
-   * Deletes a cached item.
-   * @param string Cache ID.
-   * @return bool true on success or if item doesn't exist, false on failure
-   */
-  
-  public function purge($cache_id)
-  {
-    if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
-    {
-      throw new Exception('Cache ID must be letters, numbers, and underscores.');
-    }
-    
-    $cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
-    if ( file_exists($cache_file) )
-    {
-      if ( unlink($cache_file) )
-      {
-        return true;
-      }
-      return false;
-    }
-    return true;
-  }
+		fwrite($fh, $content);
+		fclose($fh);
+		
+		return true;
+	}
+	
+	/**
+ 	* Deletes a cached item.
+ 	* @param string Cache ID.
+ 	* @return bool true on success or if item doesn't exist, false on failure
+ 	*/
+	
+	public function purge($cache_id)
+	{
+		if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
+		{
+			throw new Exception('Cache ID must be letters, numbers, and underscores.');
+		}
+		
+		$cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
+		if ( file_exists($cache_file) )
+		{
+			if ( unlink($cache_file) )
+			{
+				return true;
+			}
+			return false;
+		}
+		return true;
+	}
 }
 
--- a/includes/captcha.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/captcha.php	Sun Mar 28 23:10:46 2010 -0400
@@ -21,133 +21,133 @@
  
 class captcha_base
 {
-  
-  /**
-   * Our session ID
-   * @var string
-   */
-  
-  private $session_id;
-  
-  /**
-   * Our saved session data
-   * @var array
-   */
-  
-  private $session_data;
-  
-  /**
-   * The confirmation code we're generating.
-   * @var string
-   */
-  
-  private $code = '';
-  
-  /**
-   * Numerical ID (primary key) for our session
-   * @var int
-   */
-  
-  private $id = 0;
-  
-  /**
-   * Constructor.
-   * @param string Session ID for captcha
-   */
-  
-  function __construct($session_id, $row = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $session_id) )
-    {
-      throw new Exception('Invalid session ID');
-    }
-    $this->session_id = $session_id;
-    // If we weren't supplied with session info, retreive it
-    if ( !is_array($row) )
-    {
-      $q = $db->sql_query('SELECT code_id, code, session_data FROM ' . table_prefix . "captcha WHERE session_id = '$session_id';");
-      if ( !$q )
-        $db->_die();
-      $row = $db->fetchrow();
-      $row['code_id'] = intval($row['code_id']);
-      $db->free_result();
-    }
-    if ( !isset($row['code']) || !isset($row['session_data']) || !is_int(@$row['code_id']) )
-    {
-      throw new Exception('Row doesn\'t contain what we need (code and session_data)');
-    }
-    $this->session_data = ( is_array($x = @unserialize($row['session_data'])) ) ? $x : array();
-    $this->code = $row['code'];
-    $this->id = $row['code_id'];
-    
-    // run any custom init functions
-    if ( method_exists($this, 'construct_hook') )
-      $this->construct_hook();
-  }
-  
-  /**
-   * Retrieves a key from the session data set
-   * @param int|string Key to fetch
-   * @param mixed Default value for key
-   * @return mixed
-   */
-   
-  function session_fetch($key, $default = false)
-  {
-    return ( isset($this->session_data[$key]) ) ? $this->session_data[$key] : $default;
-  }
-  
-  /**
-   * Stores a value in the session's data set. Change must be committed using $captcha->session_commit()
-   * @param int|string Name of key
-   * @param mixed Value - can be an array, string, int, or double, but probably not objects :-)
-   */
-  
-  function session_store($key, $value)
-  {
-    $this->session_data[$key] = $value;
-  }
-  
-  /**
-   * Commits changes to the session data set to the database.
-   */
-  
-  function session_commit()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $session_data = serialize($this->session_data);
-    $session_data = $db->escape($session_data);
-    $code = $db->escape($this->code);
-    
-    $q = $db->sql_query('UPDATE ' . table_prefix . "captcha SET code = '$code', session_data = '$session_data' WHERE code_id = {$this->id};");
-    if ( !$q )
-      $db->_die();
-  }
-  
-  /**
-   * Changes the confirmation code
-   * @param string New string
-   */
-  
-  function set_code($code)
-  {
-    if ( !is_string($code) )
-      return false;
-    
-    $this->code = $code;
-  }
-  
-  /**
-   * Returns the confirmation code
-   * @return string
-   */
-  
-  function get_code()
-  {
-    return $this->code;
-  }
-  
+	
+	/**
+ 	* Our session ID
+ 	* @var string
+ 	*/
+	
+	private $session_id;
+	
+	/**
+ 	* Our saved session data
+ 	* @var array
+ 	*/
+	
+	private $session_data;
+	
+	/**
+ 	* The confirmation code we're generating.
+ 	* @var string
+ 	*/
+	
+	private $code = '';
+	
+	/**
+ 	* Numerical ID (primary key) for our session
+ 	* @var int
+ 	*/
+	
+	private $id = 0;
+	
+	/**
+ 	* Constructor.
+ 	* @param string Session ID for captcha
+ 	*/
+	
+	function __construct($session_id, $row = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $session_id) )
+		{
+			throw new Exception('Invalid session ID');
+		}
+		$this->session_id = $session_id;
+		// If we weren't supplied with session info, retreive it
+		if ( !is_array($row) )
+		{
+			$q = $db->sql_query('SELECT code_id, code, session_data FROM ' . table_prefix . "captcha WHERE session_id = '$session_id';");
+			if ( !$q )
+				$db->_die();
+			$row = $db->fetchrow();
+			$row['code_id'] = intval($row['code_id']);
+			$db->free_result();
+		}
+		if ( !isset($row['code']) || !isset($row['session_data']) || !is_int(@$row['code_id']) )
+		{
+			throw new Exception('Row doesn\'t contain what we need (code and session_data)');
+		}
+		$this->session_data = ( is_array($x = @unserialize($row['session_data'])) ) ? $x : array();
+		$this->code = $row['code'];
+		$this->id = $row['code_id'];
+		
+		// run any custom init functions
+		if ( method_exists($this, 'construct_hook') )
+			$this->construct_hook();
+	}
+	
+	/**
+ 	* Retrieves a key from the session data set
+ 	* @param int|string Key to fetch
+ 	* @param mixed Default value for key
+ 	* @return mixed
+ 	*/
+ 	
+	function session_fetch($key, $default = false)
+	{
+		return ( isset($this->session_data[$key]) ) ? $this->session_data[$key] : $default;
+	}
+	
+	/**
+ 	* Stores a value in the session's data set. Change must be committed using $captcha->session_commit()
+ 	* @param int|string Name of key
+ 	* @param mixed Value - can be an array, string, int, or double, but probably not objects :-)
+ 	*/
+	
+	function session_store($key, $value)
+	{
+		$this->session_data[$key] = $value;
+	}
+	
+	/**
+ 	* Commits changes to the session data set to the database.
+ 	*/
+	
+	function session_commit()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$session_data = serialize($this->session_data);
+		$session_data = $db->escape($session_data);
+		$code = $db->escape($this->code);
+		
+		$q = $db->sql_query('UPDATE ' . table_prefix . "captcha SET code = '$code', session_data = '$session_data' WHERE code_id = {$this->id};");
+		if ( !$q )
+			$db->_die();
+	}
+	
+	/**
+ 	* Changes the confirmation code
+ 	* @param string New string
+ 	*/
+	
+	function set_code($code)
+	{
+		if ( !is_string($code) )
+			return false;
+		
+		$this->code = $code;
+	}
+	
+	/**
+ 	* Returns the confirmation code
+ 	* @return string
+ 	*/
+	
+	function get_code()
+	{
+		return $this->code;
+	}
+	
 }
 
 /**
@@ -159,29 +159,29 @@
 
 function captcha_object($session_id, $engine = false, $row = false)
 {
-  static $singletons = array();
-  if ( !$engine )
-  {
-    $engine = getConfig('captcha_engine');
-    if ( !$engine )
-    {
-      $engine = 'freecap';
-    }
-  }
-  if( !extension_loaded("gd") || !function_exists("gd_info") || !function_exists('imagettftext') || !function_exists('imagepng') || !function_exists('imagecreatefromjpeg') )
-  {
-    $engine = 'failsafe';
-  }
-  if ( !class_exists("captcha_engine_$engine") )
-  {
-    require_once ENANO_ROOT . "/includes/captcha/engine_{$engine}.php";
-  }
-  if ( !class_exists("captcha_engine_$engine") )
-  {
-    throw new Exception("Expected but couldn't find class for captcha engine: captcha_engine_$engine");
-  }
-  $class = "captcha_engine_$engine";
-  return new $class($session_id, $row);
+	static $singletons = array();
+	if ( !$engine )
+	{
+		$engine = getConfig('captcha_engine');
+		if ( !$engine )
+		{
+			$engine = 'freecap';
+		}
+	}
+	if( !extension_loaded("gd") || !function_exists("gd_info") || !function_exists('imagettftext') || !function_exists('imagepng') || !function_exists('imagecreatefromjpeg') )
+	{
+		$engine = 'failsafe';
+	}
+	if ( !class_exists("captcha_engine_$engine") )
+	{
+		require_once ENANO_ROOT . "/includes/captcha/engine_{$engine}.php";
+	}
+	if ( !class_exists("captcha_engine_$engine") )
+	{
+		throw new Exception("Expected but couldn't find class for captcha engine: captcha_engine_$engine");
+	}
+	$class = "captcha_engine_$engine";
+	return new $class($session_id, $row);
 }
 
 ?>
--- a/includes/captcha/engine_default.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/captcha/engine_default.php	Sun Mar 28 23:10:46 2010 -0400
@@ -25,125 +25,125 @@
  
 class captcha_engine_default extends captcha_base
 {
-  function make_image()
-  {
-    $code =& strtoupper($this->get_code());
-    
-    /**
-      * The next part is orginnaly written by ted from mastercode.nl and modified for use in Enano.
-      **/
-    header("content-type:image/png");
-    header('Cache-control: no-cache, no-store');
-    $breedte = 320;
-    $hoogte = 60;
-    $img = imagecreatetruecolor($breedte,$hoogte);
-    $achtergrond = imagecolorallocate($img, $this->color("bg"), $this->color("bg"), $this->color("bg"));
-    
-    imagefilledrectangle($img, 0, 0, $breedte-1, $hoogte-1, $achtergrond);
-    for($g = 0;$g < 30; $g++)
-    {
-      $t = $this->dss_rand();
-      $t = $t[0];
-          
-      $ypos = rand(0,$hoogte);
-      $xpos = rand(0,$breedte);
-          
-      $kleur = imagecolorallocate($img, $this->color("bgtekst"), $this->color("bgtekst"), $this->color("bgtekst"));
-          
-      imagettftext($img, $this->size(), $this->move(), $xpos, $ypos, $kleur, $this->font(), $t);
-    } 			
-    $stukje = $breedte / (strlen($code) + 3);
-    
-    for($j = 0;$j < strlen($code); $j++)
-    {
-      
-      
-      $tek = $code[$j];
-      $ypos = rand(33,43);
-      $xpos = $stukje * ($j+1);
-          
-      $kleur2 = imagecolorallocate($img, $this->color("tekst"), $this->color("tekst"), $this->color("tekst"));
-      
-      imagettftext($img, $this->size(), $this->move(), $xpos, $ypos, $kleur2, $this->font() , $tek);
-    }
-      
-    imagepng($img);
-  }
-  
-  /**
-    * Some functions :)
-    * Also orginally written by mastercode.nl
-    **/
-  /**
-    * Function to create a random color
-    * @param $type string Mode for the color
-    * @return int
-    **/
-  function color($type)
-  {
-    switch($type)
-    {
-      case "bg": 
-        $kleur = rand(224,255); 
-      break;
-      case "tekst": 
-        $kleur = rand(0,127); 
-      break;
-      case "bgtekst": 
-        $kleur = rand(200,224); 
-      break;
-      default: 
-        $kleur = rand(0,255); 
-      break;
-    }
-    return $kleur;
-  }
-  /**
-    * Function to ranom the size
-    * @return int
-    **/
-  function size()
-  {
-    $grootte = rand(14,30);
-    return $grootte;
-  }
-  /**
-    * Function to random the posistion
-    * @return int
-    **/
-  function move()
-  {
-    $draai = rand(-25,25);
-    return $draai;
-  }
-  
-  /**
-    * Function to return a ttf file from fonts map
-    * @return string
-    **/
-  function font()
-  {
-    $f = @opendir(ENANO_ROOT . '/includes/captcha/fonts/');
-    if(!$f) die('Can\'t open includes/captcha/fonts/ for reading');
-    $ar = array();
-    while(($file = @readdir($f)) !== false)
-    {
-      if(!in_array($file, array('.','..')) && strstr($file, '.ttf'))
-      {
-        $ar[] = $file;
-      }
-    }
-    if(count($ar))
-    {
-      shuffle($ar);
-      $i = rand(0,(count($ar) - 1));
-      return ENANO_ROOT . '/includes/captcha/fonts/' . $ar[$i];
-    }
-  }
-  function dss_rand()
-  {
-    $val = microtime() .  mt_rand();
-    $val = md5($val . 'a');
-    return substr($val, 4, 16);
-  }
+	function make_image()
+	{
+		$code =& strtoupper($this->get_code());
+		
+		/**
+			* The next part is orginnaly written by ted from mastercode.nl and modified for use in Enano.
+			**/
+		header("content-type:image/png");
+		header('Cache-control: no-cache, no-store');
+		$breedte = 320;
+		$hoogte = 60;
+		$img = imagecreatetruecolor($breedte,$hoogte);
+		$achtergrond = imagecolorallocate($img, $this->color("bg"), $this->color("bg"), $this->color("bg"));
+		
+		imagefilledrectangle($img, 0, 0, $breedte-1, $hoogte-1, $achtergrond);
+		for($g = 0;$g < 30; $g++)
+		{
+			$t = $this->dss_rand();
+			$t = $t[0];
+					
+			$ypos = rand(0,$hoogte);
+			$xpos = rand(0,$breedte);
+					
+			$kleur = imagecolorallocate($img, $this->color("bgtekst"), $this->color("bgtekst"), $this->color("bgtekst"));
+					
+			imagettftext($img, $this->size(), $this->move(), $xpos, $ypos, $kleur, $this->font(), $t);
+		} 			
+		$stukje = $breedte / (strlen($code) + 3);
+		
+		for($j = 0;$j < strlen($code); $j++)
+		{
+			
+			
+			$tek = $code[$j];
+			$ypos = rand(33,43);
+			$xpos = $stukje * ($j+1);
+					
+			$kleur2 = imagecolorallocate($img, $this->color("tekst"), $this->color("tekst"), $this->color("tekst"));
+			
+			imagettftext($img, $this->size(), $this->move(), $xpos, $ypos, $kleur2, $this->font() , $tek);
+		}
+			
+		imagepng($img);
+	}
+	
+	/**
+		* Some functions :)
+		* Also orginally written by mastercode.nl
+		**/
+	/**
+		* Function to create a random color
+		* @param $type string Mode for the color
+		* @return int
+		**/
+	function color($type)
+	{
+		switch($type)
+		{
+			case "bg": 
+				$kleur = rand(224,255); 
+			break;
+			case "tekst": 
+				$kleur = rand(0,127); 
+			break;
+			case "bgtekst": 
+				$kleur = rand(200,224); 
+			break;
+			default: 
+				$kleur = rand(0,255); 
+			break;
+		}
+		return $kleur;
+	}
+	/**
+		* Function to ranom the size
+		* @return int
+		**/
+	function size()
+	{
+		$grootte = rand(14,30);
+		return $grootte;
+	}
+	/**
+		* Function to random the posistion
+		* @return int
+		**/
+	function move()
+	{
+		$draai = rand(-25,25);
+		return $draai;
+	}
+	
+	/**
+		* Function to return a ttf file from fonts map
+		* @return string
+		**/
+	function font()
+	{
+		$f = @opendir(ENANO_ROOT . '/includes/captcha/fonts/');
+		if(!$f) die('Can\'t open includes/captcha/fonts/ for reading');
+		$ar = array();
+		while(($file = @readdir($f)) !== false)
+		{
+			if(!in_array($file, array('.','..')) && strstr($file, '.ttf'))
+			{
+				$ar[] = $file;
+			}
+		}
+		if(count($ar))
+		{
+			shuffle($ar);
+			$i = rand(0,(count($ar) - 1));
+			return ENANO_ROOT . '/includes/captcha/fonts/' . $ar[$i];
+		}
+	}
+	function dss_rand()
+	{
+		$val = microtime() .  mt_rand();
+		$val = md5($val . 'a');
+		return substr($val, 4, 16);
+	}
 }
--- a/includes/captcha/engine_failsafe.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/captcha/engine_failsafe.php	Sun Mar 28 23:10:46 2010 -0400
@@ -25,289 +25,289 @@
  
 class captcha_engine_failsafe extends captcha_base
 {
-  function make_image()
-  {
-    $code =& strtoupper($this->get_code());
-    // We can we will generate a single filtered png 
-    // Thanks to DavidMJ for emulating zlib within the code :)
-    $_png = $this->define_filtered_pngs();
-    
-    $total_width = 320;
-    $total_height = 50;
-    $img_height = 40;
-    $img_width = 0;
-    $l = 0;
-    
-    list($usec, $sec) = explode(' ', microtime()); 
-    mt_srand($sec * $usec); 
-    
-    $char_widths = array();
-    for ($i = 0; $i < strlen($code); $i++)
-    {
-      $char = $code{$i};
-    
-      $width = mt_rand(0, 4);
-      $char_widths[] = $width;
-      $img_width += $_png[$char]['width'] - $width;
-    }
-    
-    $offset_x = mt_rand(0, $total_width - $img_width);
-    $offset_y = mt_rand(0, $total_height - $img_height);
-    
-    $image = '';
-    $hold_chars = array();
-    for ($i = 0; $i < $total_height; $i++)
-    {
-      $image .= chr(0);
-    
-      if ($i > $offset_y && $i < $offset_y + $img_height)
-      {
-        $j = 0;
-    
-        for ($k = 0; $k < $offset_x; $k++)
-        {
-          $image .= chr(mt_rand(140, 255));
-        }
-    
-        for ($k = 0; $k < strlen($code); $k++)
-        {
-          $char = $code{$k};
-    
-          if (empty($hold_chars[$char]))
-          {
-            $hold_chars[$char] = explode("\n", chunk_split(base64_decode($_png[$char]['data']), $_png[$char]['width'] + 1, "\n"));
-          }
-          $image .= $this->randomise(substr($hold_chars[$char][$l], 1), $char_widths[$j]);
-          $j++;
-        }
-    
-        for ($k = $offset_x + $img_width; $k < $total_width; $k++)
-        {
-          $image .= chr(mt_rand(140, 255));
-        }
-    
-        $l++;
-      }
-      else
-      {
-        for ($k = 0; $k < $total_width; $k++)
-        {
-          $image .= chr(mt_rand(140, 255));
-        }
-      }
-    
-    }
-    unset($hold);
-    
-    $image = $this->create_png($image, $total_width, $total_height);
+	function make_image()
+	{
+		$code =& strtoupper($this->get_code());
+		// We can we will generate a single filtered png 
+		// Thanks to DavidMJ for emulating zlib within the code :)
+		$_png = $this->define_filtered_pngs();
+		
+		$total_width = 320;
+		$total_height = 50;
+		$img_height = 40;
+		$img_width = 0;
+		$l = 0;
+		
+		list($usec, $sec) = explode(' ', microtime()); 
+		mt_srand($sec * $usec); 
+		
+		$char_widths = array();
+		for ($i = 0; $i < strlen($code); $i++)
+		{
+			$char = $code{$i};
+		
+			$width = mt_rand(0, 4);
+			$char_widths[] = $width;
+			$img_width += $_png[$char]['width'] - $width;
+		}
+		
+		$offset_x = mt_rand(0, $total_width - $img_width);
+		$offset_y = mt_rand(0, $total_height - $img_height);
+		
+		$image = '';
+		$hold_chars = array();
+		for ($i = 0; $i < $total_height; $i++)
+		{
+			$image .= chr(0);
+		
+			if ($i > $offset_y && $i < $offset_y + $img_height)
+			{
+				$j = 0;
+		
+				for ($k = 0; $k < $offset_x; $k++)
+				{
+					$image .= chr(mt_rand(140, 255));
+				}
+		
+				for ($k = 0; $k < strlen($code); $k++)
+				{
+					$char = $code{$k};
+		
+					if (empty($hold_chars[$char]))
+					{
+						$hold_chars[$char] = explode("\n", chunk_split(base64_decode($_png[$char]['data']), $_png[$char]['width'] + 1, "\n"));
+					}
+					$image .= $this->randomise(substr($hold_chars[$char][$l], 1), $char_widths[$j]);
+					$j++;
+				}
+		
+				for ($k = $offset_x + $img_width; $k < $total_width; $k++)
+				{
+					$image .= chr(mt_rand(140, 255));
+				}
+		
+				$l++;
+			}
+			else
+			{
+				for ($k = 0; $k < $total_width; $k++)
+				{
+					$image .= chr(mt_rand(140, 255));
+				}
+			}
+		
+		}
+		unset($hold);
+		
+		$image = $this->create_png($image, $total_width, $total_height);
 
-    // Output image
-    header('Content-Type: image/png');
-    header('Cache-control: no-cache, no-store');
-    echo $image;
-    
-    unset($image);
-    unset($_png);
-  }
-  // This creates a chunk of the given type, with the given data
-  // of the given length adding the relevant crc
-  function png_chunk($length, $type, $data)
-  {
-    $raw = $type;
-    $raw .= $data;
-    $crc = crc32($raw);
-    $raw .= pack('C4', $crc >> 24, $crc >> 16, $crc >> 8, $crc);
-  
-    return pack('C4', $length >> 24, $length >> 16, $length >> 8, $length) . $raw;
-  }
-  
-  // Creates greyscale 8bit png - The PNG spec can be found at
-  // http://www.libpng.org/pub/png/spec/PNG-Contents.html we use
-  // png because it's a fully recognised open standard and supported
-  // by practically all modern browsers and OSs
-  function create_png($raw_image, $width, $height)
-  
-  {
-    // SIG
-    $image = pack('C8', 137, 80, 78, 71, 13, 10, 26, 10);
-    // IHDR
-    $raw = pack('C4', $width >> 24, $width >> 16, $width >> 8, $width);
-    $raw .= pack('C4', $height >> 24, $height >> 16, $height >> 8, $height);
-    $raw .= pack('C5', 8, 0, 0, 0, 0);
-    $image .= $this->png_chunk(13, 'IHDR', $raw);
-  
-    if (@extension_loaded('zlib'))
-    {
-      $raw_image = gzcompress($raw_image);
-      $length = strlen($raw_image);
-    }
-    else
-    {
-      // The total length of this image, uncompressed, is just a calculation of pixels
-      $length = ($width + 1) * $height;
-  
-      // Adler-32 hash generation
-      // Optimized Adler-32 loop ported from the GNU Classpath project
-      $temp_length = $length;
-      $s1 = 1;
-      $s2 = $index = 0;
-  
-      while ($temp_length > 0)
-      {
-        // We can defer the modulo operation:
-        // s1 maximally grows from 65521 to 65521 + 255 * 3800
-        // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
-        $substract_value = ($temp_length < 3800) ? $temp_length : 3800;
-        $temp_length -= $substract_value;
-  
-        while (--$substract_value >= 0)
-        {
-          $s1 += ord($raw_image[$index]);
-          $s2 += $s1;
-  
-          $index++;
-        }
-  
-        $s1 %= 65521;
-        $s2 %= 65521;
-      }
-      $adler_hash = pack('N', ($s2 << 16) | $s1);
-  
-      // This is the same thing as gzcompress($raw_image, 0) but does not need zlib
-      $raw_image = pack('C3v2', 0x78, 0x01, 0x01, $length, ~$length) . $raw_image . $adler_hash;
-  
-      // The Zlib header + Adler hash make us add on 11
-      $length += 11;
-    }
-  
-    // IDAT
-    $image .= $this->png_chunk($length, 'IDAT', $raw_image);
-  
-    // IEND
-    $image .= $this->png_chunk(0, 'IEND', '');
-  
-    return $image;
-  }
-  
-  // Each 'data' element is base64_encoded uncompressed IDAT
-  // png image data
-  function define_filtered_pngs()
-  {
-    $_png = array(
-      '0' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////olFAkBAAAGDyA4P///M31/////////////wD////////////////0dAgAAAAAAAAAAAAEcPipFGHn////////////AP//////////////6DAAAAAAAAAAAAAAAAAALSEAN+T///////////8A//////////////xAAAAAAAAAAAAAAAAAAAAAACPA/////////////wD/////////////oAAAAAAAAAAAAAAAAAAAAAAAev//////////////AP////////////8oAAAAAAAAPNj/zDAAAAAAAABD//////////////8A////////////1AAAAAAAABjw////5BAAAAAAAADo/////////////wD///////////+QAAAAAAAAbP//////QgAAAAAAAKj/////////////AP///////////1wAAAAAAACs/////8AXAAAAAAAAcP////////////8A////////////OAAAAAAAAND////dNwAAAAAAAABI/////////////wD///////////8gAAAAAAAA4P//7koACwAAAAAAACT/////////////AP///////////wgAAAAAAAD///VqAwaPAAAAAAAAEP////////////8A////////////AAAAAAAAAP/8kQYDavUAAAAAAAAA/////////////wD///////////8AAAAAAAAA/6kNAEru/wAAAAAAAAD/////////////AP///////////wAAAAAAAADAIwA33f//AAAAAAAAAP////////////8A////////////FAAAAAAAADYAI8D///8AAAAAAAAQ/////////////wD///////////8kAAAAAAAAAA2p////5AAAAAAAACD/////////////AP///////////0gAAAAAAAAFkfz////UAAAAAAAAQP////////////8A////////////cAAAAAAAAET1/////7AAAAAAAABo/////////////wD///////////+oAAAAAAAAXfX/////sAAAAAAAAGj/////////////AAAAALgAAAAAAAAwAAAAAAAAAAAAAAD////////////oAAAAAAAACOT////oEAAAAAAAAOD/////////////AP////////////8+AAAAAAAAKMz/zDQAAAAAAAA0//////////////8A////////////7jgAAAAAAAAAAAAAAAAAAAAAAKT//////////////wD///////////VqAwIAAAAAAAAAAAAAAAAAAAA8////////////////AP//////////rQcDaVEAAAAAAAAAAAAAAAAAKOj///////////////8A///////////nblnu/IAIAAAAAAAAAAAAAFzw/////////////////wD////////////79////+iITCAAAAAgSITg////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////w==','width' => 40), 
-      '1' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////8BAAAAAAAP//////////////////AP////////////////////////9sAAAAAAAA//////////////////8A////////////////////////pAAAAAAAAAD//////////////////wD//////////////////////6wEAAAAAAAAAP//////////////////AP////////////////////h4AAAAAAAAAAAA//////////////////8A//////////////////ygJAAAAAAAAAAAAAD//////////////////wD//////////////9x8HAAAAAAAAAAAAAAAAP//////////////////AP//////////////AAAAAAAAAAAAAAAAAAAA//////////////////8A//////////////8AAAAAAAAAAAAAAAAAAAD//////////////////wD//////////////wAAAAAAAAR4AAAAAAAAAP//////////////////AP//////////////AAAAAAA4zP8AAAAAAAAA//////////////////8A//////////////8AAAA4sP///wAAAAAAAAD//////////////////wD//////////////yR80P//////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      '2' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////okFAkCAAABCBIfNT///////////////////8A///////////////8hAgAAAAAAAAAAAAAAFTo/////////////////wD//////////////1QAAAAAAAAAAAAAAAAAACjo////////////////AP////////////+MAAAAAAAAAAAAAAAAAAAAADj///////////////8A////////////9BAAAAAAAAAAAAAAAAAAAAAAALD//////////////wD///////////+gAAAAAAAAAHjs+KwMAAAAAAAAVP//////////////AP///////////1gAAAAAAABM/////6QAAAAAAAAU//////////////8A////////////KAAAAAAAALj/////+AAAAAAAAAD//////////////wD///////////+MfGBMOCAI8P/////wAAAAAAAACP//////////////AP///////////////////////////5wAAAAAAAAw//////////////8A///////////////////////////oFAAAAAAAAHz//////////////wD/////////////////////////6CgAAAAAAAAE3P//////////////AP///////////////////////9ggAAAAAAAAAHT///////////////8A//////////////////////+0DAAAAAAAAAA8+P///////////////wD/////////////////////gAAAAAAAAAAAKOj/////////////////AP//////////////////9FAAAAAAAAAAADzw//////////////////8A/////////////////+g4AAAAAAAAAABk/P///////////////////wD////////////////oKAAAAAAAAAAMqP//////////////////////AP//////////////6CgAAAAAAAAAMNz///////////////////////8A//////////////g4AAAAAAAAAFT0/////////////////////////wD/////////////bAAAAAAAAABU/P//////////////////////////AP///////////8wAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A////////////SAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////9AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////xAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      '3' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////8sGg0FAAAACA4cLz8////////////////////AP//////////////rBgAAAAAAAAAAAAAACTA//////////////////8A/////////////3QAAAAAAAAAAAAAAAAAAASs/////////////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAjc////////////////AP//////////6AwAAAAAAAAAAAAAAAAAAAAAAGT///////////////8A//////////94AAAAAAAABJDw/8g4AAAAAAAAHP///////////////wD//////////yAAAAAAAACE/////9gAAAAAAAAA////////////////AP///////////NSwiGQ4FOT//////AAAAAAAABD///////////////8A//////////////////////////+YAAAAAAAAVP///////////////wD//////////////////////P/ggAQAAAAAAATM////////////////AP////////////////////9gAAAAAAAAAAAElP////////////////8A/////////////////////0AAAAAAAAAAHLj//////////////////wD/////////////////////OAAAAAAAAAAwkPj/////////////////AP////////////////////8gAAAAAAAAAAAAINj///////////////8A/////////////////////xAAAAAAAAAAAAAAIPD//////////////wD/////////////////////uOz/4HgEAAAAAAAAhP//////////////AP///////////////////////////3wAAAAAAAAw//////////////8A////////////////////////////6AAAAAAAAAj//////////////wD/////////////////////////////AAAAAAAAAP//////////////AP//////////tJh8YEQoDNz//////+AAAAAAAAAY//////////////8A//////////88AAAAAAAAaP//////dAAAAAAAAEz//////////////wD//////////6QAAAAAAAAAdOD/5HQAAAAAAAAApP//////////////AP///////////CgAAAAAAAAAAAAAAAAAAAAAACD4//////////////8A////////////yAQAAAAAAAAAAAAAAAAAAAAEuP///////////////wD/////////////rAQAAAAAAAAAAAAAAAAABJD/////////////////AP//////////////zDQAAAAAAAAAAAAAACTA//////////////////8A/////////////////8BwOCAAAAAUNGi0/P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      '4' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////////////nAAAAAAAAAD///////////////8A/////////////////////////8AEAAAAAAAAAP///////////////wD////////////////////////gGAAAAAAAAAAA////////////////AP//////////////////////9DAAAAAAAAAAAAD///////////////8A//////////////////////9UAAAAAAAAAAAAAP///////////////wD/////////////////////hAAAAAAAAAAAAAAA////////////////AP///////////////////7QAAAAAAAAAAAAAAAD///////////////8A///////////////////UDAAAAAAUAAAAAAAAAP///////////////wD/////////////////7CQAAAAABMAAAAAAAAAA////////////////AP////////////////xEAAAAAACU/wAAAAAAAAD///////////////8A////////////////cAAAAAAAZP//AAAAAAAAAP///////////////wD//////////////6AAAAAAADz8//8AAAAAAAAA////////////////AP/////////////IBAAAAAAc6P///wAAAAAAAAD///////////////8A////////////5BgAAAAADMz/////AAAAAAAAAP///////////////wD///////////g0AAAAAACk//////8AAAAAAAAA////////////////AP//////////XAAAAAAAfP///////wAAAAAAAAD///////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP///////////////////////////wAAAAAAAAD///////////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      '5' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////8AAAAAAAAAAAAAAAAAAAAAAA//////////////8A///////////////MAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////6wAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////iAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////////9kAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////0QAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////IAAAAAAAYP////////////////////////////8A//////////////wAAAAAAAB8/////////////////////////////wD/////////////3AAAAAAAAIj/////////////////////////////AP////////////+4AAAAAAAAoLRYHAAEKGTE//////////////////8A/////////////5QAAAAAAAAQAAAAAAAAAABY9P///////////////wD/////////////dAAAAAAAAAAAAAAAAAAAAAA89P//////////////AP////////////9QAAAAAAAAAAAAAAAAAAAAAABg//////////////8A/////////////zAAAAAAAAAAAAAAAAAAAAAAAADQ/////////////wD/////////////IAAAAAAAAGjY/+h4BAAAAAAAAGz/////////////AP//////////////9NS0lHSc//////90AAAAAAAALP////////////8A/////////////////////////////9QAAAAAAAAE/////////////wD//////////////////////////////wAAAAAAAAD/////////////AP/////////////////////////////8AAAAAAAAEP////////////8A////////////pIRwWEAgDOD//////8wAAAAAAAA8/////////////wD///////////9EAAAAAAAAaP//////ZAAAAAAAAHz/////////////AP///////////6QAAAAAAAAAaOD/4GQAAAAAAAAE4P////////////8A/////////////CQAAAAAAAAAAAAAAAAAAAAAAGD//////////////wD/////////////yAQAAAAAAAAAAAAAAAAAAAAc7P//////////////AP//////////////rAwAAAAAAAAAAAAAAAAAGNj///////////////8A////////////////0EAAAAAAAAAAAAAAAFTo/////////////////wD//////////////////8h4QCAAAAAcQHzU////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      '6' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////+0ZCwMAAAUNGjI////////////////////AP/////////////////EMAAAAAAAAAAAAABM6P////////////////8A////////////////lAQAAAAAAAAAAAAAAAAo6P///////////////wD//////////////6wAAAAAAAAAAAAAAAAAAABI////////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAACw//////////////8A/////////////3AAAAAAAAAoxP/YPAAAAAAAAEj//////////////wD////////////4EAAAAAAACOD////YDCBAVGiAoP//////////////AP///////////7gAAAAAAABY//////////////////////////////8A////////////eAAAAAAAAJT//////////////////////////////wD///////////9MAAAAAAAAvP/IXBgABCx03P//////////////////AP///////////ygAAAAAAADcdAAAAAAAAAAEiP////////////////8A////////////FAAAAAAAAFAAAAAAAAAAAAAAcP///////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAlP//////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAQ8P////////////8A////////////AAAAAAAAAABAyP/kZAAAAAAAAACQ/////////////wD///////////8MAAAAAAAALPj/////WAAAAAAAAET/////////////AP///////////yQAAAAAAACY///////MAAAAAAAAFP////////////8A////////////SAAAAAAAAMD///////wAAAAAAAAA/////////////wD///////////9wAAAAAAAAvP///////wAAAAAAAAD/////////////AP///////////7QAAAAAAACI///////UAAAAAAAAJP////////////8A////////////+AwAAAAAACDw/////2wAAAAAAABY/////////////wD/////////////cAAAAAAAADC8/Ox4AAAAAAAAAKj/////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAAAk/P////////////8A//////////////+oAAAAAAAAAAAAAAAAAAAABLj//////////////wD///////////////+QAAAAAAAAAAAAAAAAAACQ////////////////AP////////////////+0JAAAAAAAAAAAAAAkuP////////////////8A///////////////////8sGg0FAAADCxgqPz//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      '7' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAABP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAy4/////////////wD//////////////////////////+QUAAAAAAAEuP//////////////AP/////////////////////////8QAAAAAAAAKT///////////////8A/////////////////////////4wAAAAAAAB0/////////////////wD////////////////////////cCAAAAAAANPz/////////////////AP///////////////////////0QAAAAAAATY//////////////////8A//////////////////////+0AAAAAAAAeP///////////////////wD//////////////////////CQAAAAAABTw////////////////////AP////////////////////+gAAAAAAAAkP////////////////////8A/////////////////////ywAAAAAABDw/////////////////////wD///////////////////+4AAAAAAAAbP//////////////////////AP///////////////////1wAAAAAAADQ//////////////////////8A///////////////////4DAAAAAAAMP///////////////////////wD//////////////////7QAAAAAAAB8////////////////////////AP//////////////////aAAAAAAAAMj///////////////////////8A//////////////////8oAAAAAAAM/P///////////////////////wD/////////////////8AAAAAAAAET/////////////////////////AP////////////////+0AAAAAAAAcP////////////////////////8A/////////////////4wAAAAAAACY/////////////////////////wD/////////////////WAAAAAAAAMD/////////////////////////AP////////////////80AAAAAAAA4P////////////////////////8A/////////////////xAAAAAAAAD4/////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      '8' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////////IdDQUAAAEIEiA1P//////////////////AP/////////////////gRAAAAAAAAAAAAAAAROD///////////////8A////////////////0BgAAAAAAAAAAAAAAAAAEMj//////////////wD///////////////AcAAAAAAAAAAAAAAAAAAAAHPD/////////////AP//////////////hAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A//////////////8sAAAAAAAAKMz/zCgAAAAAAAAs/////////////wD//////////////wAAAAAAAADM////zAAAAAAAAAD/////////////AP//////////////BAAAAAAAAP//////AAAAAAAABP////////////8A//////////////8sAAAAAAAAzP///9QAAAAAAAAw/////////////wD//////////////3wAAAAAAAAoyP/YNAAAAAAAAIT/////////////AP//////////////7BgAAAAAAAAAAAAAAAAAAAAc8P////////////8A////////////////xBgAAAAAAAAAAAAAAAAAGNj//////////////wD/////////////////tAQAAAAAAAAAAAAAAACo////////////////AP///////////////HAAAAAAAAAAAAAAAAAAAAB8//////////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB8/////////////wD/////////////wAAAAAAAAABk4P/UWAAAAAAAAATQ////////////AP////////////9UAAAAAAAAaP//////XAAAAAAAAGT///////////8A/////////////xgAAAAAAADg///////cAAAAAAAAJP///////////wD/////////////AAAAAAAAAP////////8AAAAAAAAA////////////AP////////////8AAAAAAAAA4P//////3AAAAAAAAAT///////////8A/////////////ygAAAAAAABg//////9cAAAAAAAALP///////////wD/////////////ZAAAAAAAAABY1P/cXAAAAAAAAABw////////////AP/////////////QAAAAAAAAAAAAAAAAAAAAAAAABNz///////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB0/////////////wD///////////////Q8AAAAAAAAAAAAAAAAAAAAUPz/////////////AP////////////////x4CAAAAAAAAAAAAAAAEIT8//////////////8A///////////////////smFQwGAAAABg0ZKT0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      '9' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////ysYCwMAAAUNGiw/P//////////////////AP////////////////+4JAAAAAAAAAAAAAAkuP////////////////8A////////////////lAQAAAAAAAAAAAAAAAAAkP///////////////wD//////////////8AEAAAAAAAAAAAAAAAAAAAAqP//////////////AP/////////////8JAAAAAAAAAAAAAAAAAAAAAAQ7P////////////8A/////////////6wAAAAAAAAAfOz8vCwAAAAAAABw/////////////wD/////////////WAAAAAAAAHD/////7BgAAAAAAAz4////////////AP////////////8kAAAAAAAA1P//////hAAAAAAAALT///////////8A/////////////wAAAAAAAAD///////+4AAAAAAAAcP///////////wD/////////////AAAAAAAAAPz//////8AAAAAAAABI////////////AP////////////8UAAAAAAAAzP//////lAAAAAAAACT///////////8A/////////////0QAAAAAAABY//////gsAAAAAAAADP///////////wD/////////////kAAAAAAAAABw5P/IPAAAAAAAAAAA////////////AP/////////////wEAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////////+UAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////9wAAAAAAAAAAAAAFAAAAAAAAAU////////////AP////////////////+IBAAAAAAAAABw3AAAAAAAACj///////////8A///////////////////cdCwEABhcxP+8AAAAAAAATP///////////wD//////////////////////////////5AAAAAAAAB4////////////AP//////////////////////////////UAAAAAAAALj///////////8A//////////////+kgGxUQCAM2P///+AIAAAAAAAQ+P///////////wD//////////////0gAAAAAAAA42P/EKAAAAAAAAHD/////////////AP//////////////sAAAAAAAAAAAAAAAAAAAAAAQ6P////////////8A////////////////TAAAAAAAAAAAAAAAAAAAAKz//////////////wD////////////////oKAAAAAAAAAAAAAAAAASU////////////////AP/////////////////sUAAAAAAAAAAAAAAwxP////////////////8A////////////////////yHA0FAAADCxktP///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'A' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////+QAAAAAAAAAAAAAAOT/////////////////AP//////////////////kAAAAAAAAAAAAAAAkP////////////////8A//////////////////88AAAAAAAAAAAAAAA8/////////////////wD/////////////////5AAAAAAAAAAAAAAAAADk////////////////AP////////////////+QAAAAAAAAAAAAAAAAAJD///////////////8A/////////////////zwAAAAAAAAAAAAAAAAAPP///////////////wD////////////////kAAAAAAAAAAgAAAAAAAAA5P//////////////AP///////////////5AAAAAAAAAAgAAAAAAAAACQ//////////////8A////////////////PAAAAAAAAAz8HAAAAAAAADz//////////////wD//////////////+QAAAAAAAAAWP9kAAAAAAAAANz/////////////AP//////////////kAAAAAAAAACk/7wAAAAAAAAAhP////////////8A//////////////88AAAAAAAABOz//BQAAAAAAAAw/////////////wD/////////////4AAAAAAAAAA8////ZAAAAAAAAADc////////////AP////////////+EAAAAAAAAAIj///+8AAAAAAAAAIT///////////8A/////////////zAAAAAAAAAA2P////wQAAAAAAAAMP///////////wD////////////cAAAAAAAAACT//////1wAAAAAAAAA3P//////////AP///////////4QAAAAAAAAAAAAAAAAAAAAAAAAAAACE//////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAAAAAADD//////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANz/////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhP////////8A//////////8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw/////////wD/////////3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc////////AP////////+EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIT///////8A/////////zAAAAAAAAAAhP///////////2QAAAAAAAAAMP///////wD////////cAAAAAAAAAADM////////////vAAAAAAAAAAA3P//////AP///////4QAAAAAAAAAHP/////////////4DAAAAAAAAACE//////8A////////MAAAAAAAAABk//////////////9cAAAAAAAAADD//////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'B' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAEDh83P///////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAEhP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAeP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAABY////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAABT///////////8A//////////8AAAAAAAAAAP/////4zEwAAAAAAAAAAP///////////wD//////////wAAAAAAAAAA////////7AAAAAAAAAAQ////////////AP//////////AAAAAAAAAAD////////sAAAAAAAAAEj///////////8A//////////8AAAAAAAAAAP/////4zEQAAAAAAAAAtP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAFz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAiA/P////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAIjPj//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAGKz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJT///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABNz//////////wD//////////wAAAAAAAAAA///////sqCAAAAAAAAAAbP//////////AP//////////AAAAAAAAAAD/////////yAAAAAAAAAAs//////////8A//////////8AAAAAAAAAAP//////////AAAAAAAAAAT//////////wD//////////wAAAAAAAAAA/////////7wAAAAAAAAAAP//////////AP//////////AAAAAAAAAAD//////+ikGAAAAAAAAAAY//////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFT//////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsP//////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAADj///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAc6P///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAATOj/////////////AP//////////AAAAAAAAAAAAAAAAAAAEIEBkkNj///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'C' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////5JRULBAAAAgkTIDQ//////////////////8A////////////////1FAAAAAAAAAAAAAAAABAyP///////////////wD//////////////4gEAAAAAAAAAAAAAAAAAAAElP//////////////AP////////////9wAAAAAAAAAAAAAAAAAAAAAAAAlP////////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAEyP///////////wD//////////9wIAAAAAAAAAAAAAAAAAAAAAAAAAAAw////////////AP//////////WAAAAAAAAAAAWMz/8JwQAAAAAAAAAACw//////////8A/////////+wEAAAAAAAAAID//////9QMAAAAAAAAAET//////////wD/////////nAAAAAAAAAAo/P///////3wAAAAABDBspP//////////AP////////9gAAAAAAAAAIz/////////3BxQjMT0//////////////8A/////////zQAAAAAAAAAzP///////////////////////////////wD/////////GAAAAAAAAADo////////////////////////////////AP////////8AAAAAAAAAAP////////////////////////////////8A/////////wAAAAAAAAAA/////////////////////////////////wD/////////AAAAAAAAAAD/////////////////////////////////AP////////8cAAAAAAAAAOj///////////////////////////////8A/////////zgAAAAAAAAA0P/////////kIGio7P///////////////wD/////////bAAAAAAAAACg/////////5wAAAAAMHS49P//////////AP////////+oAAAAAAAAAEz/////////PAAAAAAAAAAc//////////8A//////////QIAAAAAAAAALz//////6QAAAAAAAAAAGT//////////wD//////////3AAAAAAAAAADIzo/+SEBAAAAAAAAAAAyP//////////AP//////////7BAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////rAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD/////////////fAAAAAAAAAAAAAAAAAAAAAAAAJz/////////////AP//////////////iAQAAAAAAAAAAAAAAAAAAASY//////////////8A////////////////yEAAAAAAAAAAAAAAAAA8yP///////////////wD//////////////////9yIUCwQAAAAIEB4yP//////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'D' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAADChQkOT/////////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAABGjw//////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAACDY/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAABjk////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKj//////////wD///////////8AAAAAAAAAAP///+isSAAAAAAAAAAANP//////////AP///////////wAAAAAAAAAA////////hAAAAAAAAAAA2P////////8A////////////AAAAAAAAAAD/////////MAAAAAAAAACQ/////////wD///////////8AAAAAAAAAAP////////+MAAAAAAAAAFj/////////AP///////////wAAAAAAAAAA/////////8gAAAAAAAAAMP////////8A////////////AAAAAAAAAAD/////////5AAAAAAAAAAY/////////wD///////////8AAAAAAAAAAP//////////AAAAAAAAAAD/////////AP///////////wAAAAAAAAAA//////////8AAAAAAAAAAP////////8A////////////AAAAAAAAAAD//////////wAAAAAAAAAA/////////wD///////////8AAAAAAAAAAP/////////wAAAAAAAAABD/////////AP///////////wAAAAAAAAAA/////////9QAAAAAAAAAJP////////8A////////////AAAAAAAAAAD/////////qAAAAAAAAABI/////////wD///////////8AAAAAAAAAAP////////9QAAAAAAAAAHj/////////AP///////////wAAAAAAAAAA////////uAAAAAAAAAAAvP////////8A////////////AAAAAAAAAAD////w0HwEAAAAAAAAACT8/////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAADz8//////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAY6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAKNz/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAACHT0//////////////8A////////////AAAAAAAAAAAAAAAAABg4bKj0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'E' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8=','width' => 40),
-      'F' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'G' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////MB8TCgQAAAACCA4YJzs////////////////AP///////////////JQcAAAAAAAAAAAAAAAAAAhw8P////////////8A/////////////9gwAAAAAAAAAAAAAAAAAAAAAAAk2P///////////wD////////////EDAAAAAAAAAAAAAAAAAAAAAAAAAAc7P//////////AP//////////2AwAAAAAAAAAAAAAAAAAAAAAAAAAAABY//////////8A//////////wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQ/////////wD/////////kAAAAAAAAAAAEHzQ/P/gmCAAAAAAAAAAAFz/////////AP////////wcAAAAAAAAACjg////////8CwAAAAAAAAgWP////////8A////////vAAAAAAAAAAI2P//////////yBRAcJjI8P///////////wD///////94AAAAAAAAAGD/////////////////////////////////AP///////0AAAAAAAAAAsP////////////////////////////////8A////////IAAAAAAAAADc/////////////////////////////////wD///////8AAAAAAAAAAP///////wAAAAAAAAAAAAAAAAD/////////AP///////wAAAAAAAAAA////////AAAAAAAAAAAAAAAAAP////////8A////////AAAAAAAAAAD///////8AAAAAAAAAAAAAAAAA/////////wD///////8gAAAAAAAAAOD//////wAAAAAAAAAAAAAAAAD/////////AP///////0AAAAAAAAAAtP//////AAAAAAAAAAAAAAAAAP////////8A////////cAAAAAAAAABw//////8AAAAAAAAAAAAAAAAA/////////wD///////+8AAAAAAAAABDs////////////AAAAAAAAAAD/////////AP////////wYAAAAAAAAADz0//////////AAAAAAAAAAAP////////8A/////////5AAAAAAAAAAACCY4P//3KhcCAAAAAAAAAAA/////////wD/////////+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////AP//////////xAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIP////////8A////////////rAQAAAAAAAAAAAAAAAAAAAAAAAAAAGTw/////////wD/////////////vBQAAAAAAAAAAAAAAAAAAAAAADjI////////////AP//////////////8HAQAAAAAAAAAAAAAAAAAEiw//////////////8A//////////////////iwcEAgBAAABCA4aKDk/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'H' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'I' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40),
-      'J' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAj//////////////wD//////////+zMrIxwUDAQ//////wAAAAAAAAAIP//////////////AP//////////DAAAAAAAAADo////2AAAAAAAAAA0//////////////8A//////////8wAAAAAAAAAKj///+YAAAAAAAAAFj//////////////wD//////////2gAAAAAAAAAIND/yBgAAAAAAAAAkP//////////////AP//////////vAAAAAAAAAAAAAAAAAAAAAAAAADc//////////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAUP///////////////wD////////////EBAAAAAAAAAAAAAAAAAAAABjk////////////////AP////////////+sBAAAAAAAAAAAAAAAAAAY2P////////////////8A///////////////EMAAAAAAAAAAAAAAAVOj//////////////////wD/////////////////vHBAIAAAABg8fNT/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40),
-      'K' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////8AAAAAAAAAAP//////////wAQAAAAAAAAAAABw////////AP///////wAAAAAAAAAA/////////9AMAAAAAAAAAAAAcP////////8A////////AAAAAAAAAAD////////cGAAAAAAAAAAAAHD//////////wD///////8AAAAAAAAAAP//////6CgAAAAAAAAAAABs////////////AP///////wAAAAAAAAAA//////Q0AAAAAAAAAAAAVPz///////////8A////////AAAAAAAAAAD////8RAAAAAAAAAAAAFT8/////////////wD///////8AAAAAAAAAAP///1gAAAAAAAAAAABU/P//////////////AP///////wAAAAAAAAAA//9wAAAAAAAAAAAASPz///////////////8A////////AAAAAAAAAAD/jAAAAAAAAAAAADz0/////////////////wD///////8AAAAAAAAAAKQAAAAAAAAAAAA89P//////////////////AP///////wAAAAAAAAAABAAAAAAAAAAAFPT///////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAApP///////////////////wD///////8AAAAAAAAAAAAAAAAAAAAAAAAU8P//////////////////AP///////wAAAAAAAAAAAAAAAAAAAAAAAABk//////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAAAADE/////////////////wD///////8AAAAAAAAAAAAAAAAoEAAAAAAAACz8////////////////AP///////wAAAAAAAAAAAAAAGNiAAAAAAAAAAIj///////////////8A////////AAAAAAAAAAAAABjY//gYAAAAAAAACOD//////////////wD///////8AAAAAAAAAAAAY2P///5wAAAAAAAAASP//////////////AP///////wAAAAAAAAAAGNj//////CgAAAAAAAAAqP////////////8A////////AAAAAAAAAADI////////sAAAAAAAAAAc8P///////////wD///////8AAAAAAAAAAP//////////QAAAAAAAAABs////////////AP///////wAAAAAAAAAA///////////IAAAAAAAAAATI//////////8A////////AAAAAAAAAAD///////////9YAAAAAAAAADD8/////////wD///////8AAAAAAAAAAP///////////9wEAAAAAAAAAJD/////////AP///////wAAAAAAAAAA/////////////3AAAAAAAAAADOT///////8A////////AAAAAAAAAAD/////////////7BAAAAAAAAAAUP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'L' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'M' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////8AAAAAAAAAAAAAAHz//////3wAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAATP//////UAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAc//////8cAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAADw////8AAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAAALz////AAAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAAkP///5AAAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAABc////ZAAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAoAAAAADD///8wAAAAACQAAAAAAAAA////////AP//////AAAAAAAAAFwAAAAABPz//AgAAAAAXAAAAAAAAAD///////8A//////8AAAAAAAAAkAAAAAAA0P/UAAAAAACQAAAAAAAAAP///////wD//////wAAAAAAAADMAAAAAACg/6gAAAAAAMQAAAAAAAAA////////AP//////AAAAAAAAAPgEAAAAAHD/dAAAAAAE+AAAAAAAAAD///////8A//////8AAAAAAAAA/zQAAAAAQP9IAAAAADD/AAAAAAAAAP///////wD//////wAAAAAAAAD/bAAAAAAQ/xQAAAAAaP8AAAAAAAAA////////AP//////AAAAAAAAAP+gAAAAAADQAAAAAACc/wAAAAAAAAD///////8A//////8AAAAAAAAA/9QAAAAAAGgAAAAAAND/AAAAAAAAAP///////wD//////wAAAAAAAAD//wwAAAAAFAAAAAAM/P8AAAAAAAAA////////AP//////AAAAAAAAAP//RAAAAAAAAAAAADz//wAAAAAAAAD///////8A//////8AAAAAAAAA//94AAAAAAAAAAAAcP//AAAAAAAAAP///////wD//////wAAAAAAAAD//7AAAAAAAAAAAACo//8AAAAAAAAA////////AP//////AAAAAAAAAP//5AAAAAAAAAAAANz//wAAAAAAAAD///////8A//////8AAAAAAAAA////HAAAAAAAAAAQ////AAAAAAAAAP///////wD//////wAAAAAAAAD///9QAAAAAAAAAEz///8AAAAAAAAA////////AP//////AAAAAAAAAP///4gAAAAAAAAAfP///wAAAAAAAAD///////8A//////8AAAAAAAAA////vAAAAAAAAACw////AAAAAAAAAP///////wD//////wAAAAAAAAD////wAAAAAAAAAOz///8AAAAAAAAA////////AP//////AAAAAAAAAP////8sAAAAAAAc/////wAAAAAAAAD///////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'N' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAALD/////////////AAAAAAAAAP//////////AP////////8AAAAAAAAAFOj///////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAASP///////////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAkP//////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAI1P////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAw+P///////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAABw////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAC8//////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAABzs/////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAFD/////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAJz///8AAAAAAAAA//////////8A/////////wAAAAAAAAAUAAAAAAAADNz//wAAAAAAAAD//////////wD/////////AAAAAAAAALQAAAAAAAAANPz/AAAAAAAAAP//////////AP////////8AAAAAAAAA/2wAAAAAAAAAfP8AAAAAAAAA//////////8A/////////wAAAAAAAAD/+CwAAAAAAAAExAAAAAAAAAD//////////wD/////////AAAAAAAAAP//0AQAAAAAAAAgAAAAAAAAAP//////////AP////////8AAAAAAAAA////jAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////RAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP/////kFAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA//////+sAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD///////9kAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP////////QkAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA/////////8wEAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD//////////4QAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP///////////DwAAAAAAAAAAP//////////AP////////8AAAAAAAAA////////////4BAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////////////qAAAAAAAAAD//////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'O' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////0qGw4HAAAABw4aKT0/////////////////wD////////////////wcAwAAAAAAAAAAAAAAAho6P//////////////AP//////////////uBQAAAAAAAAAAAAAAAAAAAAMoP////////////8A/////////////6AEAAAAAAAAAAAAAAAAAAAAAAAAkP///////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP//////////8BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAM5P////////8A//////////9wAAAAAAAAAAAsrPD/7KQsAAAAAAAAAABg/////////wD/////////+BAAAAAAAAAAUPj///////hQAAAAAAAAAAjs////////AP////////+sAAAAAAAAABDw//////////AYAAAAAAAAAKD///////8A/////////2wAAAAAAAAAdP///////////3wAAAAAAAAAYP///////wD/////////OAAAAAAAAAC4////////////xAAAAAAAAAAw////////AP////////8cAAAAAAAAAOD////////////oAAAAAAAAABT///////8A/////////wAAAAAAAAAA//////////////8AAAAAAAAAAP///////wD/////////AAAAAAAAAAD//////////////wAAAAAAAAAA////////AP////////8AAAAAAAAAAP/////////////8AAAAAAAAAAD///////8A/////////xwAAAAAAAAA5P///////////+AAAAAAAAAAHP///////wD/////////NAAAAAAAAAC8////////////uAAAAAAAAAA4////////AP////////9oAAAAAAAAAHj///////////98AAAAAAAAAGT///////8A/////////6gAAAAAAAAAGPD/////////+BgAAAAAAAAApP///////wD/////////9AwAAAAAAAAAUPz///////xcAAAAAAAAAAjs////////AP//////////cAAAAAAAAAAALKjs//CwOAAAAAAAAAAAYP////////8A///////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzk/////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP////////////+QAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A//////////////+sEAAAAAAAAAAAAAAAAAAAAAyg/////////////wD////////////////oZAgAAAAAAAAAAAAAAARg4P//////////////AP//////////////////9KhsOCAAAAAUMFyc7P////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'P' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////wAAAAAAAAAAAAAAAAAACCxguP////////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAOOD//////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAGOD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAARP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAABo////////////AP///////////wAAAAAAAAAA////6JwMAAAAAAAAADD///////////8A////////////AAAAAAAAAAD//////6AAAAAAAAAADP///////////wD///////////8AAAAAAAAAAP//////9AAAAAAAAAAA////////////AP///////////wAAAAAAAAAA///////0AAAAAAAAAAD///////////8A////////////AAAAAAAAAAD//////5gAAAAAAAAAHP///////////wD///////////8AAAAAAAAAAP///9iICAAAAAAAAABI////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAIT/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAABU/P////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAIhPz//////////////wD///////////8AAAAAAAAAAAAAAAAABCRMkOz/////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'Q' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////SoaDQcAAAAHDhoqPT///////////////////8A//////////////BwDAAAAAAAAAAAAAAACHDo/////////////////wD///////////+4FAAAAAAAAAAAAAAAAAAAABCo////////////////AP//////////nAQAAAAAAAAAAAAAAAAAAAAAAACQ//////////////8A/////////7gEAAAAAAAAAAAAAAAAAAAAAAAAAACg/////////////wD////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzo////////////AP///////3AAAAAAAAAAACyo8P/sqCwAAAAAAAAAAGT///////////8A///////4EAAAAAAAAABM+P///////FQAAAAAAAAACPT//////////wD//////7AAAAAAAAAAFPD/////////9BgAAAAAAAAApP//////////AP//////bAAAAAAAAAB4////////////fAAAAAAAAABk//////////8A//////84AAAAAAAAALz///////////+8AAAAAAAAADT//////////wD//////xwAAAAAAAAA6P///////////+QAAAAAAAAAHP//////////AP//////AAAAAAAAAAD//////////////wAAAAAAAAAA//////////8A//////8AAAAAAAAAAP//////////////AAAAAAAAAAD//////////wD//////wAAAAAAAAAA/P////////////8AAAAAAAAAAP//////////AP//////GAAAAAAAAADg////////////4AAAAAAAAAAc//////////8A//////84AAAAAAAAALT////MJHTo//+8AAAAAAAAADT//////////wD//////2wAAAAAAAAAdP///2AAABCg/3wAAAAAAAAAZP//////////AP//////rAAAAAAAAAAY9P/sCAAAAABMGAAAAAAAAACk//////////8A///////4EAAAAAAAAABU/P+0OAAAAAAAAAAAAAAACPT//////////wD///////94AAAAAAAAAAA4sPD/gAAAAAAAAAAAAABk////////////AP////////AcAAAAAAAAAAAAAAAAAAAAAAAAAAAADOT///////////8A/////////7wEAAAAAAAAAAAAAAAAAAAAAAAAAACQ/////////////wD//////////6wEAAAAAAAAAAAAAAAAAAAAAAAAABSs////////////AP///////////7gUAAAAAAAAAAAAAAAAAAAAAAAAAABAwP////////8A//////////////BwDAAAAAAAAAAAAAAABAgAAAAAAAA8/////////wD////////////////0qGg0GAAAABgwXJjkxBgAAAAAALD/////////AP//////////////////////////////////5DQAAAAk/P////////8A////////////////////////////////////+GwAAJD//////////wD//////////////////////////////////////8A49P//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'R' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////wAAAAAAAAAAAAAAAAAAAAQgOGSk+P///////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAcuP//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAEsP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ6P///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD///////////8A/////////wAAAAAAAAAA///////svDgAAAAAAAAACP///////////wD/////////AAAAAAAAAAD/////////7AAAAAAAAAAA////////////AP////////8AAAAAAAAAAP/////////cAAAAAAAAABD///////////8A/////////wAAAAAAAAAA//////DQoCQAAAAAAAAAQP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACU////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIPj///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAzU/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAA02P//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAxctPz///////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAEDY/////////////////wD/////////AAAAAAAAAAD/9LAsAAAAAAAAAAzc////////////////AP////////8AAAAAAAAAAP///+wkAAAAAAAAADD8//////////////8A/////////wAAAAAAAAAA/////8QAAAAAAAAAAJD//////////////wD/////////AAAAAAAAAAD//////1QAAAAAAAAAFPD/////////////AP////////8AAAAAAAAAAP//////3AQAAAAAAAAAgP////////////8A/////////wAAAAAAAAAA////////aAAAAAAAAAAM6P///////////wD/////////AAAAAAAAAAD////////oCAAAAAAAAABs////////////AP////////8AAAAAAAAAAP////////+AAAAAAAAAAATc//////////8A/////////wAAAAAAAAAA//////////AUAAAAAAAAAFj//////////wD/////////AAAAAAAAAAD//////////5AAAAAAAAAAAND/////////AP////////8AAAAAAAAAAP//////////+CQAAAAAAAAAQP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'S' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////8vHBEIAgAAAQgQHC8/P////////////////8A////////////////pCQAAAAAAAAAAAAAAAAcoP///////////////wD//////////////FwAAAAAAAAAAAAAAAAAAAAAXP//////////////AP////////////9oAAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A////////////zAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////9cAAAAAAAAAAAAAAAAAAAAAAAAAACA////////////AP///////////xgAAAAAAAAAUOD/8KwkAAAAAAAAADj///////////8A////////////AAAAAAAAAAD0/////8wABCAgICxASP///////////wD///////////8MAAAAAAAAAMz/////////////////////////////AP///////////0AAAAAAAAAACFiQxPT///////////////////////8A////////////oAAAAAAAAAAAAAAAADBwtPT//////////////////wD////////////8QAAAAAAAAAAAAAAAAAAACFTA////////////////AP/////////////oOAAAAAAAAAAAAAAAAAAAAABM6P////////////8A///////////////4fAgAAAAAAAAAAAAAAAAAAAAY2P///////////wD/////////////////7IwwAAAAAAAAAAAAAAAAAAAo+P//////////AP/////////////////////koGw0BAAAAAAAAAAAAACU//////////8A///////////////////////////4uFgAAAAAAAAAADz//////////wD//////////2BgSEA0IBwA6P///////5QAAAAAAAAADP//////////AP//////////JAAAAAAAAACc/////////AAAAAAAAAAA//////////8A//////////9YAAAAAAAAACDo///////AAAAAAAAAABT//////////wD//////////6QAAAAAAAAAACCk7P/snBQAAAAAAAAAUP//////////AP//////////+BAAAAAAAAAAAAAAAAAAAAAAAAAAAACs//////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAAOP///////////wD////////////8RAAAAAAAAAAAAAAAAAAAAAAAABjc////////////AP/////////////0PAAAAAAAAAAAAAAAAAAAAAAg2P////////////8A///////////////8hBQAAAAAAAAAAAAAAAAMdPT//////////////wD/////////////////+LRwSCAMAAAAHDhoqPT/////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'T' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'U' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////JAAAAAAAAADk/////////+gAAAAAAAAAHP//////////AP////////9MAAAAAAAAAJz/////////nAAAAAAAAABE//////////8A/////////4gAAAAAAAAAHOj//////+ggAAAAAAAAAHz//////////wD/////////0AAAAAAAAAAAIJzs/+ykIAAAAAAAAAAA0P//////////AP//////////QAAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A///////////IBAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAAAAJj/////////////AP////////////+UBAAAAAAAAAAAAAAAAAAAAASU//////////////8A///////////////IPAAAAAAAAAAAAAAAAAAwyP///////////////wD/////////////////0IxYOCAIAAAEIEiAyP//////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'V' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////zAAAAAAAAAAYP//////////////ZAAAAAAAAAAw////////AP//////kAAAAAAAAAAU/P////////////8UAAAAAAAAAJD///////8A///////oBAAAAAAAAADE////////////xAAAAAAAAAAE7P///////wD///////9MAAAAAAAAAHD///////////94AAAAAAAAAEz/////////AP///////6gAAAAAAAAAJP///////////yQAAAAAAAAArP////////8A////////+BAAAAAAAAAA1P/////////YAAAAAAAAABT4/////////wD/////////aAAAAAAAAACE/////////4QAAAAAAAAAbP//////////AP/////////EAAAAAAAAADT/////////OAAAAAAAAADM//////////8A//////////8kAAAAAAAAAOT//////+QAAAAAAAAAKP///////////wD//////////4QAAAAAAAAAmP//////nAAAAAAAAACI////////////AP//////////5AAAAAAAAABE//////9EAAAAAAAABOT///////////8A////////////QAAAAAAAAAT0////9AgAAAAAAABI/////////////wD///////////+gAAAAAAAAAKT///+kAAAAAAAAAKj/////////////AP////////////QIAAAAAAAAXP///1wAAAAAAAAM+P////////////8A/////////////1wAAAAAAAAM+P/8DAAAAAAAAGT//////////////wD/////////////vAAAAAAAAAC8/7wAAAAAAAAAxP//////////////AP//////////////HAAAAAAAAGj/aAAAAAAAACT///////////////8A//////////////94AAAAAAAAHP8cAAAAAAAAhP///////////////wD//////////////9gAAAAAAAAAkAAAAAAAAADk////////////////AP///////////////zgAAAAAAAAQAAAAAAAAQP////////////////8A////////////////lAAAAAAAAAAAAAAAAACg/////////////////wD////////////////sCAAAAAAAAAAAAAAADPT/////////////////AP////////////////9QAAAAAAAAAAAAAABg//////////////////8A/////////////////7AAAAAAAAAAAAAAAMD//////////////////wD//////////////////BQAAAAAAAAAAAAc////////////////////AP//////////////////cAAAAAAAAAAAAHz///////////////////8A///////////////////MAAAAAAAAAAAA3P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'W' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//8cAAAAAAAAALz/////4AAAAAAAAAAA6P////+8AAAAAAAAABz//wD//1QAAAAAAAAAjP////+gAAAAAAAAAACo/////4wAAAAAAAAAUP//AP//jAAAAAAAAABU/////2AAAAAAAAAAAGj/////VAAAAAAAAACM//8A///EAAAAAAAAACT/////IAAAAAAAAAAAKP////8kAAAAAAAAAMT//wD///gEAAAAAAAAAPD//+AAAAAAAAAAAAAA6P//8AAAAAAAAAAE9P//AP///zAAAAAAAAAAvP//oAAAAAAAAAAAAACo//+8AAAAAAAAADD///8A////bAAAAAAAAACM//9gAAAAAAAAAAAAAGT//4wAAAAAAAAAaP///wD///+kAAAAAAAAAFT//yAAAAAAAAAAAAAAIP//VAAAAAAAAACc////AP///9gAAAAAAAAAJP/gAAAAAAAAAAAAAAAA4P8kAAAAAAAAANT///8A/////xAAAAAAAAAA8KAAAAAAAAAAAAAAAACg8AAAAAAAAAAQ/////wD/////TAAAAAAAAAC8YAAAAAAAAAAAAAAAAGC8AAAAAAAAAET/////AP////+AAAAAAAAAAIwgAAAAAAAAAAAAAAAAIIwAAAAAAAAAfP////8A/////7gAAAAAAAAANAAAAAAAACwwAAAAAAAANAAAAAAAAACw/////wD/////8AAAAAAAAAAAAAAAAAAAdHgAAAAAAAAAAAAAAAAAAOz/////AP//////KAAAAAAAAAAAAAAAAAC4vAAAAAAAAAAAAAAAAAAg//////8A//////9gAAAAAAAAAAAAAAAACPj4CAAAAAAAAAAAAAAAAFj//////wD//////5QAAAAAAAAAAAAAAABE//9IAAAAAAAAAAAAAAAAkP//////AP//////0AAAAAAAAAAAAAAAAIj//4wAAAAAAAAAAAAAAADI//////8A///////8DAAAAAAAAAAAAAAAzP//1AAAAAAAAAAAAAAABPj//////wD///////88AAAAAAAAAAAAABT/////GAAAAAAAAAAAAAA0////////AP///////3QAAAAAAAAAAAAAWP////9gAAAAAAAAAAAAAHD///////8A////////sAAAAAAAAAAAAACg/////6QAAAAAAAAAAAAApP///////wD////////kAAAAAAAAAAAAAOT/////6AAAAAAAAAAAAADc////////AP////////8cAAAAAAAAAAAo////////MAAAAAAAAAAAEP////////8A/////////1QAAAAAAAAAAHD///////94AAAAAAAAAABM/////////wD/////////jAAAAAAAAAAAtP///////7wAAAAAAAAAAID/////////AP/////////EAAAAAAAAAAT0////////+AgAAAAAAAAAuP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'X' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////9UAAAAAAAAAKz///////////+sAAAAAAAAAFD/////////AP///////+QQAAAAAAAAFOT/////////8BwAAAAAAAAM5P////////8A/////////5gAAAAAAAAATP////////9kAAAAAAAAAJD//////////wD//////////0AAAAAAAAAAoP//////wAAAAAAAAAA0/P//////////AP//////////2AgAAAAAAAAQ4P////gkAAAAAAAABMz///////////8A////////////iAAAAAAAAABA////dAAAAAAAAABw/////////////wD////////////8MAAAAAAAAACU/9AEAAAAAAAAHPD/////////////AP/////////////IBAAAAAAAAAzYMAAAAAAAAACs//////////////8A//////////////90AAAAAAAAABAAAAAAAAAATP///////////////wD///////////////QgAAAAAAAAAAAAAAAAAAzg////////////////AP///////////////7wAAAAAAAAAAAAAAAAAjP////////////////8A/////////////////2AAAAAAAAAAAAAAADD8/////////////////wD/////////////////7BQAAAAAAAAAAAAEyP//////////////////AP/////////////////gDAAAAAAAAAAAAAjY//////////////////8A/////////////////0AAAAAAAAAAAAAAADj8/////////////////wD///////////////+UAAAAAAAAAAAAAAAAAJD/////////////////AP//////////////4AwAAAAAAAAAAAAAAAAADOD///////////////8A//////////////9AAAAAAAAAAAAAAAAAAAAAQP///////////////wD/////////////nAAAAAAAAAAAWAAAAAAAAAAAlP//////////////AP///////////+QQAAAAAAAAAGD/YAAAAAAAAAAM4P////////////8A////////////TAAAAAAAAAAs9P/0LAAAAAAAAABM/////////////wD//////////6AAAAAAAAAADNT////UDAAAAAAAAACg////////////AP/////////kEAAAAAAAAACg//////+gAAAAAAAAABDk//////////8A/////////0wAAAAAAAAAYP////////9gAAAAAAAAAEz//////////wD///////+oAAAAAAAAACz0//////////QsAAAAAAAAAKT/////////AP//////7BQAAAAAAAAM1P///////////9QMAAAAAAAAFOz///////8A//////9UAAAAAAAAAKD//////////////6AAAAAAAAAAVP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'Y' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////1QAAAAAAAAAAGj//////////2gAAAAAAAAAAFT///////8A////////5BAAAAAAAAAAAMT////////EAAAAAAAAAAAQ5P///////wD/////////mAAAAAAAAAAAKPj/////+CgAAAAAAAAAAJj/////////AP//////////PAAAAAAAAAAAgP////+AAAAAAAAAAAA8//////////8A///////////YCAAAAAAAAAAE2P//2AQAAAAAAAAACNj//////////wD///////////+AAAAAAAAAAAA4//84AAAAAAAAAACA////////////AP////////////woAAAAAAAAAACUlAAAAAAAAAAAKPz///////////8A/////////////8gAAAAAAAAAABAQAAAAAAAAAADI/////////////wD//////////////2wAAAAAAAAAAAAAAAAAAAAAbP//////////////AP//////////////8BwAAAAAAAAAAAAAAAAAABzw//////////////8A////////////////tAAAAAAAAAAAAAAAAAAAtP///////////////wD/////////////////VAAAAAAAAAAAAAAAAFT/////////////////AP/////////////////oEAAAAAAAAAAAAAAQ6P////////////////8A//////////////////+cAAAAAAAAAAAAAJz//////////////////wD///////////////////9AAAAAAAAAAABA////////////////////AP///////////////////9gAAAAAAAAAANj///////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-      'Z' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAQ//////////////8A/////////////////////////1AAAAAAAAAABLz//////////////wD///////////////////////98AAAAAAAAAACY////////////////AP//////////////////////pAAAAAAAAAAAaP////////////////8A/////////////////////8QIAAAAAAAAAET8/////////////////wD////////////////////gGAAAAAAAAAAo9P//////////////////AP//////////////////9CwAAAAAAAAAFNz///////////////////8A//////////////////xMAAAAAAAAAATA/////////////////////wD/////////////////eAAAAAAAAAAAnP//////////////////////AP///////////////5wAAAAAAAAAAHT///////////////////////8A///////////////ABAAAAAAAAABM/P///////////////////////wD/////////////3BQAAAAAAAAALPT/////////////////////////AP////////////QoAAAAAAAAABjg//////////////////////////8A///////////8SAAAAAAAAAAExP///////////////////////////wD//////////2wAAAAAAAAAAKD/////////////////////////////AP////////+YAAAAAAAAAAB8//////////////////////////////8A/////////wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
-    );
-  
-    return $_png;
-  }
-  
-  // These define base64_encoded raw png image data used
-  // when we cannot generate our own single png image
-  function define_raw_pngs()
-  {
-    $_png = array(
-      '0' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QKCNGXKO6AAAAB3RJTUUH0wUOEDQ6EUG1VwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXNJREFUeNpj/M9AHGAiUt2wVvhyaqAqKyOjpG3jQwaGv+e+IUn9RwJfSjjg4iwFP1aKJD6HyyErfGGAYrquIoP5E2wK/zigu0v5wH9sChdgeKDqP1aFGhBZmxv/z0Dd4IxV4RWIpMQHIPuJAITzAqEQETx7IFQIP5CQNoJwDmALxzMQCuyjg1chnBPYwtECwr8AZN41h0p6YHOjAkTuwf//77wYuCEcFWwKOWA2fM1iZuuHcASwKYQ55c9ENuasrxgRjKlwJS+D17v/hBUeUGYwv/sfn0IRiJQZJIbxuFEFagjvSlDUQNgK2GIGqpC1JRhIfoAqxBYz0DRhn8IMJO+giKEqhMaMJBeI3AHhIKdkRPqG8DlAifqFADyasKRHO6h1Z/6fMYEwTbCmx3cWGCl8CTaFwBhGz+M2/7EpXMvOnBmIok7jBVaFz/Mi3/1pQORrhpgPyOr+M8IL0j9/gKpeLjhy5QEwoDVsYuRR3cE4IktcAJNx8cJaZBeQAAAAAElFTkSuQmCC', 
-      '1' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMi//xxVKAAAAB3RJTUUH0wUOEDYLcqnX7wAAAAlwSFlzAAALEgAACxIB0t1+/AAAAHpJREFUeNpj/M9AHGAiUh1WhR8FGUGAsMKaD9iM/I8BlmCVwVS4hoUohT8qcNiFyv2zQIWBCIV3amRwu54RKcDRAgQ1KigIcJYK7CqR3QsCFmf+Y8qgeQakbANMAz6FKjUXECbj8zWa76nm61GFw1UhI10KqVGFNFQIADdK9Zj7PsV9AAAAAElFTkSuQmCC', 
-      '2' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMwPUBEjoAAAAB3RJTUUH0wUOEDUqFe2UcgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAQxJREFUeNpj/M9AHGAiUt2owkGrkAWF93LFgStPfjCwyGiYRGijqfyPAH9aOJAkQl78RwbICkNQjdB4gUNhD7qzLLAr/CKA4YENSAoRvl7zAUJXvPmxhgfCXILVMxEQvg+IDVUhgtVqDYjkDhD7B2aQIMIx5cOTN29evLAAsaEKObBajQzmQOQMcIQjHLwQgSisIaDwBdS5LHfwK7yhAHVVyX+8CrdAA5HB5gdehQ3Yoxpd4ZcAmDqbD//xKISEIjhU//zHoxDmXQaeFRhOZ8CmzuDOf3wKf8DsDfnyH6/CHJi6P//xKjyDJethVehBpMI7DPgVwrPCCgb8AK5wDwGFcNMF8EkCASOx1QcAGUxu1untnFIAAAAASUVORK5CYII=', 
-      '3' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMxBQugk2AAAAB3RJTUUH0wUOEDU3duv4qwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATdJREFUeNpj/M9AHGAiUt0IVciCzPm7ZceZB28YGBQkLHwcmNFU/keANRJI4ioH/qMAJIUlaHatwaFwBrqrOO5gVfiCB8P9KVgVVkAtnPDh/wkLCFsGq0IFiGQLiH0D06P/GWHJ7O+NOzfuXLlzQRrEhgSawHscwYPurxAcwQMBf/4/aIAYyHIGr8IEeDhO+Y9XoQNUncwOVHGMRPEDSovc+IkzrpGDCQgUbuC1WgBhhsIHfAp3vPn/oIIFKfRxKQSDGohCA4IKX0DTD7YoRAWMUJ9iyQpbn4DBBWUQ5yFEDDnFw622gXAzwBxoYvfB5sYlUI0lD/4/gWWKJdgU/tHAcKjCD6y+PsGCpo4FJbaRgmcNqkqWCThTzxkTJHXo+Ro1HA9uOPHiATDlKJj4eKCVFIzDqWgGAK7GW/haPS+zAAAAAElFTkSuQmCC', 
-      '4' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMyqWttCEAAAAB3RJTUUH0wUOEDUxn4hdngAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKBJREFUeNpj/M9AHGAiUh2FCucyQgCK4H9McIAFixwWhQ8kGIhS+MWAgTiFIQzEKWxhIE7hFgbiFF7hASkQIajwjQpInuUAIYV/XMDyU/4TUlgAlk75T0jhArCszR9CCk+AY07mxX8CCp+AY47nzH8CCn+YgOWW/CekMAYsVfMfl0JGmCBq4kNEDp2zAn0UMmItABjRvDykPTO43DgyFQIANP6pTFLWAdoAAAAASUVORK5CYII=', 
-      '5' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMzPy3XhEAAAAB3RJTUUH0wUOEDUk8lW5dQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAQpJREFUeNpj/M9AHGAiUt2oQuIVfmREBzgU3iHWxAfEKiTaRFpZnfAfAbAr/AsxUYagiVCbeQgqhPpFYmukLCOrZupRNJUIB02BCAjAZCK+/Ed2LoJZgm6bzRfsCgMw3JWAXaEBpg8uIGSRPPMBQmXc+P+iggXCnoOQZUQK1K8PgEAjGcQs7QGL6FzG5mtkcAUiyYIQYcRRUkDTLEIWR1b4ixamQMPhrKUP3rx48eDNFXmwdyFiOthixgXqaTAnBcKpwRaOS6A6Mx78fwBVx/IAm8I/KsTGzAkWNHUyb7Ar/L8GNSlK3MCRev7/v+CApC7kBUoUoAX4yQ0nHjwAWqpiE6GNFgNDoAwHAKC2Q2lMNcCmAAAAAElFTkSuQmCC', 
-      '6' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNAObRd4vAAAAB3RJTUUH0wUOEDUc2lcB6wAAAAlwSFlzAAALEgAACxIB0t1+/AAAATtJREFUeNpj/M9AHGAiUh2Gwq2puryMjKKmmSfRVf5HBkcMEBI+L1CkUBROYUE2QuMFLoVr0CzzwKHwhQC6szZgV1gAtfHI/xs2mEYywsPxp8QHEMVxQ56B4aaJiIKIiIRCPDZf74DwI/5jB4hwPAChbAgG+BWoExlOxkoysuqW3sUV4BoQ/p0SqARLB44AF4HIByDMKMCuEIu7phCrUOADNl/DgMOJ/09SIMwPC7B5hgfC1/kB4kRAOC7YrFaByM0Ac85AOCLYrFaBhSMIQNPlG2wBDg3HP2CSGU/MuEAoiKVXUWxB9cwPiG8UwEGSg5FCMNOjwZ4/byqgpqwgMoWr/MGeZ1agqWPZgSNz/Z+AqnDCf1wK/29B8qbKDhQpRtTE8HfLjjMP3jDwKJh4hKCGJSPNC6lRhTRWCABWpdoxd/bZ4QAAAABJRU5ErkJggg==', 
-      '7' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNA18/fMoAAAAB3RJTUUH0wUOEDUVo4u5TwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAM9JREFUeNpj/M9AHGAiUt2oQnorZIGzGLFJIyJ40HqGhUiFPFuQ/YUFPBGBmLcDSQybwj8OEDOW/CegsAeiruQ/AYV3OMDqTP4QUugCceCN/wQUQn1a8Z+Awj8qYHUiHwgpXAAxcMJ/Qgp1wOoEPhBSuANiYM5/QgpjIAovEFL4gweszgAz0NASxZ4vYMqHYDKDBiIWhWhWa0CS1x9CVn+8AaYsmAlZfQRC6RDMChADGTQIKjxDrMI7EEoBi0JGlMJe8AOY+sFOSCEeQHQBAABCZ7xyT9fJhwAAAABJRU5ErkJggg==', 
-      '8' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNBeBnwpSAAAAB3RJTUUH0wUOEDUOKe5wowAAAAlwSFlzAAALEgAACxIB0t1+/AAAATVJREFUeNpj/M9AHGAiUt1AKmRB459cc+DBGwYWGQ2LEG1Umf/I4IELkozLA2QpFIUXJFDMEDiBQ+EHGTR3yHzArrAFwwct2BXqQGQ1zvw/owFh6mBXCDXmDJB5BsOrjEhxzfoHIgkiGCGB9xtrgEPtOwvEV6FWY4+ZAAgVc5LhZgKEGYI9wN+gBiPu4Pl/BFWlxA1cMfN/C0rUr8AVhX8K0KyuwaEwASNmarAqPACVTXnw/0oENBFewKYQGhYZYE4MVBM2hVAvQ1LhHQhHBVsUMjIgYhCdhy3PPASTd6GOxBYz0KhOQHajDjY3pkC1Rlz5fweqjqEAm8ILGK5gYLlDZICXYI+ZLzZo6gL+4EgUfyo4kJQJtCCpQ8kKQPB2zZ47L14AU5iMgUMAN7IM43AqHwdQIQAhMPz6Gz5V/wAAAABJRU5ErkJggg==', 
-      '9' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNCQ+T2tEAAAAB3RJTUUH0wUOEDUHUDLIBwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAUZJREFUeNpj/M9AHGAiUh26wr9rE3V5GRlFTTM3/kVT+R8Z7FBBSKjsQJFCUTiFBcWMCbgUHmBBs20FdoV/VNDUMQi8wapwDVS65s2fPToQZgFWhRFIkm8kwGyeH9gUQm2+Aua0QDhb4LJI4XgHQmmDSRMIZw+emIEENAeEcwObQhEIdQHiABRbUGPGBSIQAWL/gHqbB5tnJkC1Fjz5f8IGwxwkhR8EsCQarFE4hViF/wsQCgKgHsSu8H8HLFkUQL2rgUPh/zslOiwMEjFH/kND2geXQvQgqMAWhSjgAIRygAswIuXCpXfevHjz4M0ZdQaGhxo/wAnyBTuWmPnvARGxuPH/iAa+9Ph/A7r9Ai+wK/zvg6ZwzX8cCl9oICtjmfIfl8L/bwIQ6gyO/Met8P//EwUmwHTJo5OyBU2CkdaF1KhCWisEAM/sJxmZkdWnAAAAAElFTkSuQmCC', 
-      'A' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QFwy1U7TfAAAAB3RJTUUH0wUOEC0ZKCZtPQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAO1JREFUeNrt1LERwiAUBuAHZ2GRwsIypQMwQEZwgBQpM4QDZBSLFI7gCA5gQWGRdA5gkTuMSh48eMTUnq96wH98B+QiDCwruTD3D76qF676ueAp0Y9lSBXeSkFWaLAje3T+kkzK4SgpBzZw8pqxJWcdOJuRsyGPbWDk0tS20zw9SXsobdfytJVXdzNsP61i6Zt3K7Ht0UeUgbPdjsrOXMd+2IS2C2qb271HVWi7YANcNXFQsUEVBTXwNdl46jYRxPl52dnwRUZbhkLSDmS8DnxFRWiULxg8UxvobefuRR8ZQYDKtffVVcQWv/RrfgJC4bd0upw4MQAAAABJRU5ErkJggg==', 
-      'B' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGAusrz2zAAAAB3RJTUUH0wUOEC01Gv4B3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAANJJREFUeNpj/M9AHGAiUh0tFTKiAUHL2rsoKv9DARZDWFr+IwA+hQwMFcQqZDhCrMIIYhWK4FYIYv8444PuV+wK//9/A+UJwBUSCHAL3OEIsdoFyttCpGdiiAtHjoY/RCnk6PlBbBRKrCE6CqcQq5DlDs5whIT3CgUI788EvOEIBCegXB2YPCNMBSNMISqf5TeUjysK90LpP/itfrFEAhZCMHkWdKMYUbk2MAah7BqD02pUYEFkgMu8IE6hD0IdpmegwSejoKLjoY7syaFU7A0HhQA2e4cJytImvAAAAABJRU5ErkJggg==', 
-      'C' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGBbPqVFqAAAAB3RJTUUH0wUOEC4BEGemqAAAAAlwSFlzAAALEgAACxIB0t1+/AAAASlJREFUeNpj/M9AHGAiUt2owkGrkAWV+3TDgRtPPjBwyGiYBOijSv1HAlcCkGUcTiDLISvsQDOeZQp2hQWYDpuCTeEEbD44ganwDgc2vxpgKoyAyUWc+f9hjgCMtwFd4RuYRxog/ueBcl3QFc6BSmj8gfBrwE40yFmCrjABqrAH5mSZgJ4jX7AEjwlU4Zn/OAAsrp9AaRlccc0IzdeMsBilOPWQrBDmtpfEKnwBpZ8qZq58i6IS6vscKHcBcgQYlOz4gh6OK6AKfaB8G5hN6Aq/wBLPHjB3CczCFIzUA0u2PD0v/j9pgaf1ExgK3wgwYAEOWFL4GizqWC5gyzM1mArnEJkLZ2DPhf//n3BAVmeDkq8ZUZPL3TUn7gBLCgYFBYsAcxQZRmKrDwABNsv9SJSDwwAAAABJRU5ErkJggg==', 
-      'D' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGC1+orhOAAAAB3RJTUUH0wUOEC4yr7fHvgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAM9JREFUeNpj/M9AHGAiUt1AKmSBsxiRhXlkNBxCpFFU/ocBTDMyPvxHADwKGRgUbhCpkEHiCZEKGRyIVciwArdCIPPFGg8YzwSvQiBogXFvEFD43wDKnQDl44yZGCh9glAU2sCsJqRQBkq/gMUw3G2wuP6PnU/H9PgRSgsQUvgESosQUngFSqsQUrgCSsNiCFcU7oBx9+CL6w8XamB5SeUPkelxAZEJ1+YPcQolXhCXFTTuEJULOUq+IOVrFgasQELBxMaHG1mEcTiVjwOoEADAIkCnGpmJKgAAAABJRU5ErkJggg==', 
-      'E' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGDeDwEE0AAAAB3RJTUUH0wUOEC8CkHXGUwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD5JREFUeNpj/M9AHGAiUt2owkGrkAXGYMQqjUgJQ8EzpPsa05+D140oMYTk4KEQ4MMqZqgUhcM1czESW30AABfqB1XDnLzcAAAAAElFTkSuQmCC', 
-      'F' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGQe8AkDZAAAAB3RJTUUH0wUOEC8JB6cf2wAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUeNpj/M9AHGAiUt3wUsiCYDJikUYE3lDwDDm+xvTp4HUjIoaQXTsUAnxYxcyoQryAcUSWuAAW/gZTg/yEMAAAAABJRU5ErkJggg==', 
-      'G' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGRFI1vWIAAAAB3RJTUUH0wUOEC8QY8y3GwAAAAlwSFlzAAALEgAACxIB0t1+/AAAASZJREFUeNpj/M9AHGAiUt0IVciCwvt7ZM+FOy8+MDBwSEho2AQII8v9R4A/U2RQtHEUfEBIIim8YYBhn8oNLAqP8GBxmcwbDIU3sKljYIhAV/jHgAE7uICmcAJMQqDmwp//D2YowPgxqAr/wPyr8QAi8EEHwleIQFW4BxYicG+eEHEomHECET5QhRVQhQn/cQFoFJ6AKgwgFNcPoFwdnAoZIXmGERahKDwkIdqlR1j4PiRW4RVCCmExvQenQrSYEXiDiAoUBfC4loAK23yBSnzArhCRehRmAJPFnRUxHDgU/lDA7zZECj/Cgl2dAkaeWYNVZcoHDIX/94hgKLM4gS27/v9QIICizGMDkiQjSon7c8eBCw+e/GFgkZEwsHCRRpZiHE5FMwCa2YE+WcAOUwAAAABJRU5ErkJggg==', 
-      'H' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGRw2Z4k1AAAAB3RJTUUH0wUOEC8agxleBQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD1JREFUeNpj/M9AHGAiUt2oQvyABUozQml4+KMLDAXPDAWFLGh8RlwKh4JnaB88GOlxELhxVCFewDgEynAAN2sFVHAvevkAAAAASUVORK5CYII=', 
-      'I' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGSlg1E0WAAAAB3RJTUUH0wUOEC86uHd+zQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD5JREFUeNpj/M9AHGAiUt1AKmRBMBkxJJE9OhQ8Q32FjGhxDQsjjCQwFDwzqnCwKkRKZqO5EBMwDqcSl2iFAMMeB0s/kLo2AAAAAElFTkSuQmCC', 
-      'J' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGywiiNsbAAAAB3RJTUUH0wUOEDAFw0tdbgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKdJREFUeNpj/M9AHGAiUh3xClmwijJCaSR3Ud/qUYWjCklTyIHEhifctw8ePHgCxO+B7L9QMQlsChW+QOiX4gwMd6BiItisVoHSB6AYWQwM/kNBBszkC/9PwKyc8B8B4Ar3YPHMHWwK/xtgqAv4j1XhEfScK/EEu8L/a1BVStz4j0Ph/yPItoe8QFH3nxGlkNq75cKDB0DDVBwitNEcwjhwpdmoQrwAAN6ioiFapgUdAAAAAElFTkSuQmCC', 
-      'K' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHAEoFhGpAAAAB3RJTUUH0wUOEDANzZDVXAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPZJREFUeNpj/M9AHGAiUt2owgFSyAgFMOGDrDARxkKo0H8wYEDh/b/AAzepACqEVeEdCQx1WBW+0ICry/mPR+EXE7i6kD94FP5xwaYOi8IIrOowFRbA1Xkgq8NQ2ANXZ/PlPx6FS3CpQ1fIAmOIoKn7jxbXf2CMNxvQIxvVRAQQ+YDXaiSQQqxChiOEFGoIQGidP/gVStxogLI68CqUuPH/BzSVcTzAoxCo7v//ObBIxK0QrO7/H1iCXIFT4QkIFxbaMh9wKYQJO0D5OYQUnoDF/QkCCuHJ1+APAYV3YOloAgGF8JTO84SAwjfQiGQIgPAZqV4rAACnKSarzdlc4gAAAABJRU5ErkJggg==', 
-      'L' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHA64qQw4AAAAB3RJTUUH0wUOEDAXMPIsJgAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUeNpj/M9AHGAiUt2QUMiCYDJCaezhMBQ8M6pwVCEdFLJgCjEisRH5Zyh4hvoKGUdkQUq0QgARaARRV9jUFQAAAABJRU5ErkJggg==', 
-      'M' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHBhMfblpAAAAB3RJTUUH0wUOEDAqaJpgNwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPNJREFUeNrdlK0OgzAUhS8bCQYxMYmcmEAgEAgejQfZQyG2pAIxOYlATkAu691o2tvSYia2iv7lyzn3NG0jhG1tt5H7Aggom7ZuaKPhBFqKV+pFWDGjjcxStEAYXuvBkrKtoVX+gdRiK9i6sxjgeVGUMJzWwZLACaZOTqoAOAronmrlBuvPkQsIgHn8BqnE2AMmhaaYJ57jqTRFMwsDyW249XaJLhAujizm7UFM5XCUXTqiTvBLQYWRc7H3WWt+3NmlyGbOGh9q/45mjQxUb+CA6A2jSqu5MweX0ooQWLJxLYx6fz0GwmBOsww5GP3At/dX4Ayb7qpFI9y5ygAAAABJRU5ErkJggg==', 
-      'N' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHC6DxyzwAAAAB3RJTUUH0wUOEDAye/b4YQAAAAlwSFlzAAALEgAACxIB0t1+/AAAALRJREFUeNpj/M9AHGAiUt0IV8gIARsRMlAROP8/BEB5Ii/+/0cVgXNRhRk8iFXIMIFYhRxXiFTIYPCDSIUMBcQqZNhDrEKZN0QqZAggViHDHIIKRSAUzx1CCrdAaZM/BBT+z4Eyaggp/KEDYbAcIaDw/wUWCEuBkML/PagBgFvhfxdiFT4RIVLh/zXEKvyfQqzCLypEKvx/hoVIhf9biFX4x4ZIhf8fCBCp8P8KNBHG4VQ0AwDEOyeZhO5p1AAAAABJRU5ErkJggg==', 
-      'O' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHQExDSDoAAAAB3RJTUUH0wUOEDA4myMRfwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATtJREFUeNpj/M9AHGAiUt3wUsiCyv265ciZJ08YGGRkDGwCuFGk/iOBDwU8SDIcGS+Q5JAV7hBBs45nAVaFC1gwXTYBi8IdWNQxMCzAUPhBBJs6Bp4n6AoLYFI6az78f7NEB8ZNQFP4QwAqEfADwg+A+f0NqsI1UHGBDzCnSKC6EhYzB6B0Cj+UwZ+CKgNTeAZKu8C94QGlL6DGjAyU+wAeXC+gIiIQLiM0KzDC9CFCBlWICsnsL3aFMDc+hcs8QZWBKYSF2g24whvYFZpA6T1whUegNCwyoYGxAmYyLGZ+wOxYghqFX2BpO+APmP8nBspHj2uk1LPizf8PGyxgXPTUQ3x6JDqF//8/AYs6bHkGmCYF0O3FnguBCSaFA0kZS8IDJDlG1IIUVFK8eABMWzI6DgHCyDKMI7LEBQCD5YgI9wbKGgAAAABJRU5ErkJggg==', 
-      'P' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHQvR2Mn2AAAAB3RJTUUH0wUOEDEDMzPJGgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKVJREFUeNpj/M9AHGAiUh05ChlRAKdu4k5Ulf9hANMQiwf/EQCfQgaJB0QqZHAhViHDEbg0AV8vwRM8QN0v5vBAOSfw+BrMWQDl8MClGeEKGGEKQcRXHmQemTGD1RMy+N14o4MDyvGAS7NgGMaIzPHAYyIy4HhBZMy0EBmFIX+IUsjRgqQOi2fAgEVBwyVGGEUEQw2O3EbLzDWSFDIOhtJsVCEWAAC/Yt2X+2PYcgAAAABJRU5ErkJggg==', 
-      'Q' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHRxSC0wxAAAAB3RJTUUH0wUOEDEKSu9xvgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAW1JREFUeNpj/M9AHGAiUt2QUMiCzPm65cCZF08YGGRkDBx8uNFU/oeDDwU8SOIcBS/+IwOEwh0iaEYIrMCqcA4LprsmYFG4A4s6BoYFGAo/iGBTx8DzAl1hAUxKZ8WH/29W6MC4KWgKfwhAJXx+gPl/QmB+/4KqcANUXOQDVPiLBFRkCUwhJGb2wGzihzK4U6CMA6hReAbKc4F7wwFKX0CNGRkoB+HJJ1ARGZgAIziFM8J0IUIGXYjMZPaXkEJYYDyBiz+EuRFVoQKUdwWIz6qWvmRguAMVkUBVaIIUalPu9GgshIefAWrwrIHp//L/DQc4KjFiBi2uQ/7832KB5AX0uP5fAZOx2PDhfwNCIXrq+f9BhgEb4HmCkcL3YE3hSHkBnmfWYFMpsoaYXAgGDgcwFKLlaxYOCG2DqRCYrldkmIACUMIgZsaTI5Cg3IBNISp4AoovlT+EFf7/kYPkb3wK//8/YAGPGcYhUIYDAHBC9Yak1w7iAAAAAElFTkSuQmCC', 
-      'R' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHSkEuIgSAAAAB3RJTUUH0wUOEDEUsOBM3QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAOZJREFUeNpj/M9AHGAiUh0NFLJAaUY0YRkJHYcQdmSh/xCAzRCZHf8RAJ9CBpYNRCpkEHgBV4jfMx+mEOVGIDDAaTWY82aPBZTLgV8hUCkaH6cbP8B8gxHgyODjgwstMDfiVIgWQyFE+lrhB3EBznOFuJgxuUFMXPPEbPmDpA53FH55osKMIoAe4F826MDMvPMfj9WgWFGBBeIf/Ar/H4FxJhBQ+B8WzCIfCCi8A4uvBgIK/2fA/POCgMIXHFBuDqH02ABLM3cIKPwgAuVHEFD4fwJM4AIBhT9goe4AFWAcAsXesFIIAEvJyZHTCSiTAAAAAElFTkSuQmCC', 
-      'S' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHTRnvuTLAAAAB3RJTUUH0wUOEDEbIF9RTAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAVZJREFUeNpj/M9AHGAiUt2oQvyABYX398CWK3de/GBgkVEw8HFgRpH7jwSWqCDLyCxAlkNS+CcG3boY7AozMB3Wgk3hGSw+4HgBl0b4egIWhT9mYPGMBFQg4MH/D2tgvrKASzPC0yMjlP7CDSTOmrDIMDDwiHBsxzSRBypw5j9WgFDoAPNAxIQjX/ApXIDsC4OCLV9wKfzjwIACOEIO4IiZFxbooePzAqvC/z9qONBUStzAqvD//zc9BqgqNX5gVwgETxbkmCClvSk4FYLdsCMCptAGI2YSGV78+PLmz5MX4mDu1ByIMM9n9JiBxe4caGChy8MZMMsUIEFyAMoVwVC4BGaEwpI3/9/MEYGlJQyFPwQYsIE1mL7GlnCR0iNSXLtgqpO4gy1mvtigq1NAxCBKgP9pEUFWxlOCnNIYUYrmn3v23Ljx5gsw88sYOPhwI0sxDoEyHAABtSc836a1EQAAAABJRU5ErkJggg==', 
-      'T' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHgUdTbcyAAAAB3RJTUUH0wUOEDEgkVS4aAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADdJREFUeNpj/M9AHGAiUt0IVcgCpRlxyMODeSh4hmiFjGipB+Z7jEQ1FDwzqnBU4WBSyDicimYAb/AFTaJpyH8AAAAASUVORK5CYII=', 
-      'U' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHhEHl2NPAAAAB3RJTUUH0wUOEDEon48wWgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKlJREFUeNpj/M9AHGAiUh3xClmgNCOUhrsEXYD6Vo8qHFVIuUIVKP0USr+E0jLoCjWg9A4ovQVNHJjUIaADZsILMPeFApRfA5X/D1N4AaZRYc6b/2+WwNQxXEBX+N8Bqxcc/mMoPMGCRR3LBUyF/2dgUTjjPxaF/6egm8ky5T9Whf9P2KCoMziBJPefEaWQurjnzIMXL34wsMhoWHiYo2hjHLjSbFQhXgAAKzejCLAOcVMAAAAASUVORK5CYII=', 
-      'V' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHh/gL05IAAAAB3RJTUUH0wUOEDEuduyVbwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAVNJREFUeNpj/M9AHGAiUt2owoFRaMgIAYlIMqlQMUMo/z8ITIByRP78hwMRqNgECBei8AULVPQIXN0RqAjLGwgfYrW4B1R4DdzmLVDaQxjZ6v8roDwVuIkqMK3/ka3+/0MAKn4FKn4D5uof/5GtZmCPgEpsQHNDBDsDitVwt5tA+RZQ/pn/qFYj3PQEzHsC5WnA3QyPmQQU3+5AE0VYDTfDBcxzgQbik/8YVv93gMp9AbK/cEAD8T+m1TBb/oD8veEHhs0IE2GmxADZMRAmz4//WKxGkv3DA2Gm/MeqcA/Ujj1w1hHsCv/LQKQz/megRzyawgqIvAxMRwsuhbCEAEvGT3AphEUwNCU5IEv9R8lcUH9/wAxE5HAEgjccSBI8X3CbKOyBxAnhxm3i/w1IEgdQZFA98/+PCFydDKo6VKsZmGPQ0wgOq/+fgYvfQTORkeq1AgCIAvD7+THsDgAAAABJRU5ErkJggg==', 
-      'W' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QFhZRKnzkAAAAB3RJTUUH0wUOEDIR66frkQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXNJREFUeNrtlK1ywkAUhZdMZsJMKyIqKhAIBAKBiEBEVCDyCJV9iIo+Do9QGRERgUBEVCAqKhAIREVERURnTvfn3t27xSA6g+kOQ/ZkP/aec5NlBHXZSC7k/sE/AhUwoVkDPQ58/2RUQ2IC6B1XpN7MV8tg62/pUdjSDO7OwR2J0pbekpqZYlMG50bNSGwBDQ4pyV5YtCZ7mqZf1mO2IN2Jynba0XRx49pThjQCbEKWFfVRpIlBzlK4PuLdpxEWlTr4LHvYMEDOaTYS3HCW3DAJt8mmaSXYchZbOfEzkyYGZRbrEbX8qe7GMpLqFeyxV9F4fon1pwcxjxbqJpJTBPBJLoyHYSz1I3xq78aOMssepHZZHFjKhbX9/AZd6e9bsdABeyHTQXiE2PLO6PugCwiP/r1QVLYSlpXwKE1Wno7b7jY+hoWj0aegPyA9+jPrzgqwZJ0j8hhMVtElmDoD19FFPAvamc+sOXBm+KdYEzC63p/9D7Tr72kj/8qjAAAAAElFTkSuQmCC', 
-      'X' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHi/G9n7kAAAAB3RJTUUH0wUOEDIXAsROpAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAT9JREFUeNpj/M9AHGAiUt3IVhjKCAFr4RJroSKBMIH/YPBEAMITeQLh//8gAxHggQlAFf6fAdXnA+WnQPkT/qMp/O8AlVkA5h2A8kz+YCi8wQGREngA5PxQgXBYzvzHUPi/A2qIA5BdAmUX/Mei8I8BVHbK/wssEJbMB2wK/5+ASvPcgGlZ8x+rQriFAmghgKHwiwJKXPA8wKXw/x4UhT3/cSr8n4CkzuAPHoVvRODqWE6gyPxHTT1ffiAUCjCgAhRtDkgSFnisnoJixAScCh/wEBk8DmiucsChcA5MQQSMMQWrQlgiZ0iAByey5QiFPlBZnS//v+hgxjZc4QKYKVeAnCswby3AUAi3eAGKNoEn6Ap94A5EjXUfNIUrEA6EALgzl6AohCUGsAMhAOZMkTfICkMw3I5wZgiEyzicimYAFRFkVwgDfJ0AAAAASUVORK5CYII=', 
-      'Y' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHjkyIsu1AAAAB3RJTUUH0wUOEDIkvRQvsgAAAAlwSFlzAAALEgAACxIB0t1+/AAAANJJREFUeNrt1L0NgzAQBWAcUVB6AAZgBAoKhmAICoZgCAoKxmAECkbwABSUlBRILwF8duwYhFJEihJ37+6T5T9g8K6N20X3FdDDNjKKOeTIqZLtWcKBU73bCx1lPhgQNTWieY1zRLmGCZFQp1xTSSmBDUUgW754BF+GQLxAPUkMxMb0FlzUsqpKLXhxQPRqo+oIerggCvuMC7jhFJounA4gWhO2OIL6Jp/uzglHrh0fTyAaDRucQaTkUpxDQVBYDWZ/hYze6bsv/A8/DNlP/kgvwzuer4kCMGPZDgAAAABJRU5ErkJggg==', 
-      'Z' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHwfqWOdfAAAAB3RJTUUH0wUOEDIrLasyIwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAL5JREFUeNrl1C0OwkAQBWCWQIJEVPQIFT0GAlHBMRBIBKIHqahAIDlERY9R0UOs3ORh5qVLunmp5GfUZvczbzKzDqtltV7ofgtueHCp16h33xBGwn0KYqoTO/J868Csaj418e0cPujOkLDfmTsECcfcXOGhoC/NZQMUDBUDd5DwxiAtJGzprpCw48xVQcIhM1d6KOgLc/kIBcORgXtIeGGQOyRs6Oq0g7P92YbkRE7bRZhcwhh+6nLF5f7yx30B8Z7FgxzMWtEAAAAASUVORK5CYII=', 
-    );
-  
-    return $_png;
-  }
-  
-  // This is designed to randomise the pixels of the image data within
-  // certain limits so as to keep it readable. It also varies the image
-  // width a little
-  function randomise($scanline, $width)
-  {
-    $new_line = '';
-    $start = floor($width/2);
-    $end = strlen($scanline) - ceil($width/2);
-  
-    for ($i = $start; $i < $end; $i++)
-    {
-      $pixel = ord($scanline{$i});
-  
-      if ($pixel < 190)
-      {
-        $new_line .= chr(mt_rand(0, 205));
-      }
-      else if ($pixel > 190)
-      {
-        $new_line .= chr(mt_rand(145, 255));
-      }
-      else
-      {
-        $new_line .= $scanline{$i};
-      }
-    }
-  
-    return $new_line;
-  }
+		// Output image
+		header('Content-Type: image/png');
+		header('Cache-control: no-cache, no-store');
+		echo $image;
+		
+		unset($image);
+		unset($_png);
+	}
+	// This creates a chunk of the given type, with the given data
+	// of the given length adding the relevant crc
+	function png_chunk($length, $type, $data)
+	{
+		$raw = $type;
+		$raw .= $data;
+		$crc = crc32($raw);
+		$raw .= pack('C4', $crc >> 24, $crc >> 16, $crc >> 8, $crc);
+	
+		return pack('C4', $length >> 24, $length >> 16, $length >> 8, $length) . $raw;
+	}
+	
+	// Creates greyscale 8bit png - The PNG spec can be found at
+	// http://www.libpng.org/pub/png/spec/PNG-Contents.html we use
+	// png because it's a fully recognised open standard and supported
+	// by practically all modern browsers and OSs
+	function create_png($raw_image, $width, $height)
+	
+	{
+		// SIG
+		$image = pack('C8', 137, 80, 78, 71, 13, 10, 26, 10);
+		// IHDR
+		$raw = pack('C4', $width >> 24, $width >> 16, $width >> 8, $width);
+		$raw .= pack('C4', $height >> 24, $height >> 16, $height >> 8, $height);
+		$raw .= pack('C5', 8, 0, 0, 0, 0);
+		$image .= $this->png_chunk(13, 'IHDR', $raw);
+	
+		if (@extension_loaded('zlib'))
+		{
+			$raw_image = gzcompress($raw_image);
+			$length = strlen($raw_image);
+		}
+		else
+		{
+			// The total length of this image, uncompressed, is just a calculation of pixels
+			$length = ($width + 1) * $height;
+	
+			// Adler-32 hash generation
+			// Optimized Adler-32 loop ported from the GNU Classpath project
+			$temp_length = $length;
+			$s1 = 1;
+			$s2 = $index = 0;
+	
+			while ($temp_length > 0)
+			{
+				// We can defer the modulo operation:
+				// s1 maximally grows from 65521 to 65521 + 255 * 3800
+				// s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
+				$substract_value = ($temp_length < 3800) ? $temp_length : 3800;
+				$temp_length -= $substract_value;
+	
+				while (--$substract_value >= 0)
+				{
+					$s1 += ord($raw_image[$index]);
+					$s2 += $s1;
+	
+					$index++;
+				}
+	
+				$s1 %= 65521;
+				$s2 %= 65521;
+			}
+			$adler_hash = pack('N', ($s2 << 16) | $s1);
+	
+			// This is the same thing as gzcompress($raw_image, 0) but does not need zlib
+			$raw_image = pack('C3v2', 0x78, 0x01, 0x01, $length, ~$length) . $raw_image . $adler_hash;
+	
+			// The Zlib header + Adler hash make us add on 11
+			$length += 11;
+		}
+	
+		// IDAT
+		$image .= $this->png_chunk($length, 'IDAT', $raw_image);
+	
+		// IEND
+		$image .= $this->png_chunk(0, 'IEND', '');
+	
+		return $image;
+	}
+	
+	// Each 'data' element is base64_encoded uncompressed IDAT
+	// png image data
+	function define_filtered_pngs()
+	{
+		$_png = array(
+			'0' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////olFAkBAAAGDyA4P///M31/////////////wD////////////////0dAgAAAAAAAAAAAAEcPipFGHn////////////AP//////////////6DAAAAAAAAAAAAAAAAAALSEAN+T///////////8A//////////////xAAAAAAAAAAAAAAAAAAAAAACPA/////////////wD/////////////oAAAAAAAAAAAAAAAAAAAAAAAev//////////////AP////////////8oAAAAAAAAPNj/zDAAAAAAAABD//////////////8A////////////1AAAAAAAABjw////5BAAAAAAAADo/////////////wD///////////+QAAAAAAAAbP//////QgAAAAAAAKj/////////////AP///////////1wAAAAAAACs/////8AXAAAAAAAAcP////////////8A////////////OAAAAAAAAND////dNwAAAAAAAABI/////////////wD///////////8gAAAAAAAA4P//7koACwAAAAAAACT/////////////AP///////////wgAAAAAAAD///VqAwaPAAAAAAAAEP////////////8A////////////AAAAAAAAAP/8kQYDavUAAAAAAAAA/////////////wD///////////8AAAAAAAAA/6kNAEru/wAAAAAAAAD/////////////AP///////////wAAAAAAAADAIwA33f//AAAAAAAAAP////////////8A////////////FAAAAAAAADYAI8D///8AAAAAAAAQ/////////////wD///////////8kAAAAAAAAAA2p////5AAAAAAAACD/////////////AP///////////0gAAAAAAAAFkfz////UAAAAAAAAQP////////////8A////////////cAAAAAAAAET1/////7AAAAAAAABo/////////////wD///////////+oAAAAAAAAXfX/////sAAAAAAAAGj/////////////AAAAALgAAAAAAAAwAAAAAAAAAAAAAAD////////////oAAAAAAAACOT////oEAAAAAAAAOD/////////////AP////////////8+AAAAAAAAKMz/zDQAAAAAAAA0//////////////8A////////////7jgAAAAAAAAAAAAAAAAAAAAAAKT//////////////wD///////////VqAwIAAAAAAAAAAAAAAAAAAAA8////////////////AP//////////rQcDaVEAAAAAAAAAAAAAAAAAKOj///////////////8A///////////nblnu/IAIAAAAAAAAAAAAAFzw/////////////////wD////////////79////+iITCAAAAAgSITg////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////w==','width' => 40), 
+			'1' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////8BAAAAAAAP//////////////////AP////////////////////////9sAAAAAAAA//////////////////8A////////////////////////pAAAAAAAAAD//////////////////wD//////////////////////6wEAAAAAAAAAP//////////////////AP////////////////////h4AAAAAAAAAAAA//////////////////8A//////////////////ygJAAAAAAAAAAAAAD//////////////////wD//////////////9x8HAAAAAAAAAAAAAAAAP//////////////////AP//////////////AAAAAAAAAAAAAAAAAAAA//////////////////8A//////////////8AAAAAAAAAAAAAAAAAAAD//////////////////wD//////////////wAAAAAAAAR4AAAAAAAAAP//////////////////AP//////////////AAAAAAA4zP8AAAAAAAAA//////////////////8A//////////////8AAAA4sP///wAAAAAAAAD//////////////////wD//////////////yR80P//////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'2' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////okFAkCAAABCBIfNT///////////////////8A///////////////8hAgAAAAAAAAAAAAAAFTo/////////////////wD//////////////1QAAAAAAAAAAAAAAAAAACjo////////////////AP////////////+MAAAAAAAAAAAAAAAAAAAAADj///////////////8A////////////9BAAAAAAAAAAAAAAAAAAAAAAALD//////////////wD///////////+gAAAAAAAAAHjs+KwMAAAAAAAAVP//////////////AP///////////1gAAAAAAABM/////6QAAAAAAAAU//////////////8A////////////KAAAAAAAALj/////+AAAAAAAAAD//////////////wD///////////+MfGBMOCAI8P/////wAAAAAAAACP//////////////AP///////////////////////////5wAAAAAAAAw//////////////8A///////////////////////////oFAAAAAAAAHz//////////////wD/////////////////////////6CgAAAAAAAAE3P//////////////AP///////////////////////9ggAAAAAAAAAHT///////////////8A//////////////////////+0DAAAAAAAAAA8+P///////////////wD/////////////////////gAAAAAAAAAAAKOj/////////////////AP//////////////////9FAAAAAAAAAAADzw//////////////////8A/////////////////+g4AAAAAAAAAABk/P///////////////////wD////////////////oKAAAAAAAAAAMqP//////////////////////AP//////////////6CgAAAAAAAAAMNz///////////////////////8A//////////////g4AAAAAAAAAFT0/////////////////////////wD/////////////bAAAAAAAAABU/P//////////////////////////AP///////////8wAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A////////////SAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////9AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////xAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'3' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////8sGg0FAAAACA4cLz8////////////////////AP//////////////rBgAAAAAAAAAAAAAACTA//////////////////8A/////////////3QAAAAAAAAAAAAAAAAAAASs/////////////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAjc////////////////AP//////////6AwAAAAAAAAAAAAAAAAAAAAAAGT///////////////8A//////////94AAAAAAAABJDw/8g4AAAAAAAAHP///////////////wD//////////yAAAAAAAACE/////9gAAAAAAAAA////////////////AP///////////NSwiGQ4FOT//////AAAAAAAABD///////////////8A//////////////////////////+YAAAAAAAAVP///////////////wD//////////////////////P/ggAQAAAAAAATM////////////////AP////////////////////9gAAAAAAAAAAAElP////////////////8A/////////////////////0AAAAAAAAAAHLj//////////////////wD/////////////////////OAAAAAAAAAAwkPj/////////////////AP////////////////////8gAAAAAAAAAAAAINj///////////////8A/////////////////////xAAAAAAAAAAAAAAIPD//////////////wD/////////////////////uOz/4HgEAAAAAAAAhP//////////////AP///////////////////////////3wAAAAAAAAw//////////////8A////////////////////////////6AAAAAAAAAj//////////////wD/////////////////////////////AAAAAAAAAP//////////////AP//////////tJh8YEQoDNz//////+AAAAAAAAAY//////////////8A//////////88AAAAAAAAaP//////dAAAAAAAAEz//////////////wD//////////6QAAAAAAAAAdOD/5HQAAAAAAAAApP//////////////AP///////////CgAAAAAAAAAAAAAAAAAAAAAACD4//////////////8A////////////yAQAAAAAAAAAAAAAAAAAAAAEuP///////////////wD/////////////rAQAAAAAAAAAAAAAAAAABJD/////////////////AP//////////////zDQAAAAAAAAAAAAAACTA//////////////////8A/////////////////8BwOCAAAAAUNGi0/P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'4' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////////////nAAAAAAAAAD///////////////8A/////////////////////////8AEAAAAAAAAAP///////////////wD////////////////////////gGAAAAAAAAAAA////////////////AP//////////////////////9DAAAAAAAAAAAAD///////////////8A//////////////////////9UAAAAAAAAAAAAAP///////////////wD/////////////////////hAAAAAAAAAAAAAAA////////////////AP///////////////////7QAAAAAAAAAAAAAAAD///////////////8A///////////////////UDAAAAAAUAAAAAAAAAP///////////////wD/////////////////7CQAAAAABMAAAAAAAAAA////////////////AP////////////////xEAAAAAACU/wAAAAAAAAD///////////////8A////////////////cAAAAAAAZP//AAAAAAAAAP///////////////wD//////////////6AAAAAAADz8//8AAAAAAAAA////////////////AP/////////////IBAAAAAAc6P///wAAAAAAAAD///////////////8A////////////5BgAAAAADMz/////AAAAAAAAAP///////////////wD///////////g0AAAAAACk//////8AAAAAAAAA////////////////AP//////////XAAAAAAAfP///////wAAAAAAAAD///////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP///////////////////////////wAAAAAAAAD///////////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'5' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////8AAAAAAAAAAAAAAAAAAAAAAA//////////////8A///////////////MAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////6wAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////iAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////////9kAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////0QAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////IAAAAAAAYP////////////////////////////8A//////////////wAAAAAAAB8/////////////////////////////wD/////////////3AAAAAAAAIj/////////////////////////////AP////////////+4AAAAAAAAoLRYHAAEKGTE//////////////////8A/////////////5QAAAAAAAAQAAAAAAAAAABY9P///////////////wD/////////////dAAAAAAAAAAAAAAAAAAAAAA89P//////////////AP////////////9QAAAAAAAAAAAAAAAAAAAAAABg//////////////8A/////////////zAAAAAAAAAAAAAAAAAAAAAAAADQ/////////////wD/////////////IAAAAAAAAGjY/+h4BAAAAAAAAGz/////////////AP//////////////9NS0lHSc//////90AAAAAAAALP////////////8A/////////////////////////////9QAAAAAAAAE/////////////wD//////////////////////////////wAAAAAAAAD/////////////AP/////////////////////////////8AAAAAAAAEP////////////8A////////////pIRwWEAgDOD//////8wAAAAAAAA8/////////////wD///////////9EAAAAAAAAaP//////ZAAAAAAAAHz/////////////AP///////////6QAAAAAAAAAaOD/4GQAAAAAAAAE4P////////////8A/////////////CQAAAAAAAAAAAAAAAAAAAAAAGD//////////////wD/////////////yAQAAAAAAAAAAAAAAAAAAAAc7P//////////////AP//////////////rAwAAAAAAAAAAAAAAAAAGNj///////////////8A////////////////0EAAAAAAAAAAAAAAAFTo/////////////////wD//////////////////8h4QCAAAAAcQHzU////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'6' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////+0ZCwMAAAUNGjI////////////////////AP/////////////////EMAAAAAAAAAAAAABM6P////////////////8A////////////////lAQAAAAAAAAAAAAAAAAo6P///////////////wD//////////////6wAAAAAAAAAAAAAAAAAAABI////////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAACw//////////////8A/////////////3AAAAAAAAAoxP/YPAAAAAAAAEj//////////////wD////////////4EAAAAAAACOD////YDCBAVGiAoP//////////////AP///////////7gAAAAAAABY//////////////////////////////8A////////////eAAAAAAAAJT//////////////////////////////wD///////////9MAAAAAAAAvP/IXBgABCx03P//////////////////AP///////////ygAAAAAAADcdAAAAAAAAAAEiP////////////////8A////////////FAAAAAAAAFAAAAAAAAAAAAAAcP///////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAlP//////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAQ8P////////////8A////////////AAAAAAAAAABAyP/kZAAAAAAAAACQ/////////////wD///////////8MAAAAAAAALPj/////WAAAAAAAAET/////////////AP///////////yQAAAAAAACY///////MAAAAAAAAFP////////////8A////////////SAAAAAAAAMD///////wAAAAAAAAA/////////////wD///////////9wAAAAAAAAvP///////wAAAAAAAAD/////////////AP///////////7QAAAAAAACI///////UAAAAAAAAJP////////////8A////////////+AwAAAAAACDw/////2wAAAAAAABY/////////////wD/////////////cAAAAAAAADC8/Ox4AAAAAAAAAKj/////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAAAk/P////////////8A//////////////+oAAAAAAAAAAAAAAAAAAAABLj//////////////wD///////////////+QAAAAAAAAAAAAAAAAAACQ////////////////AP////////////////+0JAAAAAAAAAAAAAAkuP////////////////8A///////////////////8sGg0FAAADCxgqPz//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'7' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAABP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAy4/////////////wD//////////////////////////+QUAAAAAAAEuP//////////////AP/////////////////////////8QAAAAAAAAKT///////////////8A/////////////////////////4wAAAAAAAB0/////////////////wD////////////////////////cCAAAAAAANPz/////////////////AP///////////////////////0QAAAAAAATY//////////////////8A//////////////////////+0AAAAAAAAeP///////////////////wD//////////////////////CQAAAAAABTw////////////////////AP////////////////////+gAAAAAAAAkP////////////////////8A/////////////////////ywAAAAAABDw/////////////////////wD///////////////////+4AAAAAAAAbP//////////////////////AP///////////////////1wAAAAAAADQ//////////////////////8A///////////////////4DAAAAAAAMP///////////////////////wD//////////////////7QAAAAAAAB8////////////////////////AP//////////////////aAAAAAAAAMj///////////////////////8A//////////////////8oAAAAAAAM/P///////////////////////wD/////////////////8AAAAAAAAET/////////////////////////AP////////////////+0AAAAAAAAcP////////////////////////8A/////////////////4wAAAAAAACY/////////////////////////wD/////////////////WAAAAAAAAMD/////////////////////////AP////////////////80AAAAAAAA4P////////////////////////8A/////////////////xAAAAAAAAD4/////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'8' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////////IdDQUAAAEIEiA1P//////////////////AP/////////////////gRAAAAAAAAAAAAAAAROD///////////////8A////////////////0BgAAAAAAAAAAAAAAAAAEMj//////////////wD///////////////AcAAAAAAAAAAAAAAAAAAAAHPD/////////////AP//////////////hAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A//////////////8sAAAAAAAAKMz/zCgAAAAAAAAs/////////////wD//////////////wAAAAAAAADM////zAAAAAAAAAD/////////////AP//////////////BAAAAAAAAP//////AAAAAAAABP////////////8A//////////////8sAAAAAAAAzP///9QAAAAAAAAw/////////////wD//////////////3wAAAAAAAAoyP/YNAAAAAAAAIT/////////////AP//////////////7BgAAAAAAAAAAAAAAAAAAAAc8P////////////8A////////////////xBgAAAAAAAAAAAAAAAAAGNj//////////////wD/////////////////tAQAAAAAAAAAAAAAAACo////////////////AP///////////////HAAAAAAAAAAAAAAAAAAAAB8//////////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB8/////////////wD/////////////wAAAAAAAAABk4P/UWAAAAAAAAATQ////////////AP////////////9UAAAAAAAAaP//////XAAAAAAAAGT///////////8A/////////////xgAAAAAAADg///////cAAAAAAAAJP///////////wD/////////////AAAAAAAAAP////////8AAAAAAAAA////////////AP////////////8AAAAAAAAA4P//////3AAAAAAAAAT///////////8A/////////////ygAAAAAAABg//////9cAAAAAAAALP///////////wD/////////////ZAAAAAAAAABY1P/cXAAAAAAAAABw////////////AP/////////////QAAAAAAAAAAAAAAAAAAAAAAAABNz///////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB0/////////////wD///////////////Q8AAAAAAAAAAAAAAAAAAAAUPz/////////////AP////////////////x4CAAAAAAAAAAAAAAAEIT8//////////////8A///////////////////smFQwGAAAABg0ZKT0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'9' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////ysYCwMAAAUNGiw/P//////////////////AP////////////////+4JAAAAAAAAAAAAAAkuP////////////////8A////////////////lAQAAAAAAAAAAAAAAAAAkP///////////////wD//////////////8AEAAAAAAAAAAAAAAAAAAAAqP//////////////AP/////////////8JAAAAAAAAAAAAAAAAAAAAAAQ7P////////////8A/////////////6wAAAAAAAAAfOz8vCwAAAAAAABw/////////////wD/////////////WAAAAAAAAHD/////7BgAAAAAAAz4////////////AP////////////8kAAAAAAAA1P//////hAAAAAAAALT///////////8A/////////////wAAAAAAAAD///////+4AAAAAAAAcP///////////wD/////////////AAAAAAAAAPz//////8AAAAAAAABI////////////AP////////////8UAAAAAAAAzP//////lAAAAAAAACT///////////8A/////////////0QAAAAAAABY//////gsAAAAAAAADP///////////wD/////////////kAAAAAAAAABw5P/IPAAAAAAAAAAA////////////AP/////////////wEAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////////+UAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////9wAAAAAAAAAAAAAFAAAAAAAAAU////////////AP////////////////+IBAAAAAAAAABw3AAAAAAAACj///////////8A///////////////////cdCwEABhcxP+8AAAAAAAATP///////////wD//////////////////////////////5AAAAAAAAB4////////////AP//////////////////////////////UAAAAAAAALj///////////8A//////////////+kgGxUQCAM2P///+AIAAAAAAAQ+P///////////wD//////////////0gAAAAAAAA42P/EKAAAAAAAAHD/////////////AP//////////////sAAAAAAAAAAAAAAAAAAAAAAQ6P////////////8A////////////////TAAAAAAAAAAAAAAAAAAAAKz//////////////wD////////////////oKAAAAAAAAAAAAAAAAASU////////////////AP/////////////////sUAAAAAAAAAAAAAAwxP////////////////8A////////////////////yHA0FAAADCxktP///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'A' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////+QAAAAAAAAAAAAAAOT/////////////////AP//////////////////kAAAAAAAAAAAAAAAkP////////////////8A//////////////////88AAAAAAAAAAAAAAA8/////////////////wD/////////////////5AAAAAAAAAAAAAAAAADk////////////////AP////////////////+QAAAAAAAAAAAAAAAAAJD///////////////8A/////////////////zwAAAAAAAAAAAAAAAAAPP///////////////wD////////////////kAAAAAAAAAAgAAAAAAAAA5P//////////////AP///////////////5AAAAAAAAAAgAAAAAAAAACQ//////////////8A////////////////PAAAAAAAAAz8HAAAAAAAADz//////////////wD//////////////+QAAAAAAAAAWP9kAAAAAAAAANz/////////////AP//////////////kAAAAAAAAACk/7wAAAAAAAAAhP////////////8A//////////////88AAAAAAAABOz//BQAAAAAAAAw/////////////wD/////////////4AAAAAAAAAA8////ZAAAAAAAAADc////////////AP////////////+EAAAAAAAAAIj///+8AAAAAAAAAIT///////////8A/////////////zAAAAAAAAAA2P////wQAAAAAAAAMP///////////wD////////////cAAAAAAAAACT//////1wAAAAAAAAA3P//////////AP///////////4QAAAAAAAAAAAAAAAAAAAAAAAAAAACE//////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAAAAAADD//////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANz/////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhP////////8A//////////8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw/////////wD/////////3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc////////AP////////+EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIT///////8A/////////zAAAAAAAAAAhP///////////2QAAAAAAAAAMP///////wD////////cAAAAAAAAAADM////////////vAAAAAAAAAAA3P//////AP///////4QAAAAAAAAAHP/////////////4DAAAAAAAAACE//////8A////////MAAAAAAAAABk//////////////9cAAAAAAAAADD//////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'B' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAEDh83P///////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAEhP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAeP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAABY////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAABT///////////8A//////////8AAAAAAAAAAP/////4zEwAAAAAAAAAAP///////////wD//////////wAAAAAAAAAA////////7AAAAAAAAAAQ////////////AP//////////AAAAAAAAAAD////////sAAAAAAAAAEj///////////8A//////////8AAAAAAAAAAP/////4zEQAAAAAAAAAtP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAFz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAiA/P////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAIjPj//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAGKz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJT///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABNz//////////wD//////////wAAAAAAAAAA///////sqCAAAAAAAAAAbP//////////AP//////////AAAAAAAAAAD/////////yAAAAAAAAAAs//////////8A//////////8AAAAAAAAAAP//////////AAAAAAAAAAT//////////wD//////////wAAAAAAAAAA/////////7wAAAAAAAAAAP//////////AP//////////AAAAAAAAAAD//////+ikGAAAAAAAAAAY//////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFT//////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsP//////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAADj///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAc6P///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAATOj/////////////AP//////////AAAAAAAAAAAAAAAAAAAEIEBkkNj///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'C' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////5JRULBAAAAgkTIDQ//////////////////8A////////////////1FAAAAAAAAAAAAAAAABAyP///////////////wD//////////////4gEAAAAAAAAAAAAAAAAAAAElP//////////////AP////////////9wAAAAAAAAAAAAAAAAAAAAAAAAlP////////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAEyP///////////wD//////////9wIAAAAAAAAAAAAAAAAAAAAAAAAAAAw////////////AP//////////WAAAAAAAAAAAWMz/8JwQAAAAAAAAAACw//////////8A/////////+wEAAAAAAAAAID//////9QMAAAAAAAAAET//////////wD/////////nAAAAAAAAAAo/P///////3wAAAAABDBspP//////////AP////////9gAAAAAAAAAIz/////////3BxQjMT0//////////////8A/////////zQAAAAAAAAAzP///////////////////////////////wD/////////GAAAAAAAAADo////////////////////////////////AP////////8AAAAAAAAAAP////////////////////////////////8A/////////wAAAAAAAAAA/////////////////////////////////wD/////////AAAAAAAAAAD/////////////////////////////////AP////////8cAAAAAAAAAOj///////////////////////////////8A/////////zgAAAAAAAAA0P/////////kIGio7P///////////////wD/////////bAAAAAAAAACg/////////5wAAAAAMHS49P//////////AP////////+oAAAAAAAAAEz/////////PAAAAAAAAAAc//////////8A//////////QIAAAAAAAAALz//////6QAAAAAAAAAAGT//////////wD//////////3AAAAAAAAAADIzo/+SEBAAAAAAAAAAAyP//////////AP//////////7BAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////rAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD/////////////fAAAAAAAAAAAAAAAAAAAAAAAAJz/////////////AP//////////////iAQAAAAAAAAAAAAAAAAAAASY//////////////8A////////////////yEAAAAAAAAAAAAAAAAA8yP///////////////wD//////////////////9yIUCwQAAAAIEB4yP//////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'D' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAADChQkOT/////////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAABGjw//////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAACDY/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAABjk////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKj//////////wD///////////8AAAAAAAAAAP///+isSAAAAAAAAAAANP//////////AP///////////wAAAAAAAAAA////////hAAAAAAAAAAA2P////////8A////////////AAAAAAAAAAD/////////MAAAAAAAAACQ/////////wD///////////8AAAAAAAAAAP////////+MAAAAAAAAAFj/////////AP///////////wAAAAAAAAAA/////////8gAAAAAAAAAMP////////8A////////////AAAAAAAAAAD/////////5AAAAAAAAAAY/////////wD///////////8AAAAAAAAAAP//////////AAAAAAAAAAD/////////AP///////////wAAAAAAAAAA//////////8AAAAAAAAAAP////////8A////////////AAAAAAAAAAD//////////wAAAAAAAAAA/////////wD///////////8AAAAAAAAAAP/////////wAAAAAAAAABD/////////AP///////////wAAAAAAAAAA/////////9QAAAAAAAAAJP////////8A////////////AAAAAAAAAAD/////////qAAAAAAAAABI/////////wD///////////8AAAAAAAAAAP////////9QAAAAAAAAAHj/////////AP///////////wAAAAAAAAAA////////uAAAAAAAAAAAvP////////8A////////////AAAAAAAAAAD////w0HwEAAAAAAAAACT8/////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAADz8//////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAY6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAKNz/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAACHT0//////////////8A////////////AAAAAAAAAAAAAAAAABg4bKj0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'E' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8=','width' => 40),
+			'F' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'G' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////MB8TCgQAAAACCA4YJzs////////////////AP///////////////JQcAAAAAAAAAAAAAAAAAAhw8P////////////8A/////////////9gwAAAAAAAAAAAAAAAAAAAAAAAk2P///////////wD////////////EDAAAAAAAAAAAAAAAAAAAAAAAAAAc7P//////////AP//////////2AwAAAAAAAAAAAAAAAAAAAAAAAAAAABY//////////8A//////////wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQ/////////wD/////////kAAAAAAAAAAAEHzQ/P/gmCAAAAAAAAAAAFz/////////AP////////wcAAAAAAAAACjg////////8CwAAAAAAAAgWP////////8A////////vAAAAAAAAAAI2P//////////yBRAcJjI8P///////////wD///////94AAAAAAAAAGD/////////////////////////////////AP///////0AAAAAAAAAAsP////////////////////////////////8A////////IAAAAAAAAADc/////////////////////////////////wD///////8AAAAAAAAAAP///////wAAAAAAAAAAAAAAAAD/////////AP///////wAAAAAAAAAA////////AAAAAAAAAAAAAAAAAP////////8A////////AAAAAAAAAAD///////8AAAAAAAAAAAAAAAAA/////////wD///////8gAAAAAAAAAOD//////wAAAAAAAAAAAAAAAAD/////////AP///////0AAAAAAAAAAtP//////AAAAAAAAAAAAAAAAAP////////8A////////cAAAAAAAAABw//////8AAAAAAAAAAAAAAAAA/////////wD///////+8AAAAAAAAABDs////////////AAAAAAAAAAD/////////AP////////wYAAAAAAAAADz0//////////AAAAAAAAAAAP////////8A/////////5AAAAAAAAAAACCY4P//3KhcCAAAAAAAAAAA/////////wD/////////+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////AP//////////xAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIP////////8A////////////rAQAAAAAAAAAAAAAAAAAAAAAAAAAAGTw/////////wD/////////////vBQAAAAAAAAAAAAAAAAAAAAAADjI////////////AP//////////////8HAQAAAAAAAAAAAAAAAAAEiw//////////////8A//////////////////iwcEAgBAAABCA4aKDk/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'H' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'I' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40),
+			'J' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAj//////////////wD//////////+zMrIxwUDAQ//////wAAAAAAAAAIP//////////////AP//////////DAAAAAAAAADo////2AAAAAAAAAA0//////////////8A//////////8wAAAAAAAAAKj///+YAAAAAAAAAFj//////////////wD//////////2gAAAAAAAAAIND/yBgAAAAAAAAAkP//////////////AP//////////vAAAAAAAAAAAAAAAAAAAAAAAAADc//////////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAUP///////////////wD////////////EBAAAAAAAAAAAAAAAAAAAABjk////////////////AP////////////+sBAAAAAAAAAAAAAAAAAAY2P////////////////8A///////////////EMAAAAAAAAAAAAAAAVOj//////////////////wD/////////////////vHBAIAAAABg8fNT/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40),
+			'K' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////8AAAAAAAAAAP//////////wAQAAAAAAAAAAABw////////AP///////wAAAAAAAAAA/////////9AMAAAAAAAAAAAAcP////////8A////////AAAAAAAAAAD////////cGAAAAAAAAAAAAHD//////////wD///////8AAAAAAAAAAP//////6CgAAAAAAAAAAABs////////////AP///////wAAAAAAAAAA//////Q0AAAAAAAAAAAAVPz///////////8A////////AAAAAAAAAAD////8RAAAAAAAAAAAAFT8/////////////wD///////8AAAAAAAAAAP///1gAAAAAAAAAAABU/P//////////////AP///////wAAAAAAAAAA//9wAAAAAAAAAAAASPz///////////////8A////////AAAAAAAAAAD/jAAAAAAAAAAAADz0/////////////////wD///////8AAAAAAAAAAKQAAAAAAAAAAAA89P//////////////////AP///////wAAAAAAAAAABAAAAAAAAAAAFPT///////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAApP///////////////////wD///////8AAAAAAAAAAAAAAAAAAAAAAAAU8P//////////////////AP///////wAAAAAAAAAAAAAAAAAAAAAAAABk//////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAAAADE/////////////////wD///////8AAAAAAAAAAAAAAAAoEAAAAAAAACz8////////////////AP///////wAAAAAAAAAAAAAAGNiAAAAAAAAAAIj///////////////8A////////AAAAAAAAAAAAABjY//gYAAAAAAAACOD//////////////wD///////8AAAAAAAAAAAAY2P///5wAAAAAAAAASP//////////////AP///////wAAAAAAAAAAGNj//////CgAAAAAAAAAqP////////////8A////////AAAAAAAAAADI////////sAAAAAAAAAAc8P///////////wD///////8AAAAAAAAAAP//////////QAAAAAAAAABs////////////AP///////wAAAAAAAAAA///////////IAAAAAAAAAATI//////////8A////////AAAAAAAAAAD///////////9YAAAAAAAAADD8/////////wD///////8AAAAAAAAAAP///////////9wEAAAAAAAAAJD/////////AP///////wAAAAAAAAAA/////////////3AAAAAAAAAADOT///////8A////////AAAAAAAAAAD/////////////7BAAAAAAAAAAUP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'L' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'M' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////8AAAAAAAAAAAAAAHz//////3wAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAATP//////UAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAc//////8cAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAADw////8AAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAAALz////AAAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAAkP///5AAAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAABc////ZAAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAoAAAAADD///8wAAAAACQAAAAAAAAA////////AP//////AAAAAAAAAFwAAAAABPz//AgAAAAAXAAAAAAAAAD///////8A//////8AAAAAAAAAkAAAAAAA0P/UAAAAAACQAAAAAAAAAP///////wD//////wAAAAAAAADMAAAAAACg/6gAAAAAAMQAAAAAAAAA////////AP//////AAAAAAAAAPgEAAAAAHD/dAAAAAAE+AAAAAAAAAD///////8A//////8AAAAAAAAA/zQAAAAAQP9IAAAAADD/AAAAAAAAAP///////wD//////wAAAAAAAAD/bAAAAAAQ/xQAAAAAaP8AAAAAAAAA////////AP//////AAAAAAAAAP+gAAAAAADQAAAAAACc/wAAAAAAAAD///////8A//////8AAAAAAAAA/9QAAAAAAGgAAAAAAND/AAAAAAAAAP///////wD//////wAAAAAAAAD//wwAAAAAFAAAAAAM/P8AAAAAAAAA////////AP//////AAAAAAAAAP//RAAAAAAAAAAAADz//wAAAAAAAAD///////8A//////8AAAAAAAAA//94AAAAAAAAAAAAcP//AAAAAAAAAP///////wD//////wAAAAAAAAD//7AAAAAAAAAAAACo//8AAAAAAAAA////////AP//////AAAAAAAAAP//5AAAAAAAAAAAANz//wAAAAAAAAD///////8A//////8AAAAAAAAA////HAAAAAAAAAAQ////AAAAAAAAAP///////wD//////wAAAAAAAAD///9QAAAAAAAAAEz///8AAAAAAAAA////////AP//////AAAAAAAAAP///4gAAAAAAAAAfP///wAAAAAAAAD///////8A//////8AAAAAAAAA////vAAAAAAAAACw////AAAAAAAAAP///////wD//////wAAAAAAAAD////wAAAAAAAAAOz///8AAAAAAAAA////////AP//////AAAAAAAAAP////8sAAAAAAAc/////wAAAAAAAAD///////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'N' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAALD/////////////AAAAAAAAAP//////////AP////////8AAAAAAAAAFOj///////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAASP///////////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAkP//////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAI1P////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAw+P///////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAABw////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAC8//////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAABzs/////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAFD/////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAJz///8AAAAAAAAA//////////8A/////////wAAAAAAAAAUAAAAAAAADNz//wAAAAAAAAD//////////wD/////////AAAAAAAAALQAAAAAAAAANPz/AAAAAAAAAP//////////AP////////8AAAAAAAAA/2wAAAAAAAAAfP8AAAAAAAAA//////////8A/////////wAAAAAAAAD/+CwAAAAAAAAExAAAAAAAAAD//////////wD/////////AAAAAAAAAP//0AQAAAAAAAAgAAAAAAAAAP//////////AP////////8AAAAAAAAA////jAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////RAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP/////kFAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA//////+sAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD///////9kAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP////////QkAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA/////////8wEAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD//////////4QAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP///////////DwAAAAAAAAAAP//////////AP////////8AAAAAAAAA////////////4BAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////////////qAAAAAAAAAD//////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'O' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////0qGw4HAAAABw4aKT0/////////////////wD////////////////wcAwAAAAAAAAAAAAAAAho6P//////////////AP//////////////uBQAAAAAAAAAAAAAAAAAAAAMoP////////////8A/////////////6AEAAAAAAAAAAAAAAAAAAAAAAAAkP///////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP//////////8BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAM5P////////8A//////////9wAAAAAAAAAAAsrPD/7KQsAAAAAAAAAABg/////////wD/////////+BAAAAAAAAAAUPj///////hQAAAAAAAAAAjs////////AP////////+sAAAAAAAAABDw//////////AYAAAAAAAAAKD///////8A/////////2wAAAAAAAAAdP///////////3wAAAAAAAAAYP///////wD/////////OAAAAAAAAAC4////////////xAAAAAAAAAAw////////AP////////8cAAAAAAAAAOD////////////oAAAAAAAAABT///////8A/////////wAAAAAAAAAA//////////////8AAAAAAAAAAP///////wD/////////AAAAAAAAAAD//////////////wAAAAAAAAAA////////AP////////8AAAAAAAAAAP/////////////8AAAAAAAAAAD///////8A/////////xwAAAAAAAAA5P///////////+AAAAAAAAAAHP///////wD/////////NAAAAAAAAAC8////////////uAAAAAAAAAA4////////AP////////9oAAAAAAAAAHj///////////98AAAAAAAAAGT///////8A/////////6gAAAAAAAAAGPD/////////+BgAAAAAAAAApP///////wD/////////9AwAAAAAAAAAUPz///////xcAAAAAAAAAAjs////////AP//////////cAAAAAAAAAAALKjs//CwOAAAAAAAAAAAYP////////8A///////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzk/////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP////////////+QAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A//////////////+sEAAAAAAAAAAAAAAAAAAAAAyg/////////////wD////////////////oZAgAAAAAAAAAAAAAAARg4P//////////////AP//////////////////9KhsOCAAAAAUMFyc7P////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'P' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////wAAAAAAAAAAAAAAAAAACCxguP////////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAOOD//////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAGOD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAARP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAABo////////////AP///////////wAAAAAAAAAA////6JwMAAAAAAAAADD///////////8A////////////AAAAAAAAAAD//////6AAAAAAAAAADP///////////wD///////////8AAAAAAAAAAP//////9AAAAAAAAAAA////////////AP///////////wAAAAAAAAAA///////0AAAAAAAAAAD///////////8A////////////AAAAAAAAAAD//////5gAAAAAAAAAHP///////////wD///////////8AAAAAAAAAAP///9iICAAAAAAAAABI////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAIT/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAABU/P////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAIhPz//////////////wD///////////8AAAAAAAAAAAAAAAAABCRMkOz/////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'Q' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////SoaDQcAAAAHDhoqPT///////////////////8A//////////////BwDAAAAAAAAAAAAAAACHDo/////////////////wD///////////+4FAAAAAAAAAAAAAAAAAAAABCo////////////////AP//////////nAQAAAAAAAAAAAAAAAAAAAAAAACQ//////////////8A/////////7gEAAAAAAAAAAAAAAAAAAAAAAAAAACg/////////////wD////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzo////////////AP///////3AAAAAAAAAAACyo8P/sqCwAAAAAAAAAAGT///////////8A///////4EAAAAAAAAABM+P///////FQAAAAAAAAACPT//////////wD//////7AAAAAAAAAAFPD/////////9BgAAAAAAAAApP//////////AP//////bAAAAAAAAAB4////////////fAAAAAAAAABk//////////8A//////84AAAAAAAAALz///////////+8AAAAAAAAADT//////////wD//////xwAAAAAAAAA6P///////////+QAAAAAAAAAHP//////////AP//////AAAAAAAAAAD//////////////wAAAAAAAAAA//////////8A//////8AAAAAAAAAAP//////////////AAAAAAAAAAD//////////wD//////wAAAAAAAAAA/P////////////8AAAAAAAAAAP//////////AP//////GAAAAAAAAADg////////////4AAAAAAAAAAc//////////8A//////84AAAAAAAAALT////MJHTo//+8AAAAAAAAADT//////////wD//////2wAAAAAAAAAdP///2AAABCg/3wAAAAAAAAAZP//////////AP//////rAAAAAAAAAAY9P/sCAAAAABMGAAAAAAAAACk//////////8A///////4EAAAAAAAAABU/P+0OAAAAAAAAAAAAAAACPT//////////wD///////94AAAAAAAAAAA4sPD/gAAAAAAAAAAAAABk////////////AP////////AcAAAAAAAAAAAAAAAAAAAAAAAAAAAADOT///////////8A/////////7wEAAAAAAAAAAAAAAAAAAAAAAAAAACQ/////////////wD//////////6wEAAAAAAAAAAAAAAAAAAAAAAAAABSs////////////AP///////////7gUAAAAAAAAAAAAAAAAAAAAAAAAAABAwP////////8A//////////////BwDAAAAAAAAAAAAAAABAgAAAAAAAA8/////////wD////////////////0qGg0GAAAABgwXJjkxBgAAAAAALD/////////AP//////////////////////////////////5DQAAAAk/P////////8A////////////////////////////////////+GwAAJD//////////wD//////////////////////////////////////8A49P//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'R' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////wAAAAAAAAAAAAAAAAAAAAQgOGSk+P///////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAcuP//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAEsP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ6P///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD///////////8A/////////wAAAAAAAAAA///////svDgAAAAAAAAACP///////////wD/////////AAAAAAAAAAD/////////7AAAAAAAAAAA////////////AP////////8AAAAAAAAAAP/////////cAAAAAAAAABD///////////8A/////////wAAAAAAAAAA//////DQoCQAAAAAAAAAQP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACU////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIPj///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAzU/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAA02P//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAxctPz///////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAEDY/////////////////wD/////////AAAAAAAAAAD/9LAsAAAAAAAAAAzc////////////////AP////////8AAAAAAAAAAP///+wkAAAAAAAAADD8//////////////8A/////////wAAAAAAAAAA/////8QAAAAAAAAAAJD//////////////wD/////////AAAAAAAAAAD//////1QAAAAAAAAAFPD/////////////AP////////8AAAAAAAAAAP//////3AQAAAAAAAAAgP////////////8A/////////wAAAAAAAAAA////////aAAAAAAAAAAM6P///////////wD/////////AAAAAAAAAAD////////oCAAAAAAAAABs////////////AP////////8AAAAAAAAAAP////////+AAAAAAAAAAATc//////////8A/////////wAAAAAAAAAA//////////AUAAAAAAAAAFj//////////wD/////////AAAAAAAAAAD//////////5AAAAAAAAAAAND/////////AP////////8AAAAAAAAAAP//////////+CQAAAAAAAAAQP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'S' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////8vHBEIAgAAAQgQHC8/P////////////////8A////////////////pCQAAAAAAAAAAAAAAAAcoP///////////////wD//////////////FwAAAAAAAAAAAAAAAAAAAAAXP//////////////AP////////////9oAAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A////////////zAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////9cAAAAAAAAAAAAAAAAAAAAAAAAAACA////////////AP///////////xgAAAAAAAAAUOD/8KwkAAAAAAAAADj///////////8A////////////AAAAAAAAAAD0/////8wABCAgICxASP///////////wD///////////8MAAAAAAAAAMz/////////////////////////////AP///////////0AAAAAAAAAACFiQxPT///////////////////////8A////////////oAAAAAAAAAAAAAAAADBwtPT//////////////////wD////////////8QAAAAAAAAAAAAAAAAAAACFTA////////////////AP/////////////oOAAAAAAAAAAAAAAAAAAAAABM6P////////////8A///////////////4fAgAAAAAAAAAAAAAAAAAAAAY2P///////////wD/////////////////7IwwAAAAAAAAAAAAAAAAAAAo+P//////////AP/////////////////////koGw0BAAAAAAAAAAAAACU//////////8A///////////////////////////4uFgAAAAAAAAAADz//////////wD//////////2BgSEA0IBwA6P///////5QAAAAAAAAADP//////////AP//////////JAAAAAAAAACc/////////AAAAAAAAAAA//////////8A//////////9YAAAAAAAAACDo///////AAAAAAAAAABT//////////wD//////////6QAAAAAAAAAACCk7P/snBQAAAAAAAAAUP//////////AP//////////+BAAAAAAAAAAAAAAAAAAAAAAAAAAAACs//////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAAOP///////////wD////////////8RAAAAAAAAAAAAAAAAAAAAAAAABjc////////////AP/////////////0PAAAAAAAAAAAAAAAAAAAAAAg2P////////////8A///////////////8hBQAAAAAAAAAAAAAAAAMdPT//////////////wD/////////////////+LRwSCAMAAAAHDhoqPT/////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'T' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'U' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////JAAAAAAAAADk/////////+gAAAAAAAAAHP//////////AP////////9MAAAAAAAAAJz/////////nAAAAAAAAABE//////////8A/////////4gAAAAAAAAAHOj//////+ggAAAAAAAAAHz//////////wD/////////0AAAAAAAAAAAIJzs/+ykIAAAAAAAAAAA0P//////////AP//////////QAAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A///////////IBAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAAAAJj/////////////AP////////////+UBAAAAAAAAAAAAAAAAAAAAASU//////////////8A///////////////IPAAAAAAAAAAAAAAAAAAwyP///////////////wD/////////////////0IxYOCAIAAAEIEiAyP//////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'V' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////zAAAAAAAAAAYP//////////////ZAAAAAAAAAAw////////AP//////kAAAAAAAAAAU/P////////////8UAAAAAAAAAJD///////8A///////oBAAAAAAAAADE////////////xAAAAAAAAAAE7P///////wD///////9MAAAAAAAAAHD///////////94AAAAAAAAAEz/////////AP///////6gAAAAAAAAAJP///////////yQAAAAAAAAArP////////8A////////+BAAAAAAAAAA1P/////////YAAAAAAAAABT4/////////wD/////////aAAAAAAAAACE/////////4QAAAAAAAAAbP//////////AP/////////EAAAAAAAAADT/////////OAAAAAAAAADM//////////8A//////////8kAAAAAAAAAOT//////+QAAAAAAAAAKP///////////wD//////////4QAAAAAAAAAmP//////nAAAAAAAAACI////////////AP//////////5AAAAAAAAABE//////9EAAAAAAAABOT///////////8A////////////QAAAAAAAAAT0////9AgAAAAAAABI/////////////wD///////////+gAAAAAAAAAKT///+kAAAAAAAAAKj/////////////AP////////////QIAAAAAAAAXP///1wAAAAAAAAM+P////////////8A/////////////1wAAAAAAAAM+P/8DAAAAAAAAGT//////////////wD/////////////vAAAAAAAAAC8/7wAAAAAAAAAxP//////////////AP//////////////HAAAAAAAAGj/aAAAAAAAACT///////////////8A//////////////94AAAAAAAAHP8cAAAAAAAAhP///////////////wD//////////////9gAAAAAAAAAkAAAAAAAAADk////////////////AP///////////////zgAAAAAAAAQAAAAAAAAQP////////////////8A////////////////lAAAAAAAAAAAAAAAAACg/////////////////wD////////////////sCAAAAAAAAAAAAAAADPT/////////////////AP////////////////9QAAAAAAAAAAAAAABg//////////////////8A/////////////////7AAAAAAAAAAAAAAAMD//////////////////wD//////////////////BQAAAAAAAAAAAAc////////////////////AP//////////////////cAAAAAAAAAAAAHz///////////////////8A///////////////////MAAAAAAAAAAAA3P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'W' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//8cAAAAAAAAALz/////4AAAAAAAAAAA6P////+8AAAAAAAAABz//wD//1QAAAAAAAAAjP////+gAAAAAAAAAACo/////4wAAAAAAAAAUP//AP//jAAAAAAAAABU/////2AAAAAAAAAAAGj/////VAAAAAAAAACM//8A///EAAAAAAAAACT/////IAAAAAAAAAAAKP////8kAAAAAAAAAMT//wD///gEAAAAAAAAAPD//+AAAAAAAAAAAAAA6P//8AAAAAAAAAAE9P//AP///zAAAAAAAAAAvP//oAAAAAAAAAAAAACo//+8AAAAAAAAADD///8A////bAAAAAAAAACM//9gAAAAAAAAAAAAAGT//4wAAAAAAAAAaP///wD///+kAAAAAAAAAFT//yAAAAAAAAAAAAAAIP//VAAAAAAAAACc////AP///9gAAAAAAAAAJP/gAAAAAAAAAAAAAAAA4P8kAAAAAAAAANT///8A/////xAAAAAAAAAA8KAAAAAAAAAAAAAAAACg8AAAAAAAAAAQ/////wD/////TAAAAAAAAAC8YAAAAAAAAAAAAAAAAGC8AAAAAAAAAET/////AP////+AAAAAAAAAAIwgAAAAAAAAAAAAAAAAIIwAAAAAAAAAfP////8A/////7gAAAAAAAAANAAAAAAAACwwAAAAAAAANAAAAAAAAACw/////wD/////8AAAAAAAAAAAAAAAAAAAdHgAAAAAAAAAAAAAAAAAAOz/////AP//////KAAAAAAAAAAAAAAAAAC4vAAAAAAAAAAAAAAAAAAg//////8A//////9gAAAAAAAAAAAAAAAACPj4CAAAAAAAAAAAAAAAAFj//////wD//////5QAAAAAAAAAAAAAAABE//9IAAAAAAAAAAAAAAAAkP//////AP//////0AAAAAAAAAAAAAAAAIj//4wAAAAAAAAAAAAAAADI//////8A///////8DAAAAAAAAAAAAAAAzP//1AAAAAAAAAAAAAAABPj//////wD///////88AAAAAAAAAAAAABT/////GAAAAAAAAAAAAAA0////////AP///////3QAAAAAAAAAAAAAWP////9gAAAAAAAAAAAAAHD///////8A////////sAAAAAAAAAAAAACg/////6QAAAAAAAAAAAAApP///////wD////////kAAAAAAAAAAAAAOT/////6AAAAAAAAAAAAADc////////AP////////8cAAAAAAAAAAAo////////MAAAAAAAAAAAEP////////8A/////////1QAAAAAAAAAAHD///////94AAAAAAAAAABM/////////wD/////////jAAAAAAAAAAAtP///////7wAAAAAAAAAAID/////////AP/////////EAAAAAAAAAAT0////////+AgAAAAAAAAAuP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'X' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////9UAAAAAAAAAKz///////////+sAAAAAAAAAFD/////////AP///////+QQAAAAAAAAFOT/////////8BwAAAAAAAAM5P////////8A/////////5gAAAAAAAAATP////////9kAAAAAAAAAJD//////////wD//////////0AAAAAAAAAAoP//////wAAAAAAAAAA0/P//////////AP//////////2AgAAAAAAAAQ4P////gkAAAAAAAABMz///////////8A////////////iAAAAAAAAABA////dAAAAAAAAABw/////////////wD////////////8MAAAAAAAAACU/9AEAAAAAAAAHPD/////////////AP/////////////IBAAAAAAAAAzYMAAAAAAAAACs//////////////8A//////////////90AAAAAAAAABAAAAAAAAAATP///////////////wD///////////////QgAAAAAAAAAAAAAAAAAAzg////////////////AP///////////////7wAAAAAAAAAAAAAAAAAjP////////////////8A/////////////////2AAAAAAAAAAAAAAADD8/////////////////wD/////////////////7BQAAAAAAAAAAAAEyP//////////////////AP/////////////////gDAAAAAAAAAAAAAjY//////////////////8A/////////////////0AAAAAAAAAAAAAAADj8/////////////////wD///////////////+UAAAAAAAAAAAAAAAAAJD/////////////////AP//////////////4AwAAAAAAAAAAAAAAAAADOD///////////////8A//////////////9AAAAAAAAAAAAAAAAAAAAAQP///////////////wD/////////////nAAAAAAAAAAAWAAAAAAAAAAAlP//////////////AP///////////+QQAAAAAAAAAGD/YAAAAAAAAAAM4P////////////8A////////////TAAAAAAAAAAs9P/0LAAAAAAAAABM/////////////wD//////////6AAAAAAAAAADNT////UDAAAAAAAAACg////////////AP/////////kEAAAAAAAAACg//////+gAAAAAAAAABDk//////////8A/////////0wAAAAAAAAAYP////////9gAAAAAAAAAEz//////////wD///////+oAAAAAAAAACz0//////////QsAAAAAAAAAKT/////////AP//////7BQAAAAAAAAM1P///////////9QMAAAAAAAAFOz///////8A//////9UAAAAAAAAAKD//////////////6AAAAAAAAAAVP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'Y' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////1QAAAAAAAAAAGj//////////2gAAAAAAAAAAFT///////8A////////5BAAAAAAAAAAAMT////////EAAAAAAAAAAAQ5P///////wD/////////mAAAAAAAAAAAKPj/////+CgAAAAAAAAAAJj/////////AP//////////PAAAAAAAAAAAgP////+AAAAAAAAAAAA8//////////8A///////////YCAAAAAAAAAAE2P//2AQAAAAAAAAACNj//////////wD///////////+AAAAAAAAAAAA4//84AAAAAAAAAACA////////////AP////////////woAAAAAAAAAACUlAAAAAAAAAAAKPz///////////8A/////////////8gAAAAAAAAAABAQAAAAAAAAAADI/////////////wD//////////////2wAAAAAAAAAAAAAAAAAAAAAbP//////////////AP//////////////8BwAAAAAAAAAAAAAAAAAABzw//////////////8A////////////////tAAAAAAAAAAAAAAAAAAAtP///////////////wD/////////////////VAAAAAAAAAAAAAAAAFT/////////////////AP/////////////////oEAAAAAAAAAAAAAAQ6P////////////////8A//////////////////+cAAAAAAAAAAAAAJz//////////////////wD///////////////////9AAAAAAAAAAABA////////////////////AP///////////////////9gAAAAAAAAAANj///////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+			'Z' => array('data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAQ//////////////8A/////////////////////////1AAAAAAAAAABLz//////////////wD///////////////////////98AAAAAAAAAACY////////////////AP//////////////////////pAAAAAAAAAAAaP////////////////8A/////////////////////8QIAAAAAAAAAET8/////////////////wD////////////////////gGAAAAAAAAAAo9P//////////////////AP//////////////////9CwAAAAAAAAAFNz///////////////////8A//////////////////xMAAAAAAAAAATA/////////////////////wD/////////////////eAAAAAAAAAAAnP//////////////////////AP///////////////5wAAAAAAAAAAHT///////////////////////8A///////////////ABAAAAAAAAABM/P///////////////////////wD/////////////3BQAAAAAAAAALPT/////////////////////////AP////////////QoAAAAAAAAABjg//////////////////////////8A///////////8SAAAAAAAAAAExP///////////////////////////wD//////////2wAAAAAAAAAAKD/////////////////////////////AP////////+YAAAAAAAAAAB8//////////////////////////////8A/////////wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=','width' => 40), 
+		);
+	
+		return $_png;
+	}
+	
+	// These define base64_encoded raw png image data used
+	// when we cannot generate our own single png image
+	function define_raw_pngs()
+	{
+		$_png = array(
+			'0' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QKCNGXKO6AAAAB3RJTUUH0wUOEDQ6EUG1VwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXNJREFUeNpj/M9AHGAiUt2wVvhyaqAqKyOjpG3jQwaGv+e+IUn9RwJfSjjg4iwFP1aKJD6HyyErfGGAYrquIoP5E2wK/zigu0v5wH9sChdgeKDqP1aFGhBZmxv/z0Dd4IxV4RWIpMQHIPuJAITzAqEQETx7IFQIP5CQNoJwDmALxzMQCuyjg1chnBPYwtECwr8AZN41h0p6YHOjAkTuwf//77wYuCEcFWwKOWA2fM1iZuuHcASwKYQ55c9ENuasrxgRjKlwJS+D17v/hBUeUGYwv/sfn0IRiJQZJIbxuFEFagjvSlDUQNgK2GIGqpC1JRhIfoAqxBYz0DRhn8IMJO+giKEqhMaMJBeI3AHhIKdkRPqG8DlAifqFADyasKRHO6h1Z/6fMYEwTbCmx3cWGCl8CTaFwBhGz+M2/7EpXMvOnBmIok7jBVaFz/Mi3/1pQORrhpgPyOr+M8IL0j9/gKpeLjhy5QEwoDVsYuRR3cE4IktcAJNx8cJaZBeQAAAAAElFTkSuQmCC', 
+			'1' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMi//xxVKAAAAB3RJTUUH0wUOEDYLcqnX7wAAAAlwSFlzAAALEgAACxIB0t1+/AAAAHpJREFUeNpj/M9AHGAiUh1WhR8FGUGAsMKaD9iM/I8BlmCVwVS4hoUohT8qcNiFyv2zQIWBCIV3amRwu54RKcDRAgQ1KigIcJYK7CqR3QsCFmf+Y8qgeQakbANMAz6FKjUXECbj8zWa76nm61GFw1UhI10KqVGFNFQIADdK9Zj7PsV9AAAAAElFTkSuQmCC', 
+			'2' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMwPUBEjoAAAAB3RJTUUH0wUOEDUqFe2UcgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAQxJREFUeNpj/M9AHGAiUt2owkGrkAWF93LFgStPfjCwyGiYRGijqfyPAH9aOJAkQl78RwbICkNQjdB4gUNhD7qzLLAr/CKA4YENSAoRvl7zAUJXvPmxhgfCXILVMxEQvg+IDVUhgtVqDYjkDhD7B2aQIMIx5cOTN29evLAAsaEKObBajQzmQOQMcIQjHLwQgSisIaDwBdS5LHfwK7yhAHVVyX+8CrdAA5HB5gdehQ3Yoxpd4ZcAmDqbD//xKISEIjhU//zHoxDmXQaeFRhOZ8CmzuDOf3wKf8DsDfnyH6/CHJi6P//xKjyDJethVehBpMI7DPgVwrPCCgb8AK5wDwGFcNMF8EkCASOx1QcAGUxu1untnFIAAAAASUVORK5CYII=', 
+			'3' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMxBQugk2AAAAB3RJTUUH0wUOEDU3duv4qwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATdJREFUeNpj/M9AHGAiUt0IVciCzPm7ZceZB28YGBQkLHwcmNFU/keANRJI4ioH/qMAJIUlaHatwaFwBrqrOO5gVfiCB8P9KVgVVkAtnPDh/wkLCFsGq0IFiGQLiH0D06P/GWHJ7O+NOzfuXLlzQRrEhgSawHscwYPurxAcwQMBf/4/aIAYyHIGr8IEeDhO+Y9XoQNUncwOVHGMRPEDSovc+IkzrpGDCQgUbuC1WgBhhsIHfAp3vPn/oIIFKfRxKQSDGohCA4IKX0DTD7YoRAWMUJ9iyQpbn4DBBWUQ5yFEDDnFw622gXAzwBxoYvfB5sYlUI0lD/4/gWWKJdgU/tHAcKjCD6y+PsGCpo4FJbaRgmcNqkqWCThTzxkTJHXo+Ro1HA9uOPHiATDlKJj4eKCVFIzDqWgGAK7GW/haPS+zAAAAAElFTkSuQmCC', 
+			'4' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMyqWttCEAAAAB3RJTUUH0wUOEDUxn4hdngAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKBJREFUeNpj/M9AHGAiUh2FCucyQgCK4H9McIAFixwWhQ8kGIhS+MWAgTiFIQzEKWxhIE7hFgbiFF7hASkQIajwjQpInuUAIYV/XMDyU/4TUlgAlk75T0jhArCszR9CCk+AY07mxX8CCp+AY47nzH8CCn+YgOWW/CekMAYsVfMfl0JGmCBq4kNEDp2zAn0UMmItABjRvDykPTO43DgyFQIANP6pTFLWAdoAAAAASUVORK5CYII=', 
+			'5' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QMzPy3XhEAAAAB3RJTUUH0wUOEDUk8lW5dQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAQpJREFUeNpj/M9AHGAiUt2oQuIVfmREBzgU3iHWxAfEKiTaRFpZnfAfAbAr/AsxUYagiVCbeQgqhPpFYmukLCOrZupRNJUIB02BCAjAZCK+/Ed2LoJZgm6bzRfsCgMw3JWAXaEBpg8uIGSRPPMBQmXc+P+iggXCnoOQZUQK1K8PgEAjGcQs7QGL6FzG5mtkcAUiyYIQYcRRUkDTLEIWR1b4ixamQMPhrKUP3rx48eDNFXmwdyFiOthixgXqaTAnBcKpwRaOS6A6Mx78fwBVx/IAm8I/KsTGzAkWNHUyb7Ar/L8GNSlK3MCRev7/v+CApC7kBUoUoAX4yQ0nHjwAWqpiE6GNFgNDoAwHAKC2Q2lMNcCmAAAAAElFTkSuQmCC', 
+			'6' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNAObRd4vAAAAB3RJTUUH0wUOEDUc2lcB6wAAAAlwSFlzAAALEgAACxIB0t1+/AAAATtJREFUeNpj/M9AHGAiUh2Gwq2puryMjKKmmSfRVf5HBkcMEBI+L1CkUBROYUE2QuMFLoVr0CzzwKHwhQC6szZgV1gAtfHI/xs2mEYywsPxp8QHEMVxQ56B4aaJiIKIiIRCPDZf74DwI/5jB4hwPAChbAgG+BWoExlOxkoysuqW3sUV4BoQ/p0SqARLB44AF4HIByDMKMCuEIu7phCrUOADNl/DgMOJ/09SIMwPC7B5hgfC1/kB4kRAOC7YrFaByM0Ac85AOCLYrFaBhSMIQNPlG2wBDg3HP2CSGU/MuEAoiKVXUWxB9cwPiG8UwEGSg5FCMNOjwZ4/byqgpqwgMoWr/MGeZ1agqWPZgSNz/Z+AqnDCf1wK/29B8qbKDhQpRtTE8HfLjjMP3jDwKJh4hKCGJSPNC6lRhTRWCABWpdoxd/bZ4QAAAABJRU5ErkJggg==', 
+			'7' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNA18/fMoAAAAB3RJTUUH0wUOEDUVo4u5TwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAM9JREFUeNpj/M9AHGAiUt2oQnorZIGzGLFJIyJ40HqGhUiFPFuQ/YUFPBGBmLcDSQybwj8OEDOW/CegsAeiruQ/AYV3OMDqTP4QUugCceCN/wQUQn1a8Z+Awj8qYHUiHwgpXAAxcMJ/Qgp1wOoEPhBSuANiYM5/QgpjIAovEFL4gweszgAz0NASxZ4vYMqHYDKDBiIWhWhWa0CS1x9CVn+8AaYsmAlZfQRC6RDMChADGTQIKjxDrMI7EEoBi0JGlMJe8AOY+sFOSCEeQHQBAABCZ7xyT9fJhwAAAABJRU5ErkJggg==', 
+			'8' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNBeBnwpSAAAAB3RJTUUH0wUOEDUOKe5wowAAAAlwSFlzAAALEgAACxIB0t1+/AAAATVJREFUeNpj/M9AHGAiUt1AKmRB459cc+DBGwYWGQ2LEG1Umf/I4IELkozLA2QpFIUXJFDMEDiBQ+EHGTR3yHzArrAFwwct2BXqQGQ1zvw/owFh6mBXCDXmDJB5BsOrjEhxzfoHIgkiGCGB9xtrgEPtOwvEV6FWY4+ZAAgVc5LhZgKEGYI9wN+gBiPu4Pl/BFWlxA1cMfN/C0rUr8AVhX8K0KyuwaEwASNmarAqPACVTXnw/0oENBFewKYQGhYZYE4MVBM2hVAvQ1LhHQhHBVsUMjIgYhCdhy3PPASTd6GOxBYz0KhOQHajDjY3pkC1Rlz5fweqjqEAm8ILGK5gYLlDZICXYI+ZLzZo6gL+4EgUfyo4kJQJtCCpQ8kKQPB2zZ47L14AU5iMgUMAN7IM43AqHwdQIQAhMPz6Gz5V/wAAAABJRU5ErkJggg==', 
+			'9' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QNCQ+T2tEAAAAB3RJTUUH0wUOEDUHUDLIBwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAUZJREFUeNpj/M9AHGAiUh26wr9rE3V5GRlFTTM3/kVT+R8Z7FBBSKjsQJFCUTiFBcWMCbgUHmBBs20FdoV/VNDUMQi8wapwDVS65s2fPToQZgFWhRFIkm8kwGyeH9gUQm2+Aua0QDhb4LJI4XgHQmmDSRMIZw+emIEENAeEcwObQhEIdQHiABRbUGPGBSIQAWL/gHqbB5tnJkC1Fjz5f8IGwxwkhR8EsCQarFE4hViF/wsQCgKgHsSu8H8HLFkUQL2rgUPh/zslOiwMEjFH/kND2geXQvQgqMAWhSjgAIRygAswIuXCpXfevHjz4M0ZdQaGhxo/wAnyBTuWmPnvARGxuPH/iAa+9Ph/A7r9Ai+wK/zvg6ZwzX8cCl9oICtjmfIfl8L/bwIQ6gyO/Met8P//EwUmwHTJo5OyBU2CkdaF1KhCWisEAM/sJxmZkdWnAAAAAElFTkSuQmCC', 
+			'A' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QFwy1U7TfAAAAB3RJTUUH0wUOEC0ZKCZtPQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAO1JREFUeNrt1LERwiAUBuAHZ2GRwsIypQMwQEZwgBQpM4QDZBSLFI7gCA5gQWGRdA5gkTuMSh48eMTUnq96wH98B+QiDCwruTD3D76qF676ueAp0Y9lSBXeSkFWaLAje3T+kkzK4SgpBzZw8pqxJWcdOJuRsyGPbWDk0tS20zw9SXsobdfytJVXdzNsP61i6Zt3K7Ht0UeUgbPdjsrOXMd+2IS2C2qb271HVWi7YANcNXFQsUEVBTXwNdl46jYRxPl52dnwRUZbhkLSDmS8DnxFRWiULxg8UxvobefuRR8ZQYDKtffVVcQWv/RrfgJC4bd0upw4MQAAAABJRU5ErkJggg==', 
+			'B' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGAusrz2zAAAAB3RJTUUH0wUOEC01Gv4B3gAAAAlwSFlzAAALEgAACxIB0t1+/AAAANJJREFUeNpj/M9AHGAiUh0tFTKiAUHL2rsoKv9DARZDWFr+IwA+hQwMFcQqZDhCrMIIYhWK4FYIYv8444PuV+wK//9/A+UJwBUSCHAL3OEIsdoFyttCpGdiiAtHjoY/RCnk6PlBbBRKrCE6CqcQq5DlDs5whIT3CgUI788EvOEIBCegXB2YPCNMBSNMISqf5TeUjysK90LpP/itfrFEAhZCMHkWdKMYUbk2MAah7BqD02pUYEFkgMu8IE6hD0IdpmegwSejoKLjoY7syaFU7A0HhQA2e4cJytImvAAAAABJRU5ErkJggg==', 
+			'C' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGBbPqVFqAAAAB3RJTUUH0wUOEC4BEGemqAAAAAlwSFlzAAALEgAACxIB0t1+/AAAASlJREFUeNpj/M9AHGAiUt2owkGrkAWV+3TDgRtPPjBwyGiYBOijSv1HAlcCkGUcTiDLISvsQDOeZQp2hQWYDpuCTeEEbD44ganwDgc2vxpgKoyAyUWc+f9hjgCMtwFd4RuYRxog/ueBcl3QFc6BSmj8gfBrwE40yFmCrjABqrAH5mSZgJ4jX7AEjwlU4Zn/OAAsrp9AaRlccc0IzdeMsBilOPWQrBDmtpfEKnwBpZ8qZq58i6IS6vscKHcBcgQYlOz4gh6OK6AKfaB8G5hN6Aq/wBLPHjB3CczCFIzUA0u2PD0v/j9pgaf1ExgK3wgwYAEOWFL4GizqWC5gyzM1mArnEJkLZ2DPhf//n3BAVmeDkq8ZUZPL3TUn7gBLCgYFBYsAcxQZRmKrDwABNsv9SJSDwwAAAABJRU5ErkJggg==', 
+			'D' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGC1+orhOAAAAB3RJTUUH0wUOEC4yr7fHvgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAM9JREFUeNpj/M9AHGAiUt1AKmSBsxiRhXlkNBxCpFFU/ocBTDMyPvxHADwKGRgUbhCpkEHiCZEKGRyIVciwArdCIPPFGg8YzwSvQiBogXFvEFD43wDKnQDl44yZGCh9glAU2sCsJqRQBkq/gMUw3G2wuP6PnU/H9PgRSgsQUvgESosQUngFSqsQUrgCSsNiCFcU7oBx9+CL6w8XamB5SeUPkelxAZEJ1+YPcQolXhCXFTTuEJULOUq+IOVrFgasQELBxMaHG1mEcTiVjwOoEADAIkCnGpmJKgAAAABJRU5ErkJggg==', 
+			'E' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGDeDwEE0AAAAB3RJTUUH0wUOEC8CkHXGUwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD5JREFUeNpj/M9AHGAiUt2owkGrkAXGYMQqjUgJQ8EzpPsa05+D140oMYTk4KEQ4MMqZqgUhcM1czESW30AABfqB1XDnLzcAAAAAElFTkSuQmCC', 
+			'F' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGQe8AkDZAAAAB3RJTUUH0wUOEC8JB6cf2wAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUeNpj/M9AHGAiUt3wUsiCYDJikUYE3lDwDDm+xvTp4HUjIoaQXTsUAnxYxcyoQryAcUSWuAAW/gZTg/yEMAAAAABJRU5ErkJggg==', 
+			'G' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGRFI1vWIAAAAB3RJTUUH0wUOEC8QY8y3GwAAAAlwSFlzAAALEgAACxIB0t1+/AAAASZJREFUeNpj/M9AHGAiUt0IVciCwvt7ZM+FOy8+MDBwSEho2AQII8v9R4A/U2RQtHEUfEBIIim8YYBhn8oNLAqP8GBxmcwbDIU3sKljYIhAV/jHgAE7uICmcAJMQqDmwp//D2YowPgxqAr/wPyr8QAi8EEHwleIQFW4BxYicG+eEHEomHECET5QhRVQhQn/cQFoFJ6AKgwgFNcPoFwdnAoZIXmGERahKDwkIdqlR1j4PiRW4RVCCmExvQenQrSYEXiDiAoUBfC4loAK23yBSnzArhCRehRmAJPFnRUxHDgU/lDA7zZECj/Cgl2dAkaeWYNVZcoHDIX/94hgKLM4gS27/v9QIICizGMDkiQjSon7c8eBCw+e/GFgkZEwsHCRRpZiHE5FMwCa2YE+WcAOUwAAAABJRU5ErkJggg==', 
+			'H' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGRw2Z4k1AAAAB3RJTUUH0wUOEC8agxleBQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD1JREFUeNpj/M9AHGAiUt2oQvyABUozQml4+KMLDAXPDAWFLGh8RlwKh4JnaB88GOlxELhxVCFewDgEynAAN2sFVHAvevkAAAAASUVORK5CYII=', 
+			'I' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGSlg1E0WAAAAB3RJTUUH0wUOEC86uHd+zQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAD5JREFUeNpj/M9AHGAiUt1AKmRBMBkxJJE9OhQ8Q32FjGhxDQsjjCQwFDwzqnCwKkRKZqO5EBMwDqcSl2iFAMMeB0s/kLo2AAAAAElFTkSuQmCC', 
+			'J' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QGywiiNsbAAAAB3RJTUUH0wUOEDAFw0tdbgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKdJREFUeNpj/M9AHGAiUh3xClmwijJCaSR3Ud/qUYWjCklTyIHEhifctw8ePHgCxO+B7L9QMQlsChW+QOiX4gwMd6BiItisVoHSB6AYWQwM/kNBBszkC/9PwKyc8B8B4Ar3YPHMHWwK/xtgqAv4j1XhEfScK/EEu8L/a1BVStz4j0Ph/yPItoe8QFH3nxGlkNq75cKDB0DDVBwitNEcwjhwpdmoQrwAAN6ioiFapgUdAAAAAElFTkSuQmCC', 
+			'K' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHAEoFhGpAAAAB3RJTUUH0wUOEDANzZDVXAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPZJREFUeNpj/M9AHGAiUt2owgFSyAgFMOGDrDARxkKo0H8wYEDh/b/AAzepACqEVeEdCQx1WBW+0ICry/mPR+EXE7i6kD94FP5xwaYOi8IIrOowFRbA1Xkgq8NQ2ANXZ/PlPx6FS3CpQ1fIAmOIoKn7jxbXf2CMNxvQIxvVRAQQ+YDXaiSQQqxChiOEFGoIQGidP/gVStxogLI68CqUuPH/BzSVcTzAoxCo7v//ObBIxK0QrO7/H1iCXIFT4QkIFxbaMh9wKYQJO0D5OYQUnoDF/QkCCuHJ1+APAYV3YOloAgGF8JTO84SAwjfQiGQIgPAZqV4rAACnKSarzdlc4gAAAABJRU5ErkJggg==', 
+			'L' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHA64qQw4AAAAB3RJTUUH0wUOEDAXMPIsJgAAAAlwSFlzAAALEgAACxIB0t1+/AAAADlJREFUeNpj/M9AHGAiUt2QUMiCYDJCaezhMBQ8M6pwVCEdFLJgCjEisRH5Zyh4hvoKGUdkQUq0QgARaARRV9jUFQAAAABJRU5ErkJggg==', 
+			'M' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHBhMfblpAAAAB3RJTUUH0wUOEDAqaJpgNwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAPNJREFUeNrdlK0OgzAUhS8bCQYxMYmcmEAgEAgejQfZQyG2pAIxOYlATkAu691o2tvSYia2iv7lyzn3NG0jhG1tt5H7Aggom7ZuaKPhBFqKV+pFWDGjjcxStEAYXuvBkrKtoVX+gdRiK9i6sxjgeVGUMJzWwZLACaZOTqoAOAronmrlBuvPkQsIgHn8BqnE2AMmhaaYJ57jqTRFMwsDyW249XaJLhAujizm7UFM5XCUXTqiTvBLQYWRc7H3WWt+3NmlyGbOGh9q/45mjQxUb+CA6A2jSqu5MweX0ooQWLJxLYx6fz0GwmBOsww5GP3At/dX4Ayb7qpFI9y5ygAAAABJRU5ErkJggg==', 
+			'N' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHC6DxyzwAAAAB3RJTUUH0wUOEDAye/b4YQAAAAlwSFlzAAALEgAACxIB0t1+/AAAALRJREFUeNpj/M9AHGAiUt0IV8gIARsRMlAROP8/BEB5Ii/+/0cVgXNRhRk8iFXIMIFYhRxXiFTIYPCDSIUMBcQqZNhDrEKZN0QqZAggViHDHIIKRSAUzx1CCrdAaZM/BBT+z4Eyaggp/KEDYbAcIaDw/wUWCEuBkML/PagBgFvhfxdiFT4RIVLh/zXEKvyfQqzCLypEKvx/hoVIhf9biFX4x4ZIhf8fCBCp8P8KNBHG4VQ0AwDEOyeZhO5p1AAAAABJRU5ErkJggg==', 
+			'O' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHQExDSDoAAAAB3RJTUUH0wUOEDA4myMRfwAAAAlwSFlzAAALEgAACxIB0t1+/AAAATtJREFUeNpj/M9AHGAiUt3wUsiCyv265ciZJ08YGGRkDGwCuFGk/iOBDwU8SDIcGS+Q5JAV7hBBs45nAVaFC1gwXTYBi8IdWNQxMCzAUPhBBJs6Bp4n6AoLYFI6az78f7NEB8ZNQFP4QwAqEfADwg+A+f0NqsI1UHGBDzCnSKC6EhYzB6B0Cj+UwZ+CKgNTeAZKu8C94QGlL6DGjAyU+wAeXC+gIiIQLiM0KzDC9CFCBlWICsnsL3aFMDc+hcs8QZWBKYSF2g24whvYFZpA6T1whUegNCwyoYGxAmYyLGZ+wOxYghqFX2BpO+APmP8nBspHj2uk1LPizf8PGyxgXPTUQ3x6JDqF//8/AYs6bHkGmCYF0O3FnguBCSaFA0kZS8IDJDlG1IIUVFK8eABMWzI6DgHCyDKMI7LEBQCD5YgI9wbKGgAAAABJRU5ErkJggg==', 
+			'P' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHQvR2Mn2AAAAB3RJTUUH0wUOEDEDMzPJGgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKVJREFUeNpj/M9AHGAiUh05ChlRAKdu4k5Ulf9hANMQiwf/EQCfQgaJB0QqZHAhViHDEbg0AV8vwRM8QN0v5vBAOSfw+BrMWQDl8MClGeEKGGEKQcRXHmQemTGD1RMy+N14o4MDyvGAS7NgGMaIzPHAYyIy4HhBZMy0EBmFIX+IUsjRgqQOi2fAgEVBwyVGGEUEQw2O3EbLzDWSFDIOhtJsVCEWAAC/Yt2X+2PYcgAAAABJRU5ErkJggg==', 
+			'Q' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHRxSC0wxAAAAB3RJTUUH0wUOEDEKSu9xvgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAW1JREFUeNpj/M9AHGAiUt2QUMiCzPm65cCZF08YGGRkDBx8uNFU/oeDDwU8SOIcBS/+IwOEwh0iaEYIrMCqcA4LprsmYFG4A4s6BoYFGAo/iGBTx8DzAl1hAUxKZ8WH/29W6MC4KWgKfwhAJXx+gPl/QmB+/4KqcANUXOQDVPiLBFRkCUwhJGb2wGzihzK4U6CMA6hReAbKc4F7wwFKX0CNGRkoB+HJJ1ARGZgAIziFM8J0IUIGXYjMZPaXkEJYYDyBiz+EuRFVoQKUdwWIz6qWvmRguAMVkUBVaIIUalPu9GgshIefAWrwrIHp//L/DQc4KjFiBi2uQ/7832KB5AX0uP5fAZOx2PDhfwNCIXrq+f9BhgEb4HmCkcL3YE3hSHkBnmfWYFMpsoaYXAgGDgcwFKLlaxYOCG2DqRCYrldkmIACUMIgZsaTI5Cg3IBNISp4AoovlT+EFf7/kYPkb3wK//8/YAGPGcYhUIYDAHBC9Yak1w7iAAAAAElFTkSuQmCC', 
+			'R' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHSkEuIgSAAAAB3RJTUUH0wUOEDEUsOBM3QAAAAlwSFlzAAALEgAACxIB0t1+/AAAAOZJREFUeNpj/M9AHGAiUh0NFLJAaUY0YRkJHYcQdmSh/xCAzRCZHf8RAJ9CBpYNRCpkEHgBV4jfMx+mEOVGIDDAaTWY82aPBZTLgV8hUCkaH6cbP8B8gxHgyODjgwstMDfiVIgWQyFE+lrhB3EBznOFuJgxuUFMXPPEbPmDpA53FH55osKMIoAe4F826MDMvPMfj9WgWFGBBeIf/Ar/H4FxJhBQ+B8WzCIfCCi8A4uvBgIK/2fA/POCgMIXHFBuDqH02ABLM3cIKPwgAuVHEFD4fwJM4AIBhT9goe4AFWAcAsXesFIIAEvJyZHTCSiTAAAAAElFTkSuQmCC', 
+			'S' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHTRnvuTLAAAAB3RJTUUH0wUOEDEbIF9RTAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAVZJREFUeNpj/M9AHGAiUt2oQvyABYX398CWK3de/GBgkVEw8HFgRpH7jwSWqCDLyCxAlkNS+CcG3boY7AozMB3Wgk3hGSw+4HgBl0b4egIWhT9mYPGMBFQg4MH/D2tgvrKASzPC0yMjlP7CDSTOmrDIMDDwiHBsxzSRBypw5j9WgFDoAPNAxIQjX/ApXIDsC4OCLV9wKfzjwIACOEIO4IiZFxbooePzAqvC/z9qONBUStzAqvD//zc9BqgqNX5gVwgETxbkmCClvSk4FYLdsCMCptAGI2YSGV78+PLmz5MX4mDu1ByIMM9n9JiBxe4caGChy8MZMMsUIEFyAMoVwVC4BGaEwpI3/9/MEYGlJQyFPwQYsIE1mL7GlnCR0iNSXLtgqpO4gy1mvtigq1NAxCBKgP9pEUFWxlOCnNIYUYrmn3v23Ljx5gsw88sYOPhwI0sxDoEyHAABtSc836a1EQAAAABJRU5ErkJggg==', 
+			'T' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHgUdTbcyAAAAB3RJTUUH0wUOEDEgkVS4aAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADdJREFUeNpj/M9AHGAiUt0IVcgCpRlxyMODeSh4hmiFjGipB+Z7jEQ1FDwzqnBU4WBSyDicimYAb/AFTaJpyH8AAAAASUVORK5CYII=', 
+			'U' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHhEHl2NPAAAAB3RJTUUH0wUOEDEon48wWgAAAAlwSFlzAAALEgAACxIB0t1+/AAAAKlJREFUeNpj/M9AHGAiUh3xClmgNCOUhrsEXYD6Vo8qHFVIuUIVKP0USr+E0jLoCjWg9A4ovQVNHJjUIaADZsILMPeFApRfA5X/D1N4AaZRYc6b/2+WwNQxXEBX+N8Bqxcc/mMoPMGCRR3LBUyF/2dgUTjjPxaF/6egm8ky5T9Whf9P2KCoMziBJPefEaWQurjnzIMXL34wsMhoWHiYo2hjHLjSbFQhXgAAKzejCLAOcVMAAAAASUVORK5CYII=', 
+			'V' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHh/gL05IAAAAB3RJTUUH0wUOEDEuduyVbwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAVNJREFUeNpj/M9AHGAiUt2owoFRaMgIAYlIMqlQMUMo/z8ITIByRP78hwMRqNgECBei8AULVPQIXN0RqAjLGwgfYrW4B1R4DdzmLVDaQxjZ6v8roDwVuIkqMK3/ka3+/0MAKn4FKn4D5uof/5GtZmCPgEpsQHNDBDsDitVwt5tA+RZQ/pn/qFYj3PQEzHsC5WnA3QyPmQQU3+5AE0VYDTfDBcxzgQbik/8YVv93gMp9AbK/cEAD8T+m1TBb/oD8veEHhs0IE2GmxADZMRAmz4//WKxGkv3DA2Gm/MeqcA/Ujj1w1hHsCv/LQKQz/megRzyawgqIvAxMRwsuhbCEAEvGT3AphEUwNCU5IEv9R8lcUH9/wAxE5HAEgjccSBI8X3CbKOyBxAnhxm3i/w1IEgdQZFA98/+PCFydDKo6VKsZmGPQ0wgOq/+fgYvfQTORkeq1AgCIAvD7+THsDgAAAABJRU5ErkJggg==', 
+			'W' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QFhZRKnzkAAAAB3RJTUUH0wUOEDIR66frkQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAXNJREFUeNrtlK1ywkAUhZdMZsJMKyIqKhAIBAKBiEBEVCDyCJV9iIo+Do9QGRERgUBEVCAqKhAIREVERURnTvfn3t27xSA6g+kOQ/ZkP/aec5NlBHXZSC7k/sE/AhUwoVkDPQ58/2RUQ2IC6B1XpN7MV8tg62/pUdjSDO7OwR2J0pbekpqZYlMG50bNSGwBDQ4pyV5YtCZ7mqZf1mO2IN2Jynba0XRx49pThjQCbEKWFfVRpIlBzlK4PuLdpxEWlTr4LHvYMEDOaTYS3HCW3DAJt8mmaSXYchZbOfEzkyYGZRbrEbX8qe7GMpLqFeyxV9F4fon1pwcxjxbqJpJTBPBJLoyHYSz1I3xq78aOMssepHZZHFjKhbX9/AZd6e9bsdABeyHTQXiE2PLO6PugCwiP/r1QVLYSlpXwKE1Wno7b7jY+hoWj0aegPyA9+jPrzgqwZJ0j8hhMVtElmDoD19FFPAvamc+sOXBm+KdYEzC63p/9D7Tr72kj/8qjAAAAAElFTkSuQmCC', 
+			'X' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHi/G9n7kAAAAB3RJTUUH0wUOEDIXAsROpAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAT9JREFUeNpj/M9AHGAiUt3IVhjKCAFr4RJroSKBMIH/YPBEAMITeQLh//8gAxHggQlAFf6fAdXnA+WnQPkT/qMp/O8AlVkA5h2A8kz+YCi8wQGREngA5PxQgXBYzvzHUPi/A2qIA5BdAmUX/Mei8I8BVHbK/wssEJbMB2wK/5+ASvPcgGlZ8x+rQriFAmghgKHwiwJKXPA8wKXw/x4UhT3/cSr8n4CkzuAPHoVvRODqWE6gyPxHTT1ffiAUCjCgAhRtDkgSFnisnoJixAScCh/wEBk8DmiucsChcA5MQQSMMQWrQlgiZ0iAByey5QiFPlBZnS//v+hgxjZc4QKYKVeAnCswby3AUAi3eAGKNoEn6Ap94A5EjXUfNIUrEA6EALgzl6AohCUGsAMhAOZMkTfICkMw3I5wZgiEyzicimYAFRFkVwgDfJ0AAAAASUVORK5CYII=', 
+			'Y' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHjkyIsu1AAAAB3RJTUUH0wUOEDIkvRQvsgAAAAlwSFlzAAALEgAACxIB0t1+/AAAANJJREFUeNrt1L0NgzAQBWAcUVB6AAZgBAoKhmAICoZgCAoKxmAECkbwABSUlBRILwF8duwYhFJEihJ37+6T5T9g8K6N20X3FdDDNjKKOeTIqZLtWcKBU73bCx1lPhgQNTWieY1zRLmGCZFQp1xTSSmBDUUgW754BF+GQLxAPUkMxMb0FlzUsqpKLXhxQPRqo+oIerggCvuMC7jhFJounA4gWhO2OIL6Jp/uzglHrh0fTyAaDRucQaTkUpxDQVBYDWZ/hYze6bsv/A8/DNlP/kgvwzuer4kCMGPZDgAAAABJRU5ErkJggg==', 
+			'Z' => 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAAAAACpleexAAAAFXRFWHRDcmVhdGlvbiBUaW1lAAfTBQ4QHwfqWOdfAAAAB3RJTUUH0wUOEDIrLasyIwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAL5JREFUeNrl1C0OwkAQBWCWQIJEVPQIFT0GAlHBMRBIBKIHqahAIDlERY9R0UOs3ORh5qVLunmp5GfUZvczbzKzDqtltV7ofgtueHCp16h33xBGwn0KYqoTO/J868Csaj418e0cPujOkLDfmTsECcfcXOGhoC/NZQMUDBUDd5DwxiAtJGzprpCw48xVQcIhM1d6KOgLc/kIBcORgXtIeGGQOyRs6Oq0g7P92YbkRE7bRZhcwhh+6nLF5f7yx30B8Z7FgxzMWtEAAAAASUVORK5CYII=', 
+		);
+	
+		return $_png;
+	}
+	
+	// This is designed to randomise the pixels of the image data within
+	// certain limits so as to keep it readable. It also varies the image
+	// width a little
+	function randomise($scanline, $width)
+	{
+		$new_line = '';
+		$start = floor($width/2);
+		$end = strlen($scanline) - ceil($width/2);
+	
+		for ($i = $start; $i < $end; $i++)
+		{
+			$pixel = ord($scanline{$i});
+	
+			if ($pixel < 190)
+			{
+				$new_line .= chr(mt_rand(0, 205));
+			}
+			else if ($pixel > 190)
+			{
+				$new_line .= chr(mt_rand(145, 255));
+			}
+			else
+			{
+				$new_line .= $scanline{$i};
+			}
+		}
+	
+		return $new_line;
+	}
 }
--- a/includes/captcha/engine_freecap.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/captcha/engine_freecap.php	Sun Mar 28 23:10:46 2010 -0400
@@ -29,782 +29,782 @@
 
 class captcha_engine_freecap extends captcha_base
 {
-  
-  var $site_tags = array();
-  var $tag_pos = 0;
-  var $rand_func = "mt_rand";
-  var $seed_func = "mt_srand";
-  var $hash_func = "sha1";
-  var $output = "png";
-  var $use_dict = false;
-  var $dict_location = "";
-  var $max_word_length = 7;
-  var $col_type = 1;
-  var $max_attempts = 20;
-  var $font_locations = Array();
-  var $bg_type = 3;
-  var $blur_bg = false;
-  var $bg_images = Array();
-  var $merge_type = 0;
-  var $morph_bg = true;
-  var $im, $im2, $im3;
-  var $font_size = 36;
-  var $debug = false;
-  
-  function __construct($s, $r = false)
-  {
-    parent::__construct($s, $r);
-    
-    // try to avoid the 'free p*rn' method of CAPTCHA circumvention
-    // see en.wikipedia.org/wiki/CAPTCHA for more info
-    // $this->site_tags[0] = "To avoid spam, please do NOT enter the text if";
-    // $this->site_tags[1] = "this site is not puremango.co.uk";
-    // or more simply:
-    // $site_tags[0] = "for use only on puremango.co.uk";
-    // reword or add lines as you please
-    // or if you don't want any text:
-    $this->site_tags = array();
-    
-    // where to write the above:
-    // 0=top
-    // 1=bottom
-    // 2=both
-    $this->tag_pos = 1;
-    
-    // functions to call for random number generation
-    // mt_rand produces 'better' random numbers
-    // but if your server doesn't support it, it's fine to use rand instead
-    $this->rand_func = "mt_rand";
-    $this->seed_func = "mt_srand";
-    
-    // which type of hash to use?
-    // possible values: "sha1", "md5", "crc32"
-    // sha1 supported by PHP4.3.0+
-    // md5 supported by PHP3+
-    // crc32 supported by PHP4.0.1+
-    $this->hash_func = $this->session_fetch('hash_func', 'sha1');
-    // store in session so can validate in form processor
-    
-    // image type:
-    // possible values: "jpg", "png", "gif"
-    // jpg doesn't support transparency (transparent bg option ends up white)
-    // png isn't supported by old browsers (see http://www.libpng.org/pub/png/pngstatus.html)
-    // gif may not be supported by your GD Lib.
-    $this->output = "png";
-    
-    // 0=generate pseudo-random string, true=use dictionary
-    // dictionary is easier to recognise
-    // - both for humans and computers, so use random string if you're paranoid.
-    $this->use_dict = false;
-    // if your server is NOT set up to deny web access to files beginning ".ht"
-    // then you should ensure the dictionary file is kept outside the web directory
-    // eg: if www.foo.com/index.html points to c:\website\www\index.html
-    // then the dictionary should be placed in c:\website\dict.txt
-    // test your server's config by trying to access the dictionary through a web browser
-    // you should NOT be able to view the contents.
-    // can leave this blank if not using dictionary
-    $this->dict_location = ENANO_ROOT . "/includes/captcha/dicts/default.php";
-    
-    // used to calculate image width, and for non-dictionary word generation
-    $this->max_word_length = 7;
-    
-    // text colour
-    // 0=one random colour for all letters
-    // 1=different random colour for each letter
-    $this->col_type = 1;
-    
-    // maximum times a user can refresh the image
-    // on a 6500 word dictionary, I think 15-50 is enough to not annoy users and make BF unfeasble.
-    // further notes re: BF attacks in "avoid brute force attacks" section, below
-    // on the other hand, those attempting OCR will find the ability to request new images
-    // very useful; if they can't crack one, just grab an easier target...
-    // for the ultra-paranoid, setting it to <5 will still work for most users
-    $this->max_attempts = 20;
-    
-    // list of fonts to use
-    // font size should be around 35 pixels wide for each character.
-    // you can use my GD fontmaker script at www.puremango.co.uk to create your own fonts
-    // There are other programs to can create GD fonts, but my script allows a greater
-    // degree of control over exactly how wide each character is, and is therefore
-    // recommended for 'special' uses. For normal use of GD fonts,
-    // the GDFontGenerator @ http://www.philiplb.de is excellent for convering ttf to GD
-    
-    // the fonts included with freeCap *only* include lowercase alphabetic characters
-    // so are not suitable for most other uses
-    // to increase security, you really should add other fonts
-    $this->font_locations = Array(
-        //ENANO_ROOT . "/includes/captcha/fonts/assimila.ttf",
-        //ENANO_ROOT . "/includes/captcha/fonts/elephant.ttf",
-        //ENANO_ROOT . "/includes/captcha/fonts/swash_normal.ttf",
-        //ENANO_ROOT . "/includes/captcha/fonts/.ttf",
-        //ENANO_ROOT . "/includes/captcha/fonts/trekker_regular.ttf"
-        ENANO_ROOT . "/includes/captcha/fonts/FreeMonoBold.ttf",
-        ENANO_ROOT . "/includes/captcha/fonts/FreeSerifBold.ttf",
-        ENANO_ROOT . "/includes/captcha/fonts/LiberationSans-Bold.ttf",
-      );
-    
-    // background:
-    // 0=transparent (if jpg, white)
-    // 1=white bg with grid
-    // 2=white bg with squiggles
-    // 3=morphed image blocks
-    // 'random' background from v1.3 didn't provide any extra security (according to 2 independent experts)
-    // many thanks to http://ocr-research.org.ua and http://sam.zoy.org/pwntcha/ for testing
-    // for jpgs, 'transparent' is white
-    $this->bg_type = 3;
-    // should we blur the background? (looks nicer, makes text easier to read, takes longer)
-    $this->blur_bg = false;
-    
-    // for bg_type 3, which images should we use?
-    // if you add your own, make sure they're fairly 'busy' images (ie a lot of shapes in them)
-    $this->bg_images = Array(
-        ENANO_ROOT . "/includes/captcha/pics/freecap_im1.jpg",
-        ENANO_ROOT . "/includes/captcha/pics/freecap_im2.jpg",
-        ENANO_ROOT . "/includes/captcha/pics/freecap_im3.jpg",
-        ENANO_ROOT . "/includes/captcha/pics/freecap_im4.jpg",
-        ENANO_ROOT . "/includes/captcha/pics/allyourbase.jpg"
-      );
-    
-    // for non-transparent backgrounds only:
-    // if 0, merges CAPTCHA with bg
-    // if 1, write CAPTCHA over bg
-    $this->merge_type = 0;
-    // should we morph the bg? (recommend yes, but takes a little longer to compute)
-    $this->morph_bg = true;
-    
-    // you shouldn't need to edit anything below this, but it's extensively commented if you do want to play
-    // have fun, and email me with ideas, or improvements to the code (very interested in speed improvements)
-    // hope this script saves some spam :-)
-  }
-  
-  //////////////////////////////////////////////////////
-  ////// Functions:
-  //////////////////////////////////////////////////////
-  function make_seed() {
-  // from http://php.net/srand
-      list($usec, $sec) = explode(' ', microtime());
-      return (float) $sec + ((float) $usec * 100000);
-  }
-  
-  function rand_color() {
-    $rf =& $this->rand_func;
-    if($this->bg_type==3)
-    {
-      // needs darker colour..
-      return $rf(10,100);
-    } else {
-      return $rf(60,170);
-    }
-  }
-  
-  function myImageBlur($im)
-  {
-    // w00t. my very own blur function
-    // in GD2, there's a gaussian blur function. bunch of bloody show-offs... :-)
-  
-    $width = imagesx($im);
-    $height = imagesy($im);
-  
-    $temp_im = ImageCreateTrueColor($width,$height);
-    $bg = ImageColorAllocate($temp_im,150,150,150);
-  
-    // preserves transparency if in orig image
-    ImageColorTransparent($temp_im,$bg);
-  
-    // fill bg
-    ImageFill($temp_im,0,0,$bg);
-  
-    // anything higher than 3 makes it totally unreadable
-    // might be useful in a 'real' blur function, though (ie blurring pictures not text)
-    $distance = 1;
-    // use $distance=30 to have multiple copies of the word. not sure if this is useful.
-  
-    // blur by merging with itself at different x/y offsets:
-    ImageCopyMerge($temp_im, $im, 0, 0, 0, $distance, $width, $height-$distance, 70);
-    ImageCopyMerge($im, $temp_im, 0, 0, $distance, 0, $width-$distance, $height, 70);
-    ImageCopyMerge($temp_im, $im, 0, $distance, 0, 0, $width, $height, 70);
-    ImageCopyMerge($im, $temp_im, $distance, 0, 0, 0, $width, $height, 70);
-    // remove temp image
-    ImageDestroy($temp_im);
-  
-    return $im;
-  }
-  
-  function sendImage($pic)
-  {
-    // output image with appropriate headers
-    global $output,$im,$im2,$im3;
-    // ENANO - obfuscation technique disabled
-    // (this is for ethical reasons - ask dan at enanocms.org for information on why)
-    // Basically it outputs an X-Captcha header showing freeCap version, etc. Unnecessary
-    // header(base64_decode("WC1DYXB0Y2hhOiBmcmVlQ2FwIDEuNCAtIHd3dy5wdXJlbWFuZ28uY28udWs="));
-    
-    if ( $this->debug )
-    {
-      $x = imagesx($pic) - 70;
-      $y = imagesy($pic) - 20;
-      
-      $code = $this->get_code();
-      $red = ImageColorAllocateAlpha($pic, 0xAA, 0, 0, 72);
-      ImageString($pic, 5, $x, $y, $code, $red);
-      ImageString($pic, 5, 5, $y, "[debug mode]", $red);
-    }
-    
-    switch($this->output)
-    {
-      // add other cases as desired
-      case "jpg":
-        header("Content-Type: image/jpeg");
-        ImageJPEG($pic);
-        break;
-      case "gif":
-        header("Content-Type: image/gif");
-        ImageGIF($pic);
-        break;
-      case "png":
-      default:
-        header("Content-Type: image/png");
-        ImagePNG($pic);
-        break;
-    }
-  
-    // kill GD images (removes from memory)
-    ImageDestroy($this->im);
-    ImageDestroy($this->im2);
-    ImageDestroy($pic);
-    if(!empty($this->im3))
-    {
-      ImageDestroy($this->im3);
-    }
-    exit();
-  }
-  
-  function make_image()
-  {
-    //////////////////////////////////////////////////////
-    ////// Create Images + initialise a few things
-    //////////////////////////////////////////////////////
-    
-    // seed random number generator
-    // PHP 4.2.0+ doesn't need this, but lower versions will
-    $this->seed_func($this->make_seed());
-    
-    // how faded should the bg be? (100=totally gone, 0=bright as the day)
-    // to test how much protection the bg noise gives, take a screenshot of the freeCap image
-    // and take it into a photo editor. play with contrast and brightness.
-    // If you can remove most of the bg, then it's not a good enough percentage
-    switch($this->bg_type)
-    {
-      case 0:
-        break;
-      case 1:
-      case 2:
-        $bg_fade_pct = 65;
-        break;
-      case 3:
-        $bg_fade_pct = 50;
-        break;
-    }
-    // slightly randomise the bg fade
-    $bg_fade_pct += $this->rand_func(-2,2);
-    
-    // read each font and get font character widths
-    // $font_widths = Array();
-    // for($i=0 ; $i<sizeof($this->font_locations) ; $i++)
-    // {
-    //   $handle = fopen($this->font_locations[$i],"r");
-    //   // read header of GD font, up to char width
-    //   $c_wid = fread($handle,15);
-    //   $font_widths[$i] = ord($c_wid{8})+ord($c_wid{9})+ord($c_wid{10})+ord($c_wid{11});
-    //   fclose($handle);
-    // }
-    
-    // modify image width depending on maximum possible length of word
-    // you shouldn't need to use words > 6 chars in length really.
-    $width = ($this->max_word_length*($this->font_size+10)+75);
-    $height = 90;
-    
-    $this->im = ImageCreateTrueColor($width, $height);
-    $this->im2 = ImageCreateTrueColor($width, $height);
-    
-    ////////////////////////////////////////////////////////
-    // GENERATE IMAGE                                     //
-    ////////////////////////////////////////////////////////
-    
-    $word = $this->get_code();
-    
-    // save hash of word for comparison
-    // using hash so that if there's an insecurity elsewhere (eg on the form processor),
-    // an attacker could only get the hash
-    // also, shared servers usually give all users access to the session files
-    // echo `ls /tmp`; and echo `more /tmp/someone_elses_session_file`; usually work
-    // so even if your site is 100% secure, someone else's site on your server might not be
-    // hence, even if attackers can read the session file, they can't get the freeCap word
-    // (though most hashes are easy to brute force for simple strings)
-    
-    //////////////////////////////////////////////////////
-    ////// Fill BGs and Allocate Colours:
-    //////////////////////////////////////////////////////
-    
-    // set tag colour
-    // have to do this before any distortion
-    // (otherwise colour allocation fails when bg type is 1)
-    $tag_col = ImageColorAllocate($this->im,10,10,10);
-    $site_tag_col2 = ImageColorAllocate($this->im2,0,0,0);
-    
-    // set debug colours (text colours are set later)
-    $debug = ImageColorAllocate($this->im, 255, 0, 0);
-    $debug2 = ImageColorAllocate($this->im2, 255, 0, 0);
-    
-    // set background colour (can change to any colour not in possible $text_col range)
-    // it doesn't matter as it'll be transparent or coloured over.
-    // if you're using bg_type 3, you might want to try to ensure that the color chosen
-    // below doesn't appear too much in any of your background images.
-    $bg = ImageColorAllocate($this->im, 254, 254, 254);
-    $bg2 = ImageColorAllocate($this->im2, 254, 254, 254);
-    
-    // set transparencies
-    ImageColorTransparent($this->im,$bg);
-    // im2 transparent to allow characters to overlap slightly while morphing
-    ImageColorTransparent($this->im2,$bg2);
-    
-    // fill backgrounds
-    ImageFill($this->im,0,0,$bg);
-    ImageFill($this->im2,0,0,$bg2);
-    
-    if($this->bg_type!=0)
-    {
-      // generate noisy background, to be merged with CAPTCHA later
-      // any suggestions on how best to do this much appreciated
-      // sample code would be even better!
-      // I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint)
-      // so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog
-      // ideally, the character obfuscation would be strong enough not to need additional background noise
-      // in any case, I hope at least one of the options given here provide some extra security!
-    
-      $this->im3 = ImageCreateTrueColor($width,$height);
-      $temp_bg = ImageCreateTrueColor($width*1.5,$height*1.5);
-      $bg3 = ImageColorAllocate($this->im3,255,255,255);
-      ImageFill($this->im3,0,0,$bg3);
-      $temp_bg_col = ImageColorAllocate($temp_bg,255,255,255);
-      ImageFill($temp_bg,0,0,$temp_bg_col);
-      
-      // we draw all noise onto temp_bg
-      // then if we're morphing, merge from temp_bg to im3
-      // or if not, just copy a $widthx$height portion of $temp_bg to $this->im3
-      // temp_bg is much larger so that when morphing, the edges retain the noise.
-    
-      if($this->bg_type==1)
-      {
-        // grid bg:
-    
-        // draw grid on x
-        for($i=$this->rand_func(6,20) ; $i<$width*2 ; $i+=$this->rand_func(10,25))
-        {
-          ImageSetThickness($temp_bg,$this->rand_func(2,6));
-          $text_r = $this->rand_func(100,150);
-          $text_g = $this->rand_func(100,150);
-          $text_b = $this->rand_func(100,150);
-          $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
-    
-          ImageLine($temp_bg,$i,0,$i,$height*2,$text_colour3);
-        }
-        // draw grid on y
-        for($i=$this->rand_func(6,20) ; $i<$height*2 ; $i+=$this->rand_func(10,25))
-        {
-          ImageSetThickness($temp_bg,$this->rand_func(2,6));
-          $text_r = $this->rand_func(100,150);
-          $text_g = $this->rand_func(100,150);
-          $text_b = $this->rand_func(100,150);
-          $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
-    
-          ImageLine($temp_bg,0,$i,$width*2, $i ,$text_colour3);
-        }
-      } else if($this->bg_type==2) {
-        // draw squiggles!
-    
-        $bg3 = ImageColorAllocate($this->im3,255,255,255);
-        ImageFill($this->im3,0,0,$bg3);
-        ImageSetThickness($temp_bg,4);
-    
-        for($i=0 ; $i<strlen($word)+1 ; $i++)
-        {
-          $text_r = $this->rand_func(100,150);
-          $text_g = $this->rand_func(100,150);
-          $text_b = $this->rand_func(100,150);
-          $text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
-    
-          $points = Array();
-          // draw random squiggle for each character
-          // the longer the loop, the more complex the squiggle
-          // keep random so OCR can't say "if found shape has 10 points, ignore it"
-          // each squiggle will, however, be a closed shape, so OCR could try to find
-          // line terminations and start from there. (I don't think they're that advanced yet..)
-          for($j=1 ; $j<$this->rand_func(5,10) ; $j++)
-          {
-            $points[] = $this->rand_func(1*(20*($i+1)),1*(50*($i+1)));
-            $points[] = $this->rand_func(30,$height+30);
-          }
-    
-          ImagePolygon($temp_bg,$points,intval(sizeof($points)/2),$text_colour3);
-        }
-    
-      } else if($this->bg_type==3) {
-        // take random chunks of $this->bg_images and paste them onto the background
-    
-        for($i=0 ; $i<sizeof($this->bg_images) ; $i++)
-        {
-          // read each image and its size
-          $temp_im[$i] = ImageCreateFromJPEG($this->bg_images[$i]);
-          $temp_width[$i] = imagesx($temp_im[$i]);
-          $temp_height[$i] = imagesy($temp_im[$i]);
-        }
-        
-        $blocksize = $this->rand_func(20,60);
-        for($i=0 ; $i<$width*2 ; $i+=$blocksize)
-        {
-          // could randomise blocksize here... hardly matters
-          for($j=0 ; $j<$height*2 ; $j+=$blocksize)
-          {
-            $this->image_index = $this->rand_func(0,sizeof($temp_im)-1);
-            $cut_x = $this->rand_func(0,$temp_width[$this->image_index]-$blocksize);
-            $cut_y = $this->rand_func(0,$temp_height[$this->image_index]-$blocksize);
-            ImageCopy($temp_bg, $temp_im[$this->image_index], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize);
-          }
-        }
-        for($i=0 ; $i<sizeof($temp_im) ; $i++)
-        {
-          // remove bgs from memory
-          ImageDestroy($temp_im[$i]);
-        }
-    
-        // for debug:
-        //sendImage($temp_bg);
-      }
-    
-      // for debug:
-      //sendImage($this->im3);
-    
-      if($this->morph_bg)
-      {
-        // morph background
-        // we do this separately to the main text morph because:
-        // a) the main text morph is done char-by-char, this is done across whole image
-        // b) if an attacker could un-morph the bg, it would un-morph the CAPTCHA
-        // hence bg is morphed differently to text
-        // why do we morph it at all? it might make it harder for an attacker to remove the background
-        // morph_chunk 1 looks better but takes longer
-    
-        // this is a different and less perfect morph than the one we do on the CAPTCHA
-        // occasonally you get some dark background showing through around the edges
-        // it doesn't need to be perfect as it's only the bg.
-        $morph_chunk = $this->rand_func(1,5);
-        $morph_y = 0;
-        for($x=0 ; $x<$width ; $x+=$morph_chunk)
-        {
-          $morph_chunk = $this->rand_func(1,5);
-          $morph_y += $this->rand_func(-1,1);
-          ImageCopy($this->im3, $temp_bg, $x, 0, $x+30, 30+$morph_y, $morph_chunk, $height*2);
-        }
-    
-        ImageCopy($temp_bg, $this->im3, 0, 0, 0, 0, $width, $height);
-    
-        $morph_x = 0;
-        for($y=0 ; $y<=$height; $y+=$morph_chunk)
-        {
-          $morph_chunk = $this->rand_func(1,5);
-          $morph_x += $this->rand_func(-1,1);
-          ImageCopy($this->im3, $temp_bg, $morph_x, $y, 0, $y, $width, $morph_chunk);
-    
-        }
-      } else {
-        // just copy temp_bg onto im3
-        ImageCopy($this->im3,$temp_bg,0,0,30,30,$width,$height);
-      }
-    
-      ImageDestroy($temp_bg);
-    
-      if($this->blur_bg)
-      {
-        $this->myImageBlur($this->im3);
-      }
-    }
-    // for debug:
-    //sendImage($this->im3);
-    
-    //////////////////////////////////////////////////////
-    ////// Write Word
-    //////////////////////////////////////////////////////
-    
-    // write word in random starting X position
-    $word_start_x = $this->rand_func(5,32);
-    // y positions jiggled about later
-    $word_start_y = 50;
-    
-    // use last pixelwidth
-    $font_pixelwidth = $this->font_size + 10;
-    
-    if($this->col_type==0)
-    {
-      $text_r = $this->rand_color();
-      $text_g = $this->rand_color();
-      $text_b = $this->rand_color();
-      $text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b);
-    }
-    
-    // write each char in different font
-    for($i=0 ; $i<strlen($word) ; $i++)
-    {
-      if($this->col_type==1)
-      {
-        $text_r = $this->rand_color();
-        $text_g = $this->rand_color();
-        $text_b = $this->rand_color();
-        $text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b);
-      }
-    
-      $j = $this->rand_func(0,sizeof($this->font_locations)-1);
-      // $font = ImageLoadFont($this->font_locations[$j]);
-      // ImageString($this->im2, $font, $word_start_x+($font_widths[$j]*$i), $word_start_y, $word{$i}, $text_colour2);
-      ImageTTFText($this->im2, $this->font_size, 0, $word_start_x+(($font_pixelwidth)*$i), $word_start_y, $text_colour2, $this->font_locations[$j], $word{$i});
-    }
-    
-    // for debug:
-    // $this->sendImage($this->im2);
-    
-    //////////////////////////////////////////////////////
-    ////// Morph Image:
-    //////////////////////////////////////////////////////
-    
-    // calculate how big the text is in pixels
-    // (so we only morph what we need to)
-    $word_pix_size = $word_start_x+(strlen($word)*$font_pixelwidth);
-    
-    // firstly move each character up or down a bit:
-    $y_pos = 0;
-    for($i=$word_start_x ; $i<$word_pix_size ; $i+=$font_pixelwidth)
-    {
-      // move on Y axis
-      // deviates at least 4 pixels between each letter
-      $prev_y = $y_pos;
-      do{
-        $y_pos = $this->rand_func(-5,5);
-      } while($y_pos<$prev_y+2 && $y_pos>$prev_y-2);
-      ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $font_pixelwidth, $height);
-    
-      // for debug:
-      // ImageRectangle($this->im,$i,$y_pos+10,$i+$font_pixelwidth,$y_pos+70,$debug);
-    }
-    
-    // for debug:
-    // $this->sendImage($this->im);
-    
-    ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2);
-    
-    // randomly morph each character individually on x-axis
-    // this is where the main distortion happens
-    // massively improved since v1.2
-    $y_chunk = 1;
-    $morph_factor = 1;
-    $morph_x = 0;
-    for($j=0 ; $j<strlen($word) ; $j++)
-    {
-      $y_pos = 0;
-      for($i=0 ; $i<=$height; $i+=$y_chunk)
-      {
-        $orig_x = $word_start_x+($j*$font_pixelwidth);
-        // morph x += so that instead of deviating from orig x each time, we deviate from where we last deviated to
-        // get it? instead of a zig zag, we get more of a sine wave.
-        // I wish we could deviate more but it looks crap if we do.
-        $morph_x += $this->rand_func(-$morph_factor,$morph_factor);
-        // had to change this to ImageCopyMerge when starting using ImageCreateTrueColor
-        // according to the manual; "when (pct is) 100 this function behaves identically to imagecopy()"
-        // but this is NOT true when dealing with transparencies...
-        ImageCopyMerge($this->im2, $this->im, $orig_x+$morph_x, $i+$y_pos, $orig_x, $i, $font_pixelwidth, $y_chunk, 100);
-    
-        // for debug:
-        //ImageLine($this->im2, $orig_x+$morph_x, $i, $orig_x+$morph_x+1, $i+$y_chunk, $debug2);
-        //ImageLine($this->im2, $orig_x+$morph_x+$font_pixelwidth, $i, $orig_x+$morph_x+$font_pixelwidth+1, $i+$y_chunk, $debug2);
-      }
-    }
-    
-    // for debug:
-    //sendImage($this->im2);
-    
-    ImageFilledRectangle($this->im,0,0,$width,$height,$bg);
-    // now do the same on the y-axis
-    // (much easier because we can just do it across the whole image, don't have to do it char-by-char)
-    $y_pos = 0;
-    $x_chunk = 1;
-    for($i=0 ; $i<=$width ; $i+=$x_chunk)
-    {
-      // can result in image going too far off on Y-axis;
-      // not much I can do about that, apart from make image bigger
-      // again, I wish I could do 1.5 pixels
-      $y_pos += $this->rand_func(-1,1);
-      ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $x_chunk, $height);
-    
-      // for debug:
-      //ImageLine($this->im,$i+$x_chunk,0,$i+$x_chunk,100,$debug);
-      //ImageLine($this->im,$i,$y_pos+25,$i+$x_chunk,$y_pos+25,$debug);
-    }
-    
-    // for debug:
-    //sendImage($this->im);
-    
-    // blur edges:
-    // doesn't really add any security, but looks a lot nicer, and renders text a little easier to read
-    // for humans (hopefully not for OCRs, but if you know better, feel free to disable this function)
-    // (and if you do, let me know why)
-    $this->myImageBlur($this->im);
-    
-    // for debug:
-    //sendImage($this->im);
-    
-    if($this->output!="jpg" && $this->bg_type==0)
-    {
-      // make background transparent
-      ImageColorTransparent($this->im,$bg);
-    }
-    
-    
-    
-    
-    
-    //////////////////////////////////////////////////////
-    ////// Try to avoid 'free p*rn' style CAPTCHA re-use
-    //////////////////////////////////////////////////////
-    // ('*'ed to stop my site coming up for certain keyword searches on google)
-    
-    // can obscure CAPTCHA word in some cases..
-    
-    // write site tags 'shining through' the morphed image
-    ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2);
-    if(is_array($this->site_tags))
-    {
-      for($i=0 ; $i<sizeof($this->site_tags) ; $i++)
-      {
-        // ensure tags are centered
-        $tag_width = strlen($this->site_tags[$i])*6;
-        // write tag is chosen position
-        if($this->tag_pos==0 || $this->tag_pos==2)
-        {
-          // write at top
-          ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), (10*$i), $this->site_tags[$i], $site_tag_col2);
-        }
-        if($this->tag_pos==1 || $this->tag_pos==2)
-        {
-          // write at bottom
-          ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), ($height-34+($i*10)), $this->site_tags[$i], $site_tag_col2);
-        }
-      }
-    }
-    ImageCopyMerge($this->im2,$this->im,0,0,0,0,$width,$height,80);
-    ImageCopy($this->im,$this->im2,0,0,0,0,$width,$height);
-    // for debug:
-    //sendImage($this->im);
-    
-    
-    
-    
-    //////////////////////////////////////////////////////
-    ////// Merge with obfuscated background
-    //////////////////////////////////////////////////////
-    
-    if($this->bg_type!=0)
-    {
-      // merge bg image with CAPTCHA image to create smooth background
-    
-      // fade bg:
-      if($this->bg_type!=3)
-      {
-        $temp_im = ImageCreateTrueColor($width,$height);
-        $white = ImageColorAllocate($temp_im,255,255,255);
-        ImageFill($temp_im,0,0,$white);
-        ImageCopyMerge($this->im3,$temp_im,0,0,0,0,$width,$height,$bg_fade_pct);
-        // for debug:
-        //sendImage($this->im3);
-        ImageDestroy($temp_im);
-        $c_fade_pct = 50;
-      } else {
-        $c_fade_pct = $bg_fade_pct;
-      }
-    
-      // captcha over bg:
-      // might want to not blur if using this method
-      // otherwise leaves white-ish border around each letter
-      if($this->merge_type==1)
-      {
-        ImageCopyMerge($this->im3,$this->im,0,0,0,0,$width,$height,100);
-        ImageCopy($this->im,$this->im3,0,0,0,0,$width,$height);
-      } else {
-        // bg over captcha:
-        ImageCopyMerge($this->im,$this->im3,0,0,0,0,$width,$height,$c_fade_pct);
-      }
-    }
-    // for debug:
-    //sendImage($this->im);
-    
-    
-    //////////////////////////////////////////////////////
-    ////// Write tags, remove variables and output!
-    //////////////////////////////////////////////////////
-    
-    // tag it
-    // feel free to remove/change
-    // but if it's not essential I'd appreciate you leaving it
-    // after all, I've put a lot of work into this and am giving it away for free
-    // the least you could do is give me credit (or buy me stuff from amazon!)
-    // but I understand that in professional environments, your boss might not like this tag
-    // so that's cool.
-    $tag_str = "";
-    // for debug:
-    //$tag_str = "[".$word."]";
-    
-    // ensure tag is right-aligned
-    $tag_width = strlen($tag_str)*6;
-    // write tag
-    ImageString($this->im, 2, $width-$tag_width, $height-13, $tag_str, $tag_col);
-    
-    // unset all sensetive vars
-    // in case someone include()s this file on a shared server
-    // you might think this unneccessary, as it exit()s
-    // but by using register_shutdown_function
-    // on a -very- insecure shared server, they -might- be able to get the word
-    unset($word);
-    // the below aren't really essential, but might aid an OCR attack if discovered.
-    // so we unset them
-    
-    // output final image :-)
-    $this->sendImage($this->im);
-    // (sendImage also destroys all used images)
-  }
-  
-  function rand_func($s, $m)
-  {
-    global $_starttime;
-    $tn = microtime_float() - $_starttime;
-    if ( $tn > 5 )
-    {
-      echo '<pre>';
-      enano_debug_print_backtrace();
-      echo '</pre>';
-      exit;
-    }
-    $rf =& $this->rand_func;
-    return $rf($s, $m);
-  }
-  
-  function seed_func($s)
-  {
-    $rf =& $this->seed_func;
-    return $rf($s);
-  }
-  
-  function hash_func($s)
-  {
-    $rf =& $this->hash_func;
-    return $rf($s);
-  }
-  
+	
+	var $site_tags = array();
+	var $tag_pos = 0;
+	var $rand_func = "mt_rand";
+	var $seed_func = "mt_srand";
+	var $hash_func = "sha1";
+	var $output = "png";
+	var $use_dict = false;
+	var $dict_location = "";
+	var $max_word_length = 7;
+	var $col_type = 1;
+	var $max_attempts = 20;
+	var $font_locations = Array();
+	var $bg_type = 3;
+	var $blur_bg = false;
+	var $bg_images = Array();
+	var $merge_type = 0;
+	var $morph_bg = true;
+	var $im, $im2, $im3;
+	var $font_size = 36;
+	var $debug = false;
+	
+	function __construct($s, $r = false)
+	{
+		parent::__construct($s, $r);
+		
+		// try to avoid the 'free p*rn' method of CAPTCHA circumvention
+		// see en.wikipedia.org/wiki/CAPTCHA for more info
+		// $this->site_tags[0] = "To avoid spam, please do NOT enter the text if";
+		// $this->site_tags[1] = "this site is not puremango.co.uk";
+		// or more simply:
+		// $site_tags[0] = "for use only on puremango.co.uk";
+		// reword or add lines as you please
+		// or if you don't want any text:
+		$this->site_tags = array();
+		
+		// where to write the above:
+		// 0=top
+		// 1=bottom
+		// 2=both
+		$this->tag_pos = 1;
+		
+		// functions to call for random number generation
+		// mt_rand produces 'better' random numbers
+		// but if your server doesn't support it, it's fine to use rand instead
+		$this->rand_func = "mt_rand";
+		$this->seed_func = "mt_srand";
+		
+		// which type of hash to use?
+		// possible values: "sha1", "md5", "crc32"
+		// sha1 supported by PHP4.3.0+
+		// md5 supported by PHP3+
+		// crc32 supported by PHP4.0.1+
+		$this->hash_func = $this->session_fetch('hash_func', 'sha1');
+		// store in session so can validate in form processor
+		
+		// image type:
+		// possible values: "jpg", "png", "gif"
+		// jpg doesn't support transparency (transparent bg option ends up white)
+		// png isn't supported by old browsers (see http://www.libpng.org/pub/png/pngstatus.html)
+		// gif may not be supported by your GD Lib.
+		$this->output = "png";
+		
+		// 0=generate pseudo-random string, true=use dictionary
+		// dictionary is easier to recognise
+		// - both for humans and computers, so use random string if you're paranoid.
+		$this->use_dict = false;
+		// if your server is NOT set up to deny web access to files beginning ".ht"
+		// then you should ensure the dictionary file is kept outside the web directory
+		// eg: if www.foo.com/index.html points to c:\website\www\index.html
+		// then the dictionary should be placed in c:\website\dict.txt
+		// test your server's config by trying to access the dictionary through a web browser
+		// you should NOT be able to view the contents.
+		// can leave this blank if not using dictionary
+		$this->dict_location = ENANO_ROOT . "/includes/captcha/dicts/default.php";
+		
+		// used to calculate image width, and for non-dictionary word generation
+		$this->max_word_length = 7;
+		
+		// text colour
+		// 0=one random colour for all letters
+		// 1=different random colour for each letter
+		$this->col_type = 1;
+		
+		// maximum times a user can refresh the image
+		// on a 6500 word dictionary, I think 15-50 is enough to not annoy users and make BF unfeasble.
+		// further notes re: BF attacks in "avoid brute force attacks" section, below
+		// on the other hand, those attempting OCR will find the ability to request new images
+		// very useful; if they can't crack one, just grab an easier target...
+		// for the ultra-paranoid, setting it to <5 will still work for most users
+		$this->max_attempts = 20;
+		
+		// list of fonts to use
+		// font size should be around 35 pixels wide for each character.
+		// you can use my GD fontmaker script at www.puremango.co.uk to create your own fonts
+		// There are other programs to can create GD fonts, but my script allows a greater
+		// degree of control over exactly how wide each character is, and is therefore
+		// recommended for 'special' uses. For normal use of GD fonts,
+		// the GDFontGenerator @ http://www.philiplb.de is excellent for convering ttf to GD
+		
+		// the fonts included with freeCap *only* include lowercase alphabetic characters
+		// so are not suitable for most other uses
+		// to increase security, you really should add other fonts
+		$this->font_locations = Array(
+				//ENANO_ROOT . "/includes/captcha/fonts/assimila.ttf",
+				//ENANO_ROOT . "/includes/captcha/fonts/elephant.ttf",
+				//ENANO_ROOT . "/includes/captcha/fonts/swash_normal.ttf",
+				//ENANO_ROOT . "/includes/captcha/fonts/.ttf",
+				//ENANO_ROOT . "/includes/captcha/fonts/trekker_regular.ttf"
+				ENANO_ROOT . "/includes/captcha/fonts/FreeMonoBold.ttf",
+				ENANO_ROOT . "/includes/captcha/fonts/FreeSerifBold.ttf",
+				ENANO_ROOT . "/includes/captcha/fonts/LiberationSans-Bold.ttf",
+			);
+		
+		// background:
+		// 0=transparent (if jpg, white)
+		// 1=white bg with grid
+		// 2=white bg with squiggles
+		// 3=morphed image blocks
+		// 'random' background from v1.3 didn't provide any extra security (according to 2 independent experts)
+		// many thanks to http://ocr-research.org.ua and http://sam.zoy.org/pwntcha/ for testing
+		// for jpgs, 'transparent' is white
+		$this->bg_type = 3;
+		// should we blur the background? (looks nicer, makes text easier to read, takes longer)
+		$this->blur_bg = false;
+		
+		// for bg_type 3, which images should we use?
+		// if you add your own, make sure they're fairly 'busy' images (ie a lot of shapes in them)
+		$this->bg_images = Array(
+				ENANO_ROOT . "/includes/captcha/pics/freecap_im1.jpg",
+				ENANO_ROOT . "/includes/captcha/pics/freecap_im2.jpg",
+				ENANO_ROOT . "/includes/captcha/pics/freecap_im3.jpg",
+				ENANO_ROOT . "/includes/captcha/pics/freecap_im4.jpg",
+				ENANO_ROOT . "/includes/captcha/pics/allyourbase.jpg"
+			);
+		
+		// for non-transparent backgrounds only:
+		// if 0, merges CAPTCHA with bg
+		// if 1, write CAPTCHA over bg
+		$this->merge_type = 0;
+		// should we morph the bg? (recommend yes, but takes a little longer to compute)
+		$this->morph_bg = true;
+		
+		// you shouldn't need to edit anything below this, but it's extensively commented if you do want to play
+		// have fun, and email me with ideas, or improvements to the code (very interested in speed improvements)
+		// hope this script saves some spam :-)
+	}
+	
+	//////////////////////////////////////////////////////
+	////// Functions:
+	//////////////////////////////////////////////////////
+	function make_seed() {
+	// from http://php.net/srand
+			list($usec, $sec) = explode(' ', microtime());
+			return (float) $sec + ((float) $usec * 100000);
+	}
+	
+	function rand_color() {
+		$rf =& $this->rand_func;
+		if($this->bg_type==3)
+		{
+			// needs darker colour..
+			return $rf(10,100);
+		} else {
+			return $rf(60,170);
+		}
+	}
+	
+	function myImageBlur($im)
+	{
+		// w00t. my very own blur function
+		// in GD2, there's a gaussian blur function. bunch of bloody show-offs... :-)
+	
+		$width = imagesx($im);
+		$height = imagesy($im);
+	
+		$temp_im = ImageCreateTrueColor($width,$height);
+		$bg = ImageColorAllocate($temp_im,150,150,150);
+	
+		// preserves transparency if in orig image
+		ImageColorTransparent($temp_im,$bg);
+	
+		// fill bg
+		ImageFill($temp_im,0,0,$bg);
+	
+		// anything higher than 3 makes it totally unreadable
+		// might be useful in a 'real' blur function, though (ie blurring pictures not text)
+		$distance = 1;
+		// use $distance=30 to have multiple copies of the word. not sure if this is useful.
+	
+		// blur by merging with itself at different x/y offsets:
+		ImageCopyMerge($temp_im, $im, 0, 0, 0, $distance, $width, $height-$distance, 70);
+		ImageCopyMerge($im, $temp_im, 0, 0, $distance, 0, $width-$distance, $height, 70);
+		ImageCopyMerge($temp_im, $im, 0, $distance, 0, 0, $width, $height, 70);
+		ImageCopyMerge($im, $temp_im, $distance, 0, 0, 0, $width, $height, 70);
+		// remove temp image
+		ImageDestroy($temp_im);
+	
+		return $im;
+	}
+	
+	function sendImage($pic)
+	{
+		// output image with appropriate headers
+		global $output,$im,$im2,$im3;
+		// ENANO - obfuscation technique disabled
+		// (this is for ethical reasons - ask dan at enanocms.org for information on why)
+		// Basically it outputs an X-Captcha header showing freeCap version, etc. Unnecessary
+		// header(base64_decode("WC1DYXB0Y2hhOiBmcmVlQ2FwIDEuNCAtIHd3dy5wdXJlbWFuZ28uY28udWs="));
+		
+		if ( $this->debug )
+		{
+			$x = imagesx($pic) - 70;
+			$y = imagesy($pic) - 20;
+			
+			$code = $this->get_code();
+			$red = ImageColorAllocateAlpha($pic, 0xAA, 0, 0, 72);
+			ImageString($pic, 5, $x, $y, $code, $red);
+			ImageString($pic, 5, 5, $y, "[debug mode]", $red);
+		}
+		
+		switch($this->output)
+		{
+			// add other cases as desired
+			case "jpg":
+				header("Content-Type: image/jpeg");
+				ImageJPEG($pic);
+				break;
+			case "gif":
+				header("Content-Type: image/gif");
+				ImageGIF($pic);
+				break;
+			case "png":
+			default:
+				header("Content-Type: image/png");
+				ImagePNG($pic);
+				break;
+		}
+	
+		// kill GD images (removes from memory)
+		ImageDestroy($this->im);
+		ImageDestroy($this->im2);
+		ImageDestroy($pic);
+		if(!empty($this->im3))
+		{
+			ImageDestroy($this->im3);
+		}
+		exit();
+	}
+	
+	function make_image()
+	{
+		//////////////////////////////////////////////////////
+		////// Create Images + initialise a few things
+		//////////////////////////////////////////////////////
+		
+		// seed random number generator
+		// PHP 4.2.0+ doesn't need this, but lower versions will
+		$this->seed_func($this->make_seed());
+		
+		// how faded should the bg be? (100=totally gone, 0=bright as the day)
+		// to test how much protection the bg noise gives, take a screenshot of the freeCap image
+		// and take it into a photo editor. play with contrast and brightness.
+		// If you can remove most of the bg, then it's not a good enough percentage
+		switch($this->bg_type)
+		{
+			case 0:
+				break;
+			case 1:
+			case 2:
+				$bg_fade_pct = 65;
+				break;
+			case 3:
+				$bg_fade_pct = 50;
+				break;
+		}
+		// slightly randomise the bg fade
+		$bg_fade_pct += $this->rand_func(-2,2);
+		
+		// read each font and get font character widths
+		// $font_widths = Array();
+		// for($i=0 ; $i<sizeof($this->font_locations) ; $i++)
+		// {
+		//   $handle = fopen($this->font_locations[$i],"r");
+		//   // read header of GD font, up to char width
+		//   $c_wid = fread($handle,15);
+		//   $font_widths[$i] = ord($c_wid{8})+ord($c_wid{9})+ord($c_wid{10})+ord($c_wid{11});
+		//   fclose($handle);
+		// }
+		
+		// modify image width depending on maximum possible length of word
+		// you shouldn't need to use words > 6 chars in length really.
+		$width = ($this->max_word_length*($this->font_size+10)+75);
+		$height = 90;
+		
+		$this->im = ImageCreateTrueColor($width, $height);
+		$this->im2 = ImageCreateTrueColor($width, $height);
+		
+		////////////////////////////////////////////////////////
+		// GENERATE IMAGE                                     //
+		////////////////////////////////////////////////////////
+		
+		$word = $this->get_code();
+		
+		// save hash of word for comparison
+		// using hash so that if there's an insecurity elsewhere (eg on the form processor),
+		// an attacker could only get the hash
+		// also, shared servers usually give all users access to the session files
+		// echo `ls /tmp`; and echo `more /tmp/someone_elses_session_file`; usually work
+		// so even if your site is 100% secure, someone else's site on your server might not be
+		// hence, even if attackers can read the session file, they can't get the freeCap word
+		// (though most hashes are easy to brute force for simple strings)
+		
+		//////////////////////////////////////////////////////
+		////// Fill BGs and Allocate Colours:
+		//////////////////////////////////////////////////////
+		
+		// set tag colour
+		// have to do this before any distortion
+		// (otherwise colour allocation fails when bg type is 1)
+		$tag_col = ImageColorAllocate($this->im,10,10,10);
+		$site_tag_col2 = ImageColorAllocate($this->im2,0,0,0);
+		
+		// set debug colours (text colours are set later)
+		$debug = ImageColorAllocate($this->im, 255, 0, 0);
+		$debug2 = ImageColorAllocate($this->im2, 255, 0, 0);
+		
+		// set background colour (can change to any colour not in possible $text_col range)
+		// it doesn't matter as it'll be transparent or coloured over.
+		// if you're using bg_type 3, you might want to try to ensure that the color chosen
+		// below doesn't appear too much in any of your background images.
+		$bg = ImageColorAllocate($this->im, 254, 254, 254);
+		$bg2 = ImageColorAllocate($this->im2, 254, 254, 254);
+		
+		// set transparencies
+		ImageColorTransparent($this->im,$bg);
+		// im2 transparent to allow characters to overlap slightly while morphing
+		ImageColorTransparent($this->im2,$bg2);
+		
+		// fill backgrounds
+		ImageFill($this->im,0,0,$bg);
+		ImageFill($this->im2,0,0,$bg2);
+		
+		if($this->bg_type!=0)
+		{
+			// generate noisy background, to be merged with CAPTCHA later
+			// any suggestions on how best to do this much appreciated
+			// sample code would be even better!
+			// I'm not an OCR expert (hell, I'm not even an image expert; puremango.co.uk was designed in MsPaint)
+			// so the noise models are based around my -guesswork- as to what would make it hard for an OCR prog
+			// ideally, the character obfuscation would be strong enough not to need additional background noise
+			// in any case, I hope at least one of the options given here provide some extra security!
+		
+			$this->im3 = ImageCreateTrueColor($width,$height);
+			$temp_bg = ImageCreateTrueColor($width*1.5,$height*1.5);
+			$bg3 = ImageColorAllocate($this->im3,255,255,255);
+			ImageFill($this->im3,0,0,$bg3);
+			$temp_bg_col = ImageColorAllocate($temp_bg,255,255,255);
+			ImageFill($temp_bg,0,0,$temp_bg_col);
+			
+			// we draw all noise onto temp_bg
+			// then if we're morphing, merge from temp_bg to im3
+			// or if not, just copy a $widthx$height portion of $temp_bg to $this->im3
+			// temp_bg is much larger so that when morphing, the edges retain the noise.
+		
+			if($this->bg_type==1)
+			{
+				// grid bg:
+		
+				// draw grid on x
+				for($i=$this->rand_func(6,20) ; $i<$width*2 ; $i+=$this->rand_func(10,25))
+				{
+					ImageSetThickness($temp_bg,$this->rand_func(2,6));
+					$text_r = $this->rand_func(100,150);
+					$text_g = $this->rand_func(100,150);
+					$text_b = $this->rand_func(100,150);
+					$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
+		
+					ImageLine($temp_bg,$i,0,$i,$height*2,$text_colour3);
+				}
+				// draw grid on y
+				for($i=$this->rand_func(6,20) ; $i<$height*2 ; $i+=$this->rand_func(10,25))
+				{
+					ImageSetThickness($temp_bg,$this->rand_func(2,6));
+					$text_r = $this->rand_func(100,150);
+					$text_g = $this->rand_func(100,150);
+					$text_b = $this->rand_func(100,150);
+					$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
+		
+					ImageLine($temp_bg,0,$i,$width*2, $i ,$text_colour3);
+				}
+			} else if($this->bg_type==2) {
+				// draw squiggles!
+		
+				$bg3 = ImageColorAllocate($this->im3,255,255,255);
+				ImageFill($this->im3,0,0,$bg3);
+				ImageSetThickness($temp_bg,4);
+		
+				for($i=0 ; $i<strlen($word)+1 ; $i++)
+				{
+					$text_r = $this->rand_func(100,150);
+					$text_g = $this->rand_func(100,150);
+					$text_b = $this->rand_func(100,150);
+					$text_colour3 = ImageColorAllocate($temp_bg, $text_r, $text_g, $text_b);
+		
+					$points = Array();
+					// draw random squiggle for each character
+					// the longer the loop, the more complex the squiggle
+					// keep random so OCR can't say "if found shape has 10 points, ignore it"
+					// each squiggle will, however, be a closed shape, so OCR could try to find
+					// line terminations and start from there. (I don't think they're that advanced yet..)
+					for($j=1 ; $j<$this->rand_func(5,10) ; $j++)
+					{
+						$points[] = $this->rand_func(1*(20*($i+1)),1*(50*($i+1)));
+						$points[] = $this->rand_func(30,$height+30);
+					}
+		
+					ImagePolygon($temp_bg,$points,intval(sizeof($points)/2),$text_colour3);
+				}
+		
+			} else if($this->bg_type==3) {
+				// take random chunks of $this->bg_images and paste them onto the background
+		
+				for($i=0 ; $i<sizeof($this->bg_images) ; $i++)
+				{
+					// read each image and its size
+					$temp_im[$i] = ImageCreateFromJPEG($this->bg_images[$i]);
+					$temp_width[$i] = imagesx($temp_im[$i]);
+					$temp_height[$i] = imagesy($temp_im[$i]);
+				}
+				
+				$blocksize = $this->rand_func(20,60);
+				for($i=0 ; $i<$width*2 ; $i+=$blocksize)
+				{
+					// could randomise blocksize here... hardly matters
+					for($j=0 ; $j<$height*2 ; $j+=$blocksize)
+					{
+						$this->image_index = $this->rand_func(0,sizeof($temp_im)-1);
+						$cut_x = $this->rand_func(0,$temp_width[$this->image_index]-$blocksize);
+						$cut_y = $this->rand_func(0,$temp_height[$this->image_index]-$blocksize);
+						ImageCopy($temp_bg, $temp_im[$this->image_index], $i, $j, $cut_x, $cut_y, $blocksize, $blocksize);
+					}
+				}
+				for($i=0 ; $i<sizeof($temp_im) ; $i++)
+				{
+					// remove bgs from memory
+					ImageDestroy($temp_im[$i]);
+				}
+		
+				// for debug:
+				//sendImage($temp_bg);
+			}
+		
+			// for debug:
+			//sendImage($this->im3);
+		
+			if($this->morph_bg)
+			{
+				// morph background
+				// we do this separately to the main text morph because:
+				// a) the main text morph is done char-by-char, this is done across whole image
+				// b) if an attacker could un-morph the bg, it would un-morph the CAPTCHA
+				// hence bg is morphed differently to text
+				// why do we morph it at all? it might make it harder for an attacker to remove the background
+				// morph_chunk 1 looks better but takes longer
+		
+				// this is a different and less perfect morph than the one we do on the CAPTCHA
+				// occasonally you get some dark background showing through around the edges
+				// it doesn't need to be perfect as it's only the bg.
+				$morph_chunk = $this->rand_func(1,5);
+				$morph_y = 0;
+				for($x=0 ; $x<$width ; $x+=$morph_chunk)
+				{
+					$morph_chunk = $this->rand_func(1,5);
+					$morph_y += $this->rand_func(-1,1);
+					ImageCopy($this->im3, $temp_bg, $x, 0, $x+30, 30+$morph_y, $morph_chunk, $height*2);
+				}
+		
+				ImageCopy($temp_bg, $this->im3, 0, 0, 0, 0, $width, $height);
+		
+				$morph_x = 0;
+				for($y=0 ; $y<=$height; $y+=$morph_chunk)
+				{
+					$morph_chunk = $this->rand_func(1,5);
+					$morph_x += $this->rand_func(-1,1);
+					ImageCopy($this->im3, $temp_bg, $morph_x, $y, 0, $y, $width, $morph_chunk);
+		
+				}
+			} else {
+				// just copy temp_bg onto im3
+				ImageCopy($this->im3,$temp_bg,0,0,30,30,$width,$height);
+			}
+		
+			ImageDestroy($temp_bg);
+		
+			if($this->blur_bg)
+			{
+				$this->myImageBlur($this->im3);
+			}
+		}
+		// for debug:
+		//sendImage($this->im3);
+		
+		//////////////////////////////////////////////////////
+		////// Write Word
+		//////////////////////////////////////////////////////
+		
+		// write word in random starting X position
+		$word_start_x = $this->rand_func(5,32);
+		// y positions jiggled about later
+		$word_start_y = 50;
+		
+		// use last pixelwidth
+		$font_pixelwidth = $this->font_size + 10;
+		
+		if($this->col_type==0)
+		{
+			$text_r = $this->rand_color();
+			$text_g = $this->rand_color();
+			$text_b = $this->rand_color();
+			$text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b);
+		}
+		
+		// write each char in different font
+		for($i=0 ; $i<strlen($word) ; $i++)
+		{
+			if($this->col_type==1)
+			{
+				$text_r = $this->rand_color();
+				$text_g = $this->rand_color();
+				$text_b = $this->rand_color();
+				$text_colour2 = ImageColorAllocate($this->im2, $text_r, $text_g, $text_b);
+			}
+		
+			$j = $this->rand_func(0,sizeof($this->font_locations)-1);
+			// $font = ImageLoadFont($this->font_locations[$j]);
+			// ImageString($this->im2, $font, $word_start_x+($font_widths[$j]*$i), $word_start_y, $word{$i}, $text_colour2);
+			ImageTTFText($this->im2, $this->font_size, 0, $word_start_x+(($font_pixelwidth)*$i), $word_start_y, $text_colour2, $this->font_locations[$j], $word{$i});
+		}
+		
+		// for debug:
+		// $this->sendImage($this->im2);
+		
+		//////////////////////////////////////////////////////
+		////// Morph Image:
+		//////////////////////////////////////////////////////
+		
+		// calculate how big the text is in pixels
+		// (so we only morph what we need to)
+		$word_pix_size = $word_start_x+(strlen($word)*$font_pixelwidth);
+		
+		// firstly move each character up or down a bit:
+		$y_pos = 0;
+		for($i=$word_start_x ; $i<$word_pix_size ; $i+=$font_pixelwidth)
+		{
+			// move on Y axis
+			// deviates at least 4 pixels between each letter
+			$prev_y = $y_pos;
+			do{
+				$y_pos = $this->rand_func(-5,5);
+			} while($y_pos<$prev_y+2 && $y_pos>$prev_y-2);
+			ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $font_pixelwidth, $height);
+		
+			// for debug:
+			// ImageRectangle($this->im,$i,$y_pos+10,$i+$font_pixelwidth,$y_pos+70,$debug);
+		}
+		
+		// for debug:
+		// $this->sendImage($this->im);
+		
+		ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2);
+		
+		// randomly morph each character individually on x-axis
+		// this is where the main distortion happens
+		// massively improved since v1.2
+		$y_chunk = 1;
+		$morph_factor = 1;
+		$morph_x = 0;
+		for($j=0 ; $j<strlen($word) ; $j++)
+		{
+			$y_pos = 0;
+			for($i=0 ; $i<=$height; $i+=$y_chunk)
+			{
+				$orig_x = $word_start_x+($j*$font_pixelwidth);
+				// morph x += so that instead of deviating from orig x each time, we deviate from where we last deviated to
+				// get it? instead of a zig zag, we get more of a sine wave.
+				// I wish we could deviate more but it looks crap if we do.
+				$morph_x += $this->rand_func(-$morph_factor,$morph_factor);
+				// had to change this to ImageCopyMerge when starting using ImageCreateTrueColor
+				// according to the manual; "when (pct is) 100 this function behaves identically to imagecopy()"
+				// but this is NOT true when dealing with transparencies...
+				ImageCopyMerge($this->im2, $this->im, $orig_x+$morph_x, $i+$y_pos, $orig_x, $i, $font_pixelwidth, $y_chunk, 100);
+		
+				// for debug:
+				//ImageLine($this->im2, $orig_x+$morph_x, $i, $orig_x+$morph_x+1, $i+$y_chunk, $debug2);
+				//ImageLine($this->im2, $orig_x+$morph_x+$font_pixelwidth, $i, $orig_x+$morph_x+$font_pixelwidth+1, $i+$y_chunk, $debug2);
+			}
+		}
+		
+		// for debug:
+		//sendImage($this->im2);
+		
+		ImageFilledRectangle($this->im,0,0,$width,$height,$bg);
+		// now do the same on the y-axis
+		// (much easier because we can just do it across the whole image, don't have to do it char-by-char)
+		$y_pos = 0;
+		$x_chunk = 1;
+		for($i=0 ; $i<=$width ; $i+=$x_chunk)
+		{
+			// can result in image going too far off on Y-axis;
+			// not much I can do about that, apart from make image bigger
+			// again, I wish I could do 1.5 pixels
+			$y_pos += $this->rand_func(-1,1);
+			ImageCopy($this->im, $this->im2, $i, $y_pos, $i, 0, $x_chunk, $height);
+		
+			// for debug:
+			//ImageLine($this->im,$i+$x_chunk,0,$i+$x_chunk,100,$debug);
+			//ImageLine($this->im,$i,$y_pos+25,$i+$x_chunk,$y_pos+25,$debug);
+		}
+		
+		// for debug:
+		//sendImage($this->im);
+		
+		// blur edges:
+		// doesn't really add any security, but looks a lot nicer, and renders text a little easier to read
+		// for humans (hopefully not for OCRs, but if you know better, feel free to disable this function)
+		// (and if you do, let me know why)
+		$this->myImageBlur($this->im);
+		
+		// for debug:
+		//sendImage($this->im);
+		
+		if($this->output!="jpg" && $this->bg_type==0)
+		{
+			// make background transparent
+			ImageColorTransparent($this->im,$bg);
+		}
+		
+		
+		
+		
+		
+		//////////////////////////////////////////////////////
+		////// Try to avoid 'free p*rn' style CAPTCHA re-use
+		//////////////////////////////////////////////////////
+		// ('*'ed to stop my site coming up for certain keyword searches on google)
+		
+		// can obscure CAPTCHA word in some cases..
+		
+		// write site tags 'shining through' the morphed image
+		ImageFilledRectangle($this->im2,0,0,$width,$height,$bg2);
+		if(is_array($this->site_tags))
+		{
+			for($i=0 ; $i<sizeof($this->site_tags) ; $i++)
+			{
+				// ensure tags are centered
+				$tag_width = strlen($this->site_tags[$i])*6;
+				// write tag is chosen position
+				if($this->tag_pos==0 || $this->tag_pos==2)
+				{
+					// write at top
+					ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), (10*$i), $this->site_tags[$i], $site_tag_col2);
+				}
+				if($this->tag_pos==1 || $this->tag_pos==2)
+				{
+					// write at bottom
+					ImageString($this->im2, 2, intval($width/2)-intval($tag_width/2), ($height-34+($i*10)), $this->site_tags[$i], $site_tag_col2);
+				}
+			}
+		}
+		ImageCopyMerge($this->im2,$this->im,0,0,0,0,$width,$height,80);
+		ImageCopy($this->im,$this->im2,0,0,0,0,$width,$height);
+		// for debug:
+		//sendImage($this->im);
+		
+		
+		
+		
+		//////////////////////////////////////////////////////
+		////// Merge with obfuscated background
+		//////////////////////////////////////////////////////
+		
+		if($this->bg_type!=0)
+		{
+			// merge bg image with CAPTCHA image to create smooth background
+		
+			// fade bg:
+			if($this->bg_type!=3)
+			{
+				$temp_im = ImageCreateTrueColor($width,$height);
+				$white = ImageColorAllocate($temp_im,255,255,255);
+				ImageFill($temp_im,0,0,$white);
+				ImageCopyMerge($this->im3,$temp_im,0,0,0,0,$width,$height,$bg_fade_pct);
+				// for debug:
+				//sendImage($this->im3);
+				ImageDestroy($temp_im);
+				$c_fade_pct = 50;
+			} else {
+				$c_fade_pct = $bg_fade_pct;
+			}
+		
+			// captcha over bg:
+			// might want to not blur if using this method
+			// otherwise leaves white-ish border around each letter
+			if($this->merge_type==1)
+			{
+				ImageCopyMerge($this->im3,$this->im,0,0,0,0,$width,$height,100);
+				ImageCopy($this->im,$this->im3,0,0,0,0,$width,$height);
+			} else {
+				// bg over captcha:
+				ImageCopyMerge($this->im,$this->im3,0,0,0,0,$width,$height,$c_fade_pct);
+			}
+		}
+		// for debug:
+		//sendImage($this->im);
+		
+		
+		//////////////////////////////////////////////////////
+		////// Write tags, remove variables and output!
+		//////////////////////////////////////////////////////
+		
+		// tag it
+		// feel free to remove/change
+		// but if it's not essential I'd appreciate you leaving it
+		// after all, I've put a lot of work into this and am giving it away for free
+		// the least you could do is give me credit (or buy me stuff from amazon!)
+		// but I understand that in professional environments, your boss might not like this tag
+		// so that's cool.
+		$tag_str = "";
+		// for debug:
+		//$tag_str = "[".$word."]";
+		
+		// ensure tag is right-aligned
+		$tag_width = strlen($tag_str)*6;
+		// write tag
+		ImageString($this->im, 2, $width-$tag_width, $height-13, $tag_str, $tag_col);
+		
+		// unset all sensetive vars
+		// in case someone include()s this file on a shared server
+		// you might think this unneccessary, as it exit()s
+		// but by using register_shutdown_function
+		// on a -very- insecure shared server, they -might- be able to get the word
+		unset($word);
+		// the below aren't really essential, but might aid an OCR attack if discovered.
+		// so we unset them
+		
+		// output final image :-)
+		$this->sendImage($this->im);
+		// (sendImage also destroys all used images)
+	}
+	
+	function rand_func($s, $m)
+	{
+		global $_starttime;
+		$tn = microtime_float() - $_starttime;
+		if ( $tn > 5 )
+		{
+			echo '<pre>';
+			enano_debug_print_backtrace();
+			echo '</pre>';
+			exit;
+		}
+		$rf =& $this->rand_func;
+		return $rf($s, $m);
+	}
+	
+	function seed_func($s)
+	{
+		$rf =& $this->seed_func;
+		return $rf($s);
+	}
+	
+	function hash_func($s)
+	{
+		$rf =& $this->hash_func;
+		return $rf($s);
+	}
+	
 }
--- a/includes/captcha/engine_potpourri.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/captcha/engine_potpourri.php	Sun Mar 28 23:10:46 2010 -0400
@@ -22,310 +22,310 @@
  
 class captcha_engine_potpourri extends captcha_base
 {
-  var $captcha_config;
-  
-  function __construct($sid, $r = false)
-  {
-    parent::__construct($sid, $r);
-    
-    $hex = Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
-    $latticecolor = '#';
-    for ( $i = 0; $i < 6; $i++ )
-      $latticecolor .= $hex[mt_rand(0, 15)];
-    
-    $this->captcha_config = array (
-      'width' => '350',
-      'height' => '90',
-      'background_color' => '#E5ECF9',
-      'jpeg' => '0',
-      'jpeg_quality' => '95',
-      'pre_letters' => '1',
-      'pre_letters_great' => '0',
-      'font' => '1',
-      'chess' => '2',
-      'ellipses' => '2',
-      'arcs' => '2',
-      'lines' => '2',
-      'image' => '0',
-      'gammacorrect' => '0.8',
-      'foreground_lattice_x' => (string)mt_rand(25, 30),
-      'foreground_lattice_y' => (string)mt_rand(25, 30),
-      'lattice_color' => $latticecolor,
-    );
-  }
-  
-  function make_image()
-  {
-    $code = $this->get_code();
-    
-    // Prefs
-    $total_width = $this->captcha_config['width'];
-    $total_height = $this->captcha_config['height'];
-    
-    $hex_bg_color = $this->get_rgb($this->captcha_config['background_color']);
-    $bg_color = array();
-    $bg_color = explode(",", $hex_bg_color);
-    
-    $jpeg = $this->captcha_config['jpeg'];
-    $img_quality = $this->captcha_config['jpeg_quality'];
-    // Max quality is 95
-    
-    $pre_letters = $this->captcha_config['pre_letters'];
-    $pre_letter_great = $this->captcha_config['pre_letters_great'];
-    $rnd_font = $this->captcha_config['font'];
-    $chess = $this->captcha_config['chess'];
-    $ellipses = $this->captcha_config['ellipses'];
-    $arcs = $this->captcha_config['arcs'];
-    $lines = $this->captcha_config['lines'];
-    $image = $this->captcha_config['image'];
-    
-    $gammacorrect = $this->captcha_config['gammacorrect'];
-    
-    $foreground_lattice_y = $this->captcha_config['foreground_lattice_y'];
-    $foreground_lattice_x = $this->captcha_config['foreground_lattice_x'];
-    $hex_lattice_color = $this->get_rgb($this->captcha_config['lattice_color']);
-    $rgb_lattice_color = array();
-    $rgb_lattice_color = explode(",", $hex_lattice_color);
-    
-    $font_debug = false;
-    
-    // Fonts and images init
-    if ($image)
-    {
-      $bg_imgs = array();
-      if ($img_dir = opendir(ENANO_ROOT.'/includes/captcha/pics/'))
-      {
-        while (true == ($file = @readdir($img_dir))) 
-        { 
-          if ((substr(strtolower($file), -3) == 'jpg') || (substr(strtolower($file), -3) == 'gif'))    
-          {         
-            $bg_imgs[] = $file; 
-          }     
-        }
-        closedir($img_dir);
-      }
-      // Grab a random Background Image or set FALSE if none was found
-      $bg_img = ( count($bg_imgs) ) ? rand(0, (count($bg_imgs)-1)) : false;
-    }
-    
-    $fonts = array();
-    if ($fonts_dir = opendir(ENANO_ROOT . '/includes/captcha/fonts/'))
-    {
-      while (true == ($file = @readdir($fonts_dir))) 
-      { 
-        if ((substr(strtolower($file), strlen($file)-3, strlen($file)) == 'ttf'))
-        {
-          $fonts[] = $file; 
-        }     
-      }
-      closedir($fonts_dir);
-    } else {
-      die('Error reading directory: '.ENANO_ROOT.'/includes/captcha/fonts/');
-    }
-    $font = mt_rand(0, (count($fonts)-1));
-    
-    // Generate
-    $image = ($this->gdVersion() >= 2) ? imagecreatetruecolor($total_width, $total_height) : imagecreate($total_width, $total_height);
-    $background_color = imagecolorallocate($image, $bg_color[0], $bg_color[1], $bg_color[2]);
-    imagefill($image, 0, 0, $background_color);
-    
-    // Generate backgrund
-    if ($chess == '1' || $chess == '2' && rand(0,1))
-    {
-      // Draw rectangles
-      for($i = 0; $i <= 8; $i++)
-      {
-        $rectanglecolor = imagecolorallocate($image, rand(100,200),rand(100,200),rand(100,200));
-        imagefilledrectangle($image, 0, 0, round($total_width-($total_width/8*$i)), round($total_height), $rectanglecolor);
-        $rectanglecolor = imagecolorallocate($image, rand(100,200),rand(100,200),rand(100,200));
-        imagefilledrectangle($image, 0, 0, round($total_width-($total_width/8*$i)), round($total_height/2), $rectanglecolor);
-      }
-    }
-    if ($ellipses == '1' || $ellipses == '2' && rand(0,1))
-    {
-      // Draw random ellipses
-      for ($i = 1; $i <= 60; $i++)
-      {
-        $ellipsecolor = imagecolorallocate($image, rand(100,250),rand(100,250),rand(100,250));
-        imagefilledellipse($image, round(rand(0, $total_width)), round(rand(0, $total_height)), round(rand(0, $total_width/8)), round(rand(0, $total_height/4)), $ellipsecolor);	
-      }
-    }
-    if ($arcs == '1' || $arcs == '2' && rand(0,1))
-    {
-      // Draw random partial ellipses
-      for ($i = 0; $i <= 30; $i++)
-      {
-        $linecolor = imagecolorallocate($image, rand(120,255),rand(120,255),rand(120,255));
-        $cx = round(rand(1, $total_width));
-        $cy = round(rand(1, $total_height));
-        $int_w = round(rand(1, $total_width/2));
-        $int_h = round(rand(1, $total_height));
-        imagearc($image, $cx, $cy, $int_w, $int_h, round(rand(0, 190)), round(rand(191, 360)), $linecolor);
-        imagearc($image, $cx-1, $cy-1, $int_w, $int_h, round(rand(0, 190)), round(rand(191, 360)), $linecolor);
-      }
-    }
-    if ($lines == '1' || $lines == '2' && rand(0,1))
-    {
-      // Draw random lines
-      for ($i = 0; $i <= 50; $i++)
-      {
-        $linecolor = imagecolorallocate($image, rand(120,255),rand(120,255),rand(120,255));
-        imageline($image, round(rand(1, $total_width*3)), round(rand(1, $total_height*5)), round(rand(1, $total_width/2)), round(rand(1, $total_height*2)), $linecolor);
-      }
-    }
-    
-    $text_color_array = array('255,51,0', '51,77,255', '204,51,102', '0,153,0', '255,166,2', '255,0,255', '255,0,0', '0,255,0', '0,0,255', '0,255,255');
-    shuffle($text_color_array);
-    $pre_text_color_array = array('255,71,20', '71,20,224', '224,71,122', '20,173,20', '255,186,22', '25,25,25');
-    shuffle($pre_text_color_array);
-    $white = imagecolorallocate($image, 255, 255, 255);
-    $gray = imagecolorallocate($image, 100, 100, 100);
-    $black = imagecolorallocate($image, 0, 0, 0);
-    $lattice_color = imagecolorallocate($image, $rgb_lattice_color[0], $rgb_lattice_color[1], $rgb_lattice_color[2]);
-    
-    $x_char_position = (round(($total_width - 12) / strlen($code)) + mt_rand(-3, 5));
-    
-    for ($i = 0; $i < strlen($code); $i++)
-    {
-      mt_srand((double)microtime()*1000000);
-    
-      $char = $code{$i};
-      $size = mt_rand(floor($total_height / 3.5), ceil($total_height / 2.8));
-      $font = ($rnd_font) ? rand(0, (count($fonts)-1)) : $font;
-      $angle = mt_rand(-30, 30);
-    
-      $char_pos = array();
-      $char_pos = imagettfbbox($size, $angle, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
-      $letter_width = abs($char_pos[0]) + abs($char_pos[4]);
-      $letter_height = abs($char_pos[1]) + abs($char_pos[5]);
-    
-      $x_pos = ($x_char_position / 4) + ($i * $x_char_position);
-      ($i == strlen($code)-1 && $x_pos >= ($total_width - ($letter_width + 5))) ? $x_pos = ($total_width - ($letter_width + 5)) : '';
-      $y_pos = mt_rand(($size * 1.4 ), $total_height - ($size * 0.4));
-    
-    //	Pre letters
-      $size = ($pre_letter_great) ? $size + (2 * $pre_letters) : $size - (2 * $pre_letters);
-      for ($count = 1; $count <= $pre_letters; $count++)
-      {
-        $pre_angle = $angle + mt_rand(-20, 20);
-    
-        $text_color = $pre_text_color_array[mt_rand(0,count($pre_text_color_array)-1)];
-        $text_color = explode(",", $text_color);
-        $textcolor = imagecolorallocate($image, $text_color[0], $text_color[1], $text_color[2]);
-    
-        imagettftext($image, $size, $pre_angle, $x_pos, $y_pos-2, $white, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
-        imagettftext($image, $size, $pre_angle, $x_pos+2, $y_pos, $black, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
-        imagettftext($image, $size, $pre_angle, $x_pos+1, $y_pos-1, $textcolor, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
-    
-        $size = ($pre_letter_great) ? $size - 2 : $size + 2;
-      }
-    
-    //	Final letters
-      $text_color = $text_color_array[mt_rand(0,count($text_color_array)-1)];
-      $text_color = explode(",", $text_color);
-      $textcolor = imagecolorallocate($image, $text_color[0], $text_color[1], $text_color[2]);
-    
-      imagettftext($image, $size, $angle, $x_pos, $y_pos-2, $white, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
-      imagettftext($image, $size, $angle, $x_pos+2, $y_pos, $black, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
-      imagettftext($image, $size, $angle, $x_pos+1, $y_pos-1, $textcolor, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
-    }
-    
-    
-    ($gammacorrect) ? imagegammacorrect($image, 1.0, $gammacorrect) : '';
-    
-    // Generate a white lattice in foreground
-    if ($foreground_lattice_y)
-    {
-      // x lines
-      $ih = round($total_height / $foreground_lattice_y);
-      for ($i = 0; $i <= $ih; $i++)
-      {
-        imageline($image, 0, $i*$foreground_lattice_y, $total_width, $i*$foreground_lattice_y, $lattice_color);
-      }
-    }
-    if ($foreground_lattice_x)
-    {
-      // y lines
-      $iw = round($total_width / $foreground_lattice_x);
-      for ($i = 0; $i <= $iw; $i++)
-      {
-        imageline($image, $i*$foreground_lattice_x, 0, $i*$foreground_lattice_x, $total_height, $lattice_color);
-      }
-    }
-    
-    // Font debug
-    if ($font_debug && !$rnd_font)
-    {
-      imagestring($image, 5, 2, 0, $fonts[$font], $white);
-      imagestring($image, 5, 5, 0, $fonts[$font], $white);
-      imagestring($image, 5, 4, 2, $fonts[$font], $gray);
-      imagestring($image, 5, 3, 1, $fonts[$font], $black);
-    }
-    
-    // Display
-    header("Last-Modified: " . gmdate("D, d M Y H:i:s") ." GMT"); 
-    header("Pragma: no-cache"); 
-    header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate");
-    (!$jpeg) ? header("Content-Type: image/png") : header("Content-Type: image/jpeg");
-    
-    (!$jpeg) ? imagepng($image) : imagejpeg($image, '', $img_quality);
-    imagedestroy($image);
-  }
-  
-  // Function get_rgb by Frank Burian
-  // http://www.phpfuncs.org/?content=show&id=46
-  function get_rgb($hex) { 
-    $hex_array = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
-        'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 
-        'F' => 15); 
-    $hex = str_replace('#', '', strtoupper($hex)); 
-    if (($length = strlen($hex)) == 3) { 
-        $hex = $hex{0}.$hex{0}.$hex{1}.$hex{1}.$hex{2}.$hex{2}; 
-        $length = 6; 
-    } 
-    if ($length != 6 or strlen(str_replace(array_keys($hex_array), '', $hex))) 
-        return NULL; 
-    $rgb['r'] = $hex_array[$hex{0}] * 16 + $hex_array[$hex{1}]; 
-    $rgb['g'] = $hex_array[$hex{2}] * 16 + $hex_array[$hex{3}]; 
-    $rgb['b']= $hex_array[$hex{4}] * 16 + $hex_array[$hex{5}]; 
-    return $rgb['r'].','.$rgb['g'].','.$rgb['b']; 
-  }
-  
-  // Function  gdVersion by Hagan Fox
-  // http://de3.php.net/manual/en/function.gd-info.php#52481
-  function gdVersion($user_ver = 0)
-  {
-     if (! extension_loaded('gd')) { return; }
-     static $gd_ver = 0;
-     // Just accept the specified setting if it's 1.
-     if ($user_ver == 1) { $gd_ver = 1; return 1; }
-     // Use the static variable if function was called previously.
-     if ($user_ver !=2 && $gd_ver > 0 ) { return $gd_ver; }
-     // Use the gd_info() function if possible.
-     if (function_exists('gd_info')) {
-         $ver_info = gd_info();
-         preg_match('/\d/', $ver_info['GD Version'], $match);
-         $gd_ver = $match[0];
-         return $match[0];
-     }
-     // If phpinfo() is disabled use a specified / fail-safe choice...
-     if (preg_match('/phpinfo/', ini_get('disable_functions'))) {
-         if ($user_ver == 2) {
-             $gd_ver = 2;
-             return 2;
-         } else {
-             $gd_ver = 1;
-             return 1;
-         }
-     }
-     // ...otherwise use phpinfo().
-     ob_start();
-     phpinfo(8);
-     $info = ob_get_contents();
-     ob_end_clean();
-     $info = stristr($info, 'gd version');
-     preg_match('/\d/', $info, $match);
-     $gd_ver = $match[0];
-     return $match[0];
-  }
+	var $captcha_config;
+	
+	function __construct($sid, $r = false)
+	{
+		parent::__construct($sid, $r);
+		
+		$hex = Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
+		$latticecolor = '#';
+		for ( $i = 0; $i < 6; $i++ )
+			$latticecolor .= $hex[mt_rand(0, 15)];
+		
+		$this->captcha_config = array (
+			'width' => '350',
+			'height' => '90',
+			'background_color' => '#E5ECF9',
+			'jpeg' => '0',
+			'jpeg_quality' => '95',
+			'pre_letters' => '1',
+			'pre_letters_great' => '0',
+			'font' => '1',
+			'chess' => '2',
+			'ellipses' => '2',
+			'arcs' => '2',
+			'lines' => '2',
+			'image' => '0',
+			'gammacorrect' => '0.8',
+			'foreground_lattice_x' => (string)mt_rand(25, 30),
+			'foreground_lattice_y' => (string)mt_rand(25, 30),
+			'lattice_color' => $latticecolor,
+		);
+	}
+	
+	function make_image()
+	{
+		$code = $this->get_code();
+		
+		// Prefs
+		$total_width = $this->captcha_config['width'];
+		$total_height = $this->captcha_config['height'];
+		
+		$hex_bg_color = $this->get_rgb($this->captcha_config['background_color']);
+		$bg_color = array();
+		$bg_color = explode(",", $hex_bg_color);
+		
+		$jpeg = $this->captcha_config['jpeg'];
+		$img_quality = $this->captcha_config['jpeg_quality'];
+		// Max quality is 95
+		
+		$pre_letters = $this->captcha_config['pre_letters'];
+		$pre_letter_great = $this->captcha_config['pre_letters_great'];
+		$rnd_font = $this->captcha_config['font'];
+		$chess = $this->captcha_config['chess'];
+		$ellipses = $this->captcha_config['ellipses'];
+		$arcs = $this->captcha_config['arcs'];
+		$lines = $this->captcha_config['lines'];
+		$image = $this->captcha_config['image'];
+		
+		$gammacorrect = $this->captcha_config['gammacorrect'];
+		
+		$foreground_lattice_y = $this->captcha_config['foreground_lattice_y'];
+		$foreground_lattice_x = $this->captcha_config['foreground_lattice_x'];
+		$hex_lattice_color = $this->get_rgb($this->captcha_config['lattice_color']);
+		$rgb_lattice_color = array();
+		$rgb_lattice_color = explode(",", $hex_lattice_color);
+		
+		$font_debug = false;
+		
+		// Fonts and images init
+		if ($image)
+		{
+			$bg_imgs = array();
+			if ($img_dir = opendir(ENANO_ROOT.'/includes/captcha/pics/'))
+			{
+				while (true == ($file = @readdir($img_dir))) 
+				{ 
+					if ((substr(strtolower($file), -3) == 'jpg') || (substr(strtolower($file), -3) == 'gif'))    
+					{         
+						$bg_imgs[] = $file; 
+					}     
+				}
+				closedir($img_dir);
+			}
+			// Grab a random Background Image or set FALSE if none was found
+			$bg_img = ( count($bg_imgs) ) ? rand(0, (count($bg_imgs)-1)) : false;
+		}
+		
+		$fonts = array();
+		if ($fonts_dir = opendir(ENANO_ROOT . '/includes/captcha/fonts/'))
+		{
+			while (true == ($file = @readdir($fonts_dir))) 
+			{ 
+				if ((substr(strtolower($file), strlen($file)-3, strlen($file)) == 'ttf'))
+				{
+					$fonts[] = $file; 
+				}     
+			}
+			closedir($fonts_dir);
+		} else {
+			die('Error reading directory: '.ENANO_ROOT.'/includes/captcha/fonts/');
+		}
+		$font = mt_rand(0, (count($fonts)-1));
+		
+		// Generate
+		$image = ($this->gdVersion() >= 2) ? imagecreatetruecolor($total_width, $total_height) : imagecreate($total_width, $total_height);
+		$background_color = imagecolorallocate($image, $bg_color[0], $bg_color[1], $bg_color[2]);
+		imagefill($image, 0, 0, $background_color);
+		
+		// Generate backgrund
+		if ($chess == '1' || $chess == '2' && rand(0,1))
+		{
+			// Draw rectangles
+			for($i = 0; $i <= 8; $i++)
+			{
+				$rectanglecolor = imagecolorallocate($image, rand(100,200),rand(100,200),rand(100,200));
+				imagefilledrectangle($image, 0, 0, round($total_width-($total_width/8*$i)), round($total_height), $rectanglecolor);
+				$rectanglecolor = imagecolorallocate($image, rand(100,200),rand(100,200),rand(100,200));
+				imagefilledrectangle($image, 0, 0, round($total_width-($total_width/8*$i)), round($total_height/2), $rectanglecolor);
+			}
+		}
+		if ($ellipses == '1' || $ellipses == '2' && rand(0,1))
+		{
+			// Draw random ellipses
+			for ($i = 1; $i <= 60; $i++)
+			{
+				$ellipsecolor = imagecolorallocate($image, rand(100,250),rand(100,250),rand(100,250));
+				imagefilledellipse($image, round(rand(0, $total_width)), round(rand(0, $total_height)), round(rand(0, $total_width/8)), round(rand(0, $total_height/4)), $ellipsecolor);	
+			}
+		}
+		if ($arcs == '1' || $arcs == '2' && rand(0,1))
+		{
+			// Draw random partial ellipses
+			for ($i = 0; $i <= 30; $i++)
+			{
+				$linecolor = imagecolorallocate($image, rand(120,255),rand(120,255),rand(120,255));
+				$cx = round(rand(1, $total_width));
+				$cy = round(rand(1, $total_height));
+				$int_w = round(rand(1, $total_width/2));
+				$int_h = round(rand(1, $total_height));
+				imagearc($image, $cx, $cy, $int_w, $int_h, round(rand(0, 190)), round(rand(191, 360)), $linecolor);
+				imagearc($image, $cx-1, $cy-1, $int_w, $int_h, round(rand(0, 190)), round(rand(191, 360)), $linecolor);
+			}
+		}
+		if ($lines == '1' || $lines == '2' && rand(0,1))
+		{
+			// Draw random lines
+			for ($i = 0; $i <= 50; $i++)
+			{
+				$linecolor = imagecolorallocate($image, rand(120,255),rand(120,255),rand(120,255));
+				imageline($image, round(rand(1, $total_width*3)), round(rand(1, $total_height*5)), round(rand(1, $total_width/2)), round(rand(1, $total_height*2)), $linecolor);
+			}
+		}
+		
+		$text_color_array = array('255,51,0', '51,77,255', '204,51,102', '0,153,0', '255,166,2', '255,0,255', '255,0,0', '0,255,0', '0,0,255', '0,255,255');
+		shuffle($text_color_array);
+		$pre_text_color_array = array('255,71,20', '71,20,224', '224,71,122', '20,173,20', '255,186,22', '25,25,25');
+		shuffle($pre_text_color_array);
+		$white = imagecolorallocate($image, 255, 255, 255);
+		$gray = imagecolorallocate($image, 100, 100, 100);
+		$black = imagecolorallocate($image, 0, 0, 0);
+		$lattice_color = imagecolorallocate($image, $rgb_lattice_color[0], $rgb_lattice_color[1], $rgb_lattice_color[2]);
+		
+		$x_char_position = (round(($total_width - 12) / strlen($code)) + mt_rand(-3, 5));
+		
+		for ($i = 0; $i < strlen($code); $i++)
+		{
+			mt_srand((double)microtime()*1000000);
+		
+			$char = $code{$i};
+			$size = mt_rand(floor($total_height / 3.5), ceil($total_height / 2.8));
+			$font = ($rnd_font) ? rand(0, (count($fonts)-1)) : $font;
+			$angle = mt_rand(-30, 30);
+		
+			$char_pos = array();
+			$char_pos = imagettfbbox($size, $angle, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
+			$letter_width = abs($char_pos[0]) + abs($char_pos[4]);
+			$letter_height = abs($char_pos[1]) + abs($char_pos[5]);
+		
+			$x_pos = ($x_char_position / 4) + ($i * $x_char_position);
+			($i == strlen($code)-1 && $x_pos >= ($total_width - ($letter_width + 5))) ? $x_pos = ($total_width - ($letter_width + 5)) : '';
+			$y_pos = mt_rand(($size * 1.4 ), $total_height - ($size * 0.4));
+		
+		//	Pre letters
+			$size = ($pre_letter_great) ? $size + (2 * $pre_letters) : $size - (2 * $pre_letters);
+			for ($count = 1; $count <= $pre_letters; $count++)
+			{
+				$pre_angle = $angle + mt_rand(-20, 20);
+		
+				$text_color = $pre_text_color_array[mt_rand(0,count($pre_text_color_array)-1)];
+				$text_color = explode(",", $text_color);
+				$textcolor = imagecolorallocate($image, $text_color[0], $text_color[1], $text_color[2]);
+		
+				imagettftext($image, $size, $pre_angle, $x_pos, $y_pos-2, $white, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
+				imagettftext($image, $size, $pre_angle, $x_pos+2, $y_pos, $black, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
+				imagettftext($image, $size, $pre_angle, $x_pos+1, $y_pos-1, $textcolor, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
+		
+				$size = ($pre_letter_great) ? $size - 2 : $size + 2;
+			}
+		
+		//	Final letters
+			$text_color = $text_color_array[mt_rand(0,count($text_color_array)-1)];
+			$text_color = explode(",", $text_color);
+			$textcolor = imagecolorallocate($image, $text_color[0], $text_color[1], $text_color[2]);
+		
+			imagettftext($image, $size, $angle, $x_pos, $y_pos-2, $white, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
+			imagettftext($image, $size, $angle, $x_pos+2, $y_pos, $black, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
+			imagettftext($image, $size, $angle, $x_pos+1, $y_pos-1, $textcolor, ENANO_ROOT.'/includes/captcha/fonts/'.$fonts[$font], $char);
+		}
+		
+		
+		($gammacorrect) ? imagegammacorrect($image, 1.0, $gammacorrect) : '';
+		
+		// Generate a white lattice in foreground
+		if ($foreground_lattice_y)
+		{
+			// x lines
+			$ih = round($total_height / $foreground_lattice_y);
+			for ($i = 0; $i <= $ih; $i++)
+			{
+				imageline($image, 0, $i*$foreground_lattice_y, $total_width, $i*$foreground_lattice_y, $lattice_color);
+			}
+		}
+		if ($foreground_lattice_x)
+		{
+			// y lines
+			$iw = round($total_width / $foreground_lattice_x);
+			for ($i = 0; $i <= $iw; $i++)
+			{
+				imageline($image, $i*$foreground_lattice_x, 0, $i*$foreground_lattice_x, $total_height, $lattice_color);
+			}
+		}
+		
+		// Font debug
+		if ($font_debug && !$rnd_font)
+		{
+			imagestring($image, 5, 2, 0, $fonts[$font], $white);
+			imagestring($image, 5, 5, 0, $fonts[$font], $white);
+			imagestring($image, 5, 4, 2, $fonts[$font], $gray);
+			imagestring($image, 5, 3, 1, $fonts[$font], $black);
+		}
+		
+		// Display
+		header("Last-Modified: " . gmdate("D, d M Y H:i:s") ." GMT"); 
+		header("Pragma: no-cache"); 
+		header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate");
+		(!$jpeg) ? header("Content-Type: image/png") : header("Content-Type: image/jpeg");
+		
+		(!$jpeg) ? imagepng($image) : imagejpeg($image, '', $img_quality);
+		imagedestroy($image);
+	}
+	
+	// Function get_rgb by Frank Burian
+	// http://www.phpfuncs.org/?content=show&id=46
+	function get_rgb($hex) { 
+		$hex_array = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
+				'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 
+				'F' => 15); 
+		$hex = str_replace('#', '', strtoupper($hex)); 
+		if (($length = strlen($hex)) == 3) { 
+				$hex = $hex{0}.$hex{0}.$hex{1}.$hex{1}.$hex{2}.$hex{2}; 
+				$length = 6; 
+		} 
+		if ($length != 6 or strlen(str_replace(array_keys($hex_array), '', $hex))) 
+				return NULL; 
+		$rgb['r'] = $hex_array[$hex{0}] * 16 + $hex_array[$hex{1}]; 
+		$rgb['g'] = $hex_array[$hex{2}] * 16 + $hex_array[$hex{3}]; 
+		$rgb['b']= $hex_array[$hex{4}] * 16 + $hex_array[$hex{5}]; 
+		return $rgb['r'].','.$rgb['g'].','.$rgb['b']; 
+	}
+	
+	// Function  gdVersion by Hagan Fox
+	// http://de3.php.net/manual/en/function.gd-info.php#52481
+	function gdVersion($user_ver = 0)
+	{
+ 		if (! extension_loaded('gd')) { return; }
+ 		static $gd_ver = 0;
+ 		// Just accept the specified setting if it's 1.
+ 		if ($user_ver == 1) { $gd_ver = 1; return 1; }
+ 		// Use the static variable if function was called previously.
+ 		if ($user_ver !=2 && $gd_ver > 0 ) { return $gd_ver; }
+ 		// Use the gd_info() function if possible.
+ 		if (function_exists('gd_info')) {
+ 				$ver_info = gd_info();
+ 				preg_match('/\d/', $ver_info['GD Version'], $match);
+ 				$gd_ver = $match[0];
+ 				return $match[0];
+ 		}
+ 		// If phpinfo() is disabled use a specified / fail-safe choice...
+ 		if (preg_match('/phpinfo/', ini_get('disable_functions'))) {
+ 				if ($user_ver == 2) {
+ 						$gd_ver = 2;
+ 						return 2;
+ 				} else {
+ 						$gd_ver = 1;
+ 						return 1;
+ 				}
+ 		}
+ 		// ...otherwise use phpinfo().
+ 		ob_start();
+ 		phpinfo(8);
+ 		$info = ob_get_contents();
+ 		ob_end_clean();
+ 		$info = stristr($info, 'gd version');
+ 		preg_match('/\d/', $info, $match);
+ 		$gd_ver = $match[0];
+ 		return $match[0];
+	}
 }
--- a/includes/clientside/css/enano-shared-ie.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/css/enano-shared-ie.css	Sun Mar 28 23:10:46 2010 -0400
@@ -3,27 +3,27 @@
  */
 
 * html span.tc_small_index_1 {
-  filter: alpha(opacity=100);
+	filter: alpha(opacity=100);
 }
 * html span.tc_small_index_2 {
-  filter: alpha(opacity=80);
+	filter: alpha(opacity=80);
 }
 * html span.tc_small_index_3 {
-  filter: alpha(opacity=60);
+	filter: alpha(opacity=60);
 }
 * html span.tc_small_index_4 {
-  filter: alpha(opacity=50);
+	filter: alpha(opacity=50);
 }
 * html span.tc_small_index_5 {
-  filter: alpha(opacity=45);
+	filter: alpha(opacity=45);
 }
 * html span.tc_small_index_6 {
-  filter: alpha(opacity=40);
+	filter: alpha(opacity=40);
 }
 * html span.tc_small_index_7 {
-  filter: alpha(opacity=35);
+	filter: alpha(opacity=35);
 }
 * html span.tc_small_index_8 {
-  filter: alpha(opacity=30);
+	filter: alpha(opacity=30);
 }
 
--- a/includes/clientside/css/enano-shared.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/css/enano-shared.css	Sun Mar 28 23:10:46 2010 -0400
@@ -17,143 +17,143 @@
 div.success-box-mini              { background-image: url(../../../images/mini-success.png);  background-position: 5px center; background-repeat: no-repeat; background-color: #F4FFF4; border: 1px solid #406080; padding: 4px 4px 4px 26px; margin: 1em 0; min-height: 17px; }
 
 a img {
-  border-width: 0;
+	border-width: 0;
 }
 
 /* Similar to the Mediawikian alert box (usermessage) */
 
 div.alert {
-  background-color: #F09090;
-  border: 1px solid #D03030;
-  color: #300000;
-  padding: 3px;
-  position: relative;
-  top: -3px;
+	background-color: #F09090;
+	border: 1px solid #D03030;
+	color: #300000;
+	padding: 3px;
+	position: relative;
+	top: -3px;
 }
 
 /* toolbar */
 div.toolbar {
-  border-bottom: 1px solid #909090;
-  background-color: #D0D0D0;
-  padding: 2px 0;
-  height: 22px;
-  font-family: arial, sans-serif;
-  font-size: 8pt;
+	border-bottom: 1px solid #909090;
+	background-color: #D0D0D0;
+	padding: 2px 0;
+	height: 22px;
+	font-family: arial, sans-serif;
+	font-size: 8pt;
 }
 div.toolbar ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar ul li {
-  list-style: none;
-  margin: 0;
-  float: left;
+	list-style: none;
+	margin: 0;
+	float: left;
 }
 div.toolbar a img {
-  opacity: 0.6;
-  /*filter: alpha(opacity=60);*/
+	opacity: 0.6;
+	/*filter: alpha(opacity=60);*/
 }
 div.toolbar a:hover img, div.toolbar a:focus img {
-  opacity: 1;
-  /*filter: alpha(opacity=100);*/
+	opacity: 1;
+	/*filter: alpha(opacity=100);*/
 }
 div.toolbar a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar a:hover, div.toolbar a:focus {
-  border: 1px solid #202090;
-  background-color: #ceceed;
-  color: #000000;
-  text-decoration: none;
+	border: 1px solid #202090;
+	background-color: #ceceed;
+	color: #000000;
+	text-decoration: none;
 }
 div.toolbar a:active {
-  border: 1px solid #A0A0A0;
-  background-color: #E0E0E0;
+	border: 1px solid #A0A0A0;
+	background-color: #E0E0E0;
 }
 div.toolbar img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar a span {
-  position: relative;
-  top: -4px;
+	position: relative;
+	top: -4px;
 }
 div.toolbar li span {
-  padding-left: 2px;
-  padding-right: 5px;
+	padding-left: 2px;
+	padding-right: 5px;
 }
 
 /* vertical toolbar */
 div.toolbar_vert {
-  border: 1px solid #909090;
-  background-color: #D0D0D0;
-  padding: 2px 0;
+	border: 1px solid #909090;
+	background-color: #D0D0D0;
+	padding: 2px 0;
 }
 div.toolbar_vert ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar_vert ul li {
-  list-style: none;
-  margin: 0;
+	list-style: none;
+	margin: 0;
 }
 div.toolbar_vert a img {
-  opacity: 0.6;
-  /*filter: alpha(opacity=60);*/
+	opacity: 0.6;
+	/*filter: alpha(opacity=60);*/
 }
 div.toolbar_vert a:hover img {
-  opacity: 1;
-  /*filter: alpha(opacity=100);*/
+	opacity: 1;
+	/*filter: alpha(opacity=100);*/
 }
 div.toolbar_vert a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar_vert a:hover {
-  border: 1px solid #202090;
-  background-color: #ceceed;
-  color: #000000;
-  text-decoration: none;
+	border: 1px solid #202090;
+	background-color: #ceceed;
+	color: #000000;
+	text-decoration: none;
 }
 div.toolbar_vert a:active {
-  border: 1px solid #A0A0A0;
-  background-color: #E0E0E0;
+	border: 1px solid #A0A0A0;
+	background-color: #E0E0E0;
 }
 div.toolbar_vert img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar_vert a span {
-  position: relative;
-  top: -4px;
+	position: relative;
+	top: -4px;
 }
 div.toolbar_vert li span {
-  padding-left: 2px;
-  padding-right: 5px;
+	padding-left: 2px;
+	padding-right: 5px;
 }
 
 div.toolbar_vert li > span {
-  display: block;
-  padding: 4px 5px;
+	display: block;
+	padding: 4px 5px;
 }
 
 div.breadcrumbs                   { margin: 10px 0; padding: 5px; border: 1px solid #AAAAAA; background-color: #E8E8E8; font-size: smaller; font-weight: bold; }
@@ -182,8 +182,8 @@
 
 /* Search results */
 div.search-result, div.search-hibar, div.search-lobar {
-  font-family: arial, helvetica, sans-serif;
-  font-size: 8pt;
+	font-family: arial, helvetica, sans-serif;
+	font-size: 8pt;
 }
 div.search-result h3   { font-size: 14pt; margin: 10px 0 0 0; }
 div.search-result h3 a { color: blue !important; font-weight: normal; padding-bottom: 0; }
@@ -201,41 +201,41 @@
  */
  
 input.js-search-box {
-  font-size: 13px;
-  margin: 0;
-  padding: 1px !important;
-  background-image: url(../../../images/search-box-normal.gif);
-  height: 15px;
-  background-repeat: repeat-x;
-  border-width: 1px;
-  border-style: solid;
-  border-color: #6c6c6c;
-  color: #C0C0C0;
+	font-size: 13px;
+	margin: 0;
+	padding: 1px !important;
+	background-image: url(../../../images/search-box-normal.gif);
+	height: 15px;
+	background-repeat: repeat-x;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #6c6c6c;
+	color: #C0C0C0;
 }
 
 input.js-search-box:focus {
-  background-image: url(../../../images/search-box-hilite.gif);
-  color: #666;
+	background-image: url(../../../images/search-box-hilite.gif);
+	color: #666;
 }
 
 div.js-search-submit {
-  display: block;
-  position: absolute;
-  width: 24px;
-  height: 19px;
-  font-size: 1px;
-  line-height: 19px;
-  clip: rect(0px, 24px, 19px, 0px);
-  overflow: hidden;
-  margin: 0;
-  padding: 0;
-  background: transparent url(../../../images/search-btn-normal.png) no-repeat !important;
-  background-repeat: no-repeat;
-  cursor: pointer;
+	display: block;
+	position: absolute;
+	width: 24px;
+	height: 19px;
+	font-size: 1px;
+	line-height: 19px;
+	clip: rect(0px, 24px, 19px, 0px);
+	overflow: hidden;
+	margin: 0;
+	padding: 0;
+	background: transparent url(../../../images/search-btn-normal.png) no-repeat !important;
+	background-repeat: no-repeat;
+	cursor: pointer;
 }
 
 div.js-search-submit:hover {
-  background-image: url(../../../images/search-btn-hilite.png);
+	background-image: url(../../../images/search-btn-hilite.png);
 }
 
 /*
@@ -243,105 +243,105 @@
  */
 
 div.menu, div.menu_nojs {
-  background-color: #D0D0D0;
-  border: 1px solid #A0A0A0;
-  font-size: 9pt;
+	background-color: #D0D0D0;
+	border: 1px solid #A0A0A0;
+	font-size: 9pt;
 }
 div.menu a, div.menu div.label {
-  padding: 2pt 5px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  color: #404040;
+	padding: 2pt 5px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	color: #404040;
 }
 div.menu_nojs a, div.menu_nojs div.label {
-  padding: 2pt 5px;
-  text-decoration: none;
-  display: block;
-  color: #404040;
+	padding: 2pt 5px;
+	text-decoration: none;
+	display: block;
+	color: #404040;
 }
 div.menu div.label, div.menu_nojs div.label {
-  color: #101010;
+	color: #101010;
 }
 div.menu span.sep, div.menu_nojs span.sep {
-  display: block;
-  float: left;
-  width: 5px;
+	display: block;
+	float: left;
+	width: 5px;
 }
 div.menu div.multopts, div.menu_nojs div.multopts {
-  line-height: 17pt;
+	line-height: 17pt;
 }
 div.menu div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts a, div.menu_nojs div.multopts div.label {
-  float: none;
-  display: inline;
+	float: none;
+	display: inline;
 }
 div.menu a:hover, div.menu_nojs a:hover {
-  color: #FFFFFF;
-  background-color: #808080;
+	color: #FFFFFF;
+	background-color: #808080;
 }
 div.menu input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="text"], div.menu_nojs input[type ^="password"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 4px 5px;
-  max-width: 70px;
-  background-color: #E0E0E0;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 4px 5px;
+	max-width: 70px;
+	background-color: #E0E0E0;
 }
 div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu_nojs input[type ^="password"]:hover {
-  background-color: #E8E8E8;
+	background-color: #E8E8E8;
 }
 div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu_nojs input[type ^="password"]:focus {
-  background-color: #F0F0F0;
+	background-color: #F0F0F0;
 }
 div.menu input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="button"], div.menu_nojs input[type ^="submit"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 3px 5px;
-  max-width: 70px;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 3px 5px;
+	max-width: 70px;
 }
 div.menu a.current, div.menu a.current:hover, div.menu_nojs a.current, div.menu_nojs a.current:hover {
-  color: #FFFFFF;
-  background-color: #505050;
+	color: #FFFFFF;
+	background-color: #505050;
 }
 div.menu ul {
-  display: none;
-  position: absolute;
-  padding: 0;
-  margin: 0;
-  background-color: #D0D0D0;
-  border: 1px solid #A0A0A0;
-  min-width: 120px;
+	display: none;
+	position: absolute;
+	padding: 0;
+	margin: 0;
+	background-color: #D0D0D0;
+	border: 1px solid #A0A0A0;
+	min-width: 120px;
 }
 div.menu_nojs ul {
-  display: block;
-  clear: both;
+	display: block;
+	clear: both;
 }
 div.menu ul li, div.menu_nojs ul li {
-  list-style: none;
+	list-style: none;
 }
 div.menu ul a, div.menu_nojs ul a {
-  float: none;
-  margin: 0;
+	float: none;
+	margin: 0;
 }
 span.menuclear {
-  font-size: 1px;
-  height: 0px;
-  width: 0px;
-  clear: left;
-  line-height: 0px;
-  display: block;
+	font-size: 1px;
+	height: 0px;
+	width: 0px;
+	clear: left;
+	line-height: 0px;
+	display: block;
 }
 
 /* Progress bars */
 div.progressbar {
-  padding: 2px;
-  background-color: #90A0B0;
-  width: 308px;
+	padding: 2px;
+	background-color: #90A0B0;
+	width: 308px;
 }
 div.progressbar_inner {
-  min-width: 30px;
-  color: white;
-  background-color: #7080A0;
-  padding: 4px;
+	min-width: 30px;
+	color: white;
+	background-color: #7080A0;
+	padding: 4px;
 }
 /* User notification - courtest of wikipedia.org (not sure if this is included with MediaWiki) */
 /* user notification thing */
@@ -355,26 +355,26 @@
 	vertical-align: middle;
 }
 .usermessage a:link, .usermessage a:active, .usermessage a:visited {
-  color: #CA7520;
+	color: #CA7520;
 }
 .usermessage a:hover {
-  color: #AA5500 !important;
+	color: #AA5500 !important;
 }
 .usermessage h2 {
-  border-bottom-color: #ef8500 !important;
-  color: black;
+	border-bottom-color: #ef8500 !important;
+	color: black;
 }
 div.thumbnail {
-  display: table;
-  border: 1px solid #AAAAAA;
-  background-color: #F0F0F0;
-  padding: 4px;
-  margin-bottom: 10px;
+	display: table;
+	border: 1px solid #AAAAAA;
+	background-color: #F0F0F0;
+	padding: 4px;
+	margin-bottom: 10px;
 }
 div.thumbnail-inner {
-  background-image: url(../../../images/thumbnail.png);
-  background-position: top right;
-  background-repeat: no-repeat;
+	background-image: url(../../../images/thumbnail.png);
+	background-position: top right;
+	background-repeat: no-repeat;
 }
 
 /* Tables where diffs are shown */
@@ -386,366 +386,366 @@
 
 /* Tag cloud */
 span.tc_word_normal {
-  font-family: Arial, helvetica, sans-serif;
-  font-size: 10pt;
-  letter-spacing: 3px;
-  padding: 4px 4px 4px 4px;
+	font-family: Arial, helvetica, sans-serif;
+	font-size: 10pt;
+	letter-spacing: 3px;
+	padding: 4px 4px 4px 4px;
 }
 span.tc_word_small {
-  font-family: Arial, helvetica, sans-serif;
-  font-size: 8pt;
-  color: #000;
+	font-family: Arial, helvetica, sans-serif;
+	font-size: 8pt;
+	color: #000;
 }
 span.tc_word_normal a, span.tc_word_small a {
-  color: inherit !important;
-  text-decoration: none !important;
+	color: inherit !important;
+	text-decoration: none !important;
 }
 /* These are from http://www.lotsofcode.com/php/tutorials/tag-cloud */
 span.tc_normal_index_1 {
-  color: #000;
-  font-size: 2.4em;
+	color: #000;
+	font-size: 2.4em;
 }
 span.tc_normal_index_2 {
-  color: #333;
-  font-size: 2.2em;
+	color: #333;
+	font-size: 2.2em;
 }
 span.tc_normal_index_3 {
-  color: #666;
-  font-size: 2.0em;
+	color: #666;
+	font-size: 2.0em;
 }
 span.tc_normal_index_4 {
-  color: #999;
-  font-size: 1em;
+	color: #999;
+	font-size: 1em;
 }
 span.tc_normal_index_5 {
-  color: #aaa;
-  font-size: 1.6em;
+	color: #aaa;
+	font-size: 1.6em;
 }
 span.tc_normal_index_6 {
-  color: #bbb;
-  font-size: 1.4em;
+	color: #bbb;
+	font-size: 1.4em;
 }
 span.tc_normal_index_7 {
-  color: #ccc;
-  font-size: 1.2em;
+	color: #ccc;
+	font-size: 1.2em;
 }
 span.tc_normal_index_8 {
-  color: #ddd;
-  font-size: 0.8em;
+	color: #ddd;
+	font-size: 0.8em;
 }
 span.tc_small_index_1 {
-  opacity: 1;
-  font-size: 1.4em;
+	opacity: 1;
+	font-size: 1.4em;
 }
 span.tc_small_index_2 {
-  opacity: 0.8;
-  font-size: 1.3em;
+	opacity: 0.8;
+	font-size: 1.3em;
 }
 span.tc_small_index_3 {
-  opacity: 0.6;
-  font-size: 1.2em;
+	opacity: 0.6;
+	font-size: 1.2em;
 }
 span.tc_small_index_4 {
-  opacity: 0.5;
-  font-size: 1em;
+	opacity: 0.5;
+	font-size: 1em;
 }
 span.tc_small_index_5 {
-  opacity: 0.45;
-  font-size: 1.3em;
+	opacity: 0.45;
+	font-size: 1.3em;
 }
 span.tc_small_index_6 {
-  opacity: 0.4;
-  font-size: 1.1em;
+	opacity: 0.4;
+	font-size: 1.1em;
 }
 span.tc_small_index_7 {
-  opacity: 0.35;
-  font-size: 0.9em;
+	opacity: 0.35;
+	font-size: 0.9em;
 }
 span.tc_small_index_8 {
-  opacity: 0.3;
-  font-size: 0.7em;
+	opacity: 0.3;
+	font-size: 0.7em;
 }
 
 /* Default private message AJAX interface styles (colors and style based on those of Gmail) */
 
 div#privmsgs {
-  /* Neal prefers this border but I personally consider it distasteful because it detracts from the Gmail-ey look.
-  border: 1px solid #c0c0c0; */
-  background-color: white;
-  color: black;
+	/* Neal prefers this border but I personally consider it distasteful because it detracts from the Gmail-ey look.
+	border: 1px solid #c0c0c0; */
+	background-color: white;
+	color: black;
 }
 
 span.pm_link {
-  color: #0000ff;
-  cursor: pointer;
-  text-decoration: underline;
+	color: #0000ff;
+	cursor: pointer;
+	text-decoration: underline;
 }
 
 span.pm_link_folder {
-  display: block;
-  text-decoration: none;
-  padding: 3px;
-  border-radius: 5px 0 0 5px;
-  -moz-border-radius: 5px 0 0 5px;
+	display: block;
+	text-decoration: none;
+	padding: 3px;
+	border-radius: 5px 0 0 5px;
+	-moz-border-radius: 5px 0 0 5px;
 }
 
 span.pm_link_selected {
-  background-color: #c3d9ff;
-  font-weight: bold;
-  text-decoration: underline;
+	background-color: #c3d9ff;
+	font-weight: bold;
+	text-decoration: underline;
 }
 
 span.pm_link_selected_trash {
-  background-color: #d9d9db;
+	background-color: #d9d9db;
 }
 
 div.pm_break {
-  height: 10px;
+	height: 10px;
 }
 
 span#pm_btn_compose {
-  margin-top: 5px;
+	margin-top: 5px;
 }
 
 div.pm_main {
-  background-color: #c3d9ff;
-  padding: 5px 5px 3px 5px;
-  margin-left: 12em;
-  min-height: 16em;
-  border-radius: 5px;
-  -moz-border-radius: 5px;
+	background-color: #c3d9ff;
+	padding: 5px 5px 3px 5px;
+	margin-left: 12em;
+	min-height: 16em;
+	border-radius: 5px;
+	-moz-border-radius: 5px;
 }
 
 div.pm_main_trash {
-  background-color: #d9d9db;
+	background-color: #d9d9db;
 }
 
 div.pm_status {
-  display: table;
-  background-color: #cc0000;
-  padding: 3px;
-  margin: 0 auto;
-  color: white;
+	display: table;
+	background-color: #cc0000;
+	padding: 3px;
+	margin: 0 auto;
+	color: white;
 }
 
 div.pm_teaser {
-  background-color: white;
-  color: black;
-  text-align: center;
-  padding: 8em 1em 8em 1em;
+	background-color: white;
+	color: black;
+	text-align: center;
+	padding: 8em 1em 8em 1em;
 }
 
 div.pm_mlist_message {
-  background-color: #e8eef7;
-  color: black;
-  border-bottom: 1px solid #d8d8d8;
-  cursor: pointer;
+	background-color: #e8eef7;
+	color: black;
+	border-bottom: 1px solid #d8d8d8;
+	cursor: pointer;
 }
 
 div.pm_mlist_message span.pm_subject {
-  font-weight: normal;
-  display: inline-block;
-  clip: rect(0px, auto, auto, 0px);
-  overflow: hidden;
-  margin: 0 3px 0 0;
+	font-weight: normal;
+	display: inline-block;
+	clip: rect(0px, auto, auto, 0px);
+	overflow: hidden;
+	margin: 0 3px 0 0;
 }
 
 div.pm_mlist_message span.pm_sender {
-  display: inline-block;
-  width: 30%;
-  margin: 0 10px 0% 4px;
-  font-weight: normal;
-  clip: rect(0px, auto, auto, 0px);
-  overflow: hidden;
+	display: inline-block;
+	width: 30%;
+	margin: 0 10px 0% 4px;
+	font-weight: normal;
+	clip: rect(0px, auto, auto, 0px);
+	overflow: hidden;
 }
 
 div.pm_mlist_message span.pm_miniclip {
-  color: #909090;
-  display: inline-block;
-  clip: rect(0px, auto, auto, 0px);
-  overflow: hidden;
+	color: #909090;
+	display: inline-block;
+	clip: rect(0px, auto, auto, 0px);
+	overflow: hidden;
 }
 
 div.pm_messagelist_inner {
-  min-height: 12em;
-  background-color: white;
+	min-height: 12em;
+	background-color: white;
 }
 
 div.pm_mlist_message_unread {
-  background-color: white;
+	background-color: white;
 }
 
 div.pm_mlist_message_unread span.pm_subject {
-  font-weight: bold;
+	font-weight: bold;
 }
 
 div.pm_mlist_message_unread span.pm_sender {
-  font-weight: bold;
+	font-weight: bold;
 }
 
 div.pm_mlist_message_selected {
-  background-color: #ffffcc;
+	background-color: #ffffcc;
 }
 
 span.pm_toolbar_label {
-  color: black;
-  font-weight: bold;
+	color: black;
+	font-weight: bold;
 }
 
 div.noborderbottom * {
-  border-bottom-width: 0px;
+	border-bottom-width: 0px;
 }
 
 div.nobordertop * {
-  border-top-width: 0px;
+	border-top-width: 0px;
 }
 
 /* Theme buttons in admin CP */
 
 div.themebutton {
-  width: 216px;
-  float: left;
-  background-position: center center;
-  background-repeat: no-repeat;
-  margin-right: 10px;
-  padding: 5px;
-  border: 1px solid #F0F0F0;
+	width: 216px;
+	float: left;
+	background-position: center center;
+	background-repeat: no-repeat;
+	margin-right: 10px;
+	padding: 5px;
+	border: 1px solid #F0F0F0;
 }
 
 div.themebutton_theme_disabled {
-  background-color: #D84308;
+	background-color: #D84308;
 }
 
 div.themebutton_theme_system {
-  display: none;
+	display: none;
 }
 
 div.themebutton a.tb-inner {
-  opacity: 0;
-  filter: alpha(opacity=0);
-  display: block;
-  height: 110px;
-  padding-top: 40px;
-  text-align: center;
-  font-size: 40px;
-  text-decoration: none;
+	opacity: 0;
+	filter: alpha(opacity=0);
+	display: block;
+	height: 110px;
+	padding-top: 40px;
+	text-align: center;
+	font-size: 40px;
+	text-decoration: none;
 }
 div.themebutton_theme_system a.tb-inner {
-  font-size: 28px;
-  height: 100px;
-  padding-top: 50px;
+	font-size: 28px;
+	height: 100px;
+	padding-top: 50px;
 }
 div.themebutton a.tb-inner:hover {
-  opacity: 0.75;
-  filter: alpha(opacity=75);
-  background-color: #ffffff;
+	opacity: 0.75;
+	filter: alpha(opacity=75);
+	background-color: #ffffff;
 }
 div.themebutton a.tb-inner span.themename {
-  font-size: 8pt;
-  color: #606060;
-  display: block;
+	font-size: 8pt;
+	color: #606060;
+	display: block;
 }
 
 div.themebutton div.status {
-  opacity: 0.75;
-  filter: alpha(opacity=75);
-  background-image: url(../../../images/loading-big.gif);
-  background-repeat: no-repeat;
-  background-position: center center;
-  background-color: #ffffff;
-  height: 150px;
+	opacity: 0.75;
+	filter: alpha(opacity=75);
+	background-image: url(../../../images/loading-big.gif);
+	background-repeat: no-repeat;
+	background-position: center center;
+	background-color: #ffffff;
+	height: 150px;
 }
 
 /* Expandable fieldsets */
 
 fieldset legend a.expander {
-  padding-left: 11px;
-  background-position: left center;
-  background-repeat: no-repeat;
-  color: inherit;
-  cursor: pointer;
+	padding-left: 11px;
+	background-position: left center;
+	background-repeat: no-repeat;
+	color: inherit;
+	cursor: pointer;
 }
 
 fieldset legend a.expander:hover {
-  color: inherit;
+	color: inherit;
 }
 
 fieldset legend a.expander-closed {
-  background-image: url(../../../images/expander/closed.gif);
-  
+	background-image: url(../../../images/expander/closed.gif);
+	
 }
 
 fieldset legend a.expander-closed:hover {
-  background-image: url(../../../images/expander/closed-prelight.gif);
+	background-image: url(../../../images/expander/closed-prelight.gif);
 }
 
 fieldset legend a.expander-open {
-  background-image: url(../../../images/expander/open.gif);
-  padding-left: 17px;
+	background-image: url(../../../images/expander/open.gif);
+	padding-left: 17px;
 }
 
 fieldset legend a.expander-open:hover {
-  background-image: url(../../../images/expander/open-prelight.gif);
+	background-image: url(../../../images/expander/open-prelight.gif);
 }
 
 /* Flown-in mini prompts */
 
 div.miniprompt {
-  position: absolute;
-  z-index: 999;
+	position: absolute;
+	z-index: 999;
 }
 
 div.miniprompt div.mp-top, div.miniprompt div.mp-bottom {
-  width: 388px;
-  height: 57px;
-  background-image: url(../../../images/prompt-top.png);
-  background-repeat: no-repeat;
-  background-position: center center;
+	width: 388px;
+	height: 57px;
+	background-image: url(../../../images/prompt-top.png);
+	background-repeat: no-repeat;
+	background-position: center center;
 }
 
 div.miniprompt div.mp-bottom {
-  height: 42px;
-  background-image: url(../../../images/prompt-bottom.png);
+	height: 42px;
+	background-image: url(../../../images/prompt-bottom.png);
 }
 
 div.miniprompt div.mp-body {
-  padding: 0 44px 10px 44px;
-  width: 300px;
-  background-image: url(../../../images/prompt-body.png);
-  background-repeat: repeat-y;
-  background-position: center center;
+	padding: 0 44px 10px 44px;
+	width: 300px;
+	background-image: url(../../../images/prompt-body.png);
+	background-repeat: repeat-y;
+	background-position: center center;
 }
 
 div.miniprompt h3 {
-  /* fix padding issues on firefox */
-  margin: 0 0 10px 0;
+	/* fix padding issues on firefox */
+	margin: 0 0 10px 0;
 }
 
 /* for buttons */
 div.miniprompt div.mp-buttons {
-  text-align: right;
-  position: relative;
-  top: 10px;
+	text-align: right;
+	position: relative;
+	top: 10px;
 }
 
 /* pseudo-buttons made with <a> tags */
 .abutton {
-  padding: 3px 5px;
-  background-color: #f0f0f0;
-  cursor: pointer;
-  margin: 0 3px;
-  text-decoration: none;
+	padding: 3px 5px;
+	background-color: #f0f0f0;
+	cursor: pointer;
+	margin: 0 3px;
+	text-decoration: none;
 }
 
 .abutton:hover {
-  color: #f0f0f0 !important;
-  background-color: #606060;
+	color: #f0f0f0 !important;
+	background-color: #606060;
 }
 
 .abutton.block {
-  display: block;
-  width: 60%;
-  margin: 0 auto 10px auto;
+	display: block;
+	width: 60%;
+	margin: 0 auto 10px auto;
 }
 
 .abutton_green       { color:            #008800 !important; }
@@ -756,46 +756,46 @@
 .abutton_red:hover   { background-color: #880000 !important; }
 
 .abutton_img, .abutton.icon {
-  background-image: url('../../../images/mini-error.png');
-  background-position: 4px center;
-  background-repeat: no-repeat;
-  padding-left: 24px;
+	background-image: url('../../../images/mini-error.png');
+	background-position: 4px center;
+	background-repeat: no-repeat;
+	padding-left: 24px;
 }
 
 /* User rank administration */
 
 div.rankadmin-left {
-  float: left;
-  border: 1px solid #e0e0e0;
-  margin: 0 1.4em 0 0;
-  padding: 0.6em;
+	float: left;
+	border: 1px solid #e0e0e0;
+	margin: 0 1.4em 0 0;
+	padding: 0.6em;
 }
 
 a.rankadmin-editlink {
-  display: block;
-  font-size: large;
-  padding: 3px;
-  text-decoration: none;
+	display: block;
+	font-size: large;
+	padding: 3px;
+	text-decoration: none;
 }
 
 a.rankadmin-createlink {
-  border-top: 1px solid #a0a0a0;
+	border-top: 1px solid #a0a0a0;
 }
 
 a.rankadmin-editlink:hover {
-  background-color: #f0f0f0;
+	background-color: #f0f0f0;
 }
 
 div.rankadmin-right {
-  float: left;
+	float: left;
 }
 
 .adminiconsprite {
-  width: 16px;
-  height: 16px;
-  background-image: url(../../../images/icons/applets/sprite.png);
-  background-position: center center;
-  background-repeat: no-repeat;
+	width: 16px;
+	height: 16px;
+	background-image: url(../../../images/icons/applets/sprite.png);
+	background-position: center center;
+	background-repeat: no-repeat;
 }
 
 /*
@@ -805,49 +805,49 @@
  */
  
 div.userpage_wrap {
-  /* Content starts at 4.05em */
-  position: relative;
-  top: 4em;
-  margin-bottom: 4em;
-  border: 0.05em solid #a0a0a0;
+	/* Content starts at 4.05em */
+	position: relative;
+	top: 4em;
+	margin-bottom: 4em;
+	border: 0.05em solid #a0a0a0;
 }
 
 ul.userpage_links {
-  top: -3.05em;
-  position: absolute;
-  padding-left: 10px;
-  list-style-type: none !important;
-  list-style-image: none !important;
+	top: -3.05em;
+	position: absolute;
+	padding-left: 10px;
+	list-style-type: none !important;
+	list-style-image: none !important;
 }
 
 ul.userpage_links li {
-  line-height: 1.85em;
-  border-width: 0.05em 0.05em 0 0.05em;
-  float: left;
-  margin-right: 5px;
-  padding: 0 7px;
-  list-style-type: none !important;
-  list-style-image: none !important;
-  border-style: solid;
-  border-color: #808080;
+	line-height: 1.85em;
+	border-width: 0.05em 0.05em 0 0.05em;
+	float: left;
+	margin-right: 5px;
+	padding: 0 7px;
+	list-style-type: none !important;
+	list-style-image: none !important;
+	border-style: solid;
+	border-color: #808080;
 }
 
 ul.userpage_links li.userpage_tab_active {
-  border-bottom-color: #ffffff;
-  border-bottom-width: 0.05em;
-  line-height: 2.05em;
-  margin-top: -0.2em;
-  font-weight: bold;
+	border-bottom-color: #ffffff;
+	border-bottom-width: 0.05em;
+	line-height: 2.05em;
+	margin-top: -0.2em;
+	font-weight: bold;
 }
 
 ul.userpage_links li:hover {
-  border-bottom-color: #ffffff;
-  cursor: pointer;
+	border-bottom-color: #ffffff;
+	cursor: pointer;
 }
 
 div.userpage_block {
-  clear: both;
-  padding: 10px;
+	clear: both;
+	padding: 10px;
 }
 
 /*
@@ -855,12 +855,12 @@
  */
 
 div.acl_inherit {
-  padding: 5px;
-  margin-bottom: 1px;
+	padding: 5px;
+	margin-bottom: 1px;
 }
 
 td.acl_inherit_key {
-  width: 15px;
+	width: 15px;
 }
 
 .acl_enano_default   { background-color: #FFFFC6; }
@@ -875,12 +875,12 @@
 .acl_local_user      { background-color: #FFB6B6; }
 
 span.acl_failed_deps {
-  font-weight: bold;
-  font-size: smaller;
+	font-weight: bold;
+	font-size: smaller;
 }
 
 span.acl_failed_deps span.title {
-  color: #ff0000;
+	color: #ff0000;
 }
 
 /**
@@ -888,84 +888,84 @@
  */
 
 div#theme-selector-wrapper {
-  position: absolute;
-  width: 100%;
-  margin: 0;
-  padding: 0;
-  top: 0;
-  margin-top: 75px;
+	position: absolute;
+	width: 100%;
+	margin: 0;
+	padding: 0;
+	top: 0;
+	margin-top: 75px;
 }
 
 div#theme-selector-body {
-  margin: 0 auto;
-  padding: 20px;
-  background-color: #ffffff;
-  text-align: center;
-  /* width: 708px; */
-  width: 130px;
-  height: 130px;
+	margin: 0 auto;
+	padding: 20px;
+	background-color: #ffffff;
+	text-align: center;
+	/* width: 708px; */
+	width: 130px;
+	height: 130px;
 }
 
 div#theme-selector-inner h3 {
-  font-size: x-large;
+	font-size: x-large;
 }
 
 div#theme-selector-inner ul {
-  list-style-type: none;
-  margin: 0;
-  padding: 0;
+	list-style-type: none;
+	margin: 0;
+	padding: 0;
 }
 
 div#theme-selector-inner ul li {
-  float: left;
+	float: left;
 }
 
 div#theme-selector-inner ul li a {
-  display: block;
-  border: 1px solid #d0d0d0;
-  padding: 4px;
-  width: 216px;
-  line-height: 150px;
-  text-align: center;
-  margin: 2px 5px;
-  text-decoration: none;
-  background-position: center center;
-  background-repeat: no-repeat;
-  background-image: url(../../../images/themepreview.png);
+	display: block;
+	border: 1px solid #d0d0d0;
+	padding: 4px;
+	width: 216px;
+	line-height: 150px;
+	text-align: center;
+	margin: 2px 5px;
+	text-decoration: none;
+	background-position: center center;
+	background-repeat: no-repeat;
+	background-image: url(../../../images/themepreview.png);
 }
 
 div#theme-selector-inner ul li a span {
-  color: #456798;
-  background-color: #fff;
-  opacity: 0;
-  filter: alpha(opacity=0);
-  display: block;
-  width: 216px;
-  line-height: 150px;
-  font-size: x-large;
+	color: #456798;
+	background-color: #fff;
+	opacity: 0;
+	filter: alpha(opacity=0);
+	display: block;
+	width: 216px;
+	line-height: 150px;
+	font-size: x-large;
 }
 
 div#theme-selector-inner ul li a:hover span {
-  opacity: 0.6;
-  filter: alpha(opacity=60);
+	opacity: 0.6;
+	filter: alpha(opacity=60);
 }
 
 div#theme-selector-inner .abutton {
-  font-size: x-large;
+	font-size: x-large;
 }
 
 div#theme-selector-inner ul li a span.loading {
-  background-image: url(../../../images/loading-big.gif);
-  background-position: center center;
-  background-repeat: no-repeat;
+	background-image: url(../../../images/loading-big.gif);
+	background-position: center center;
+	background-repeat: no-repeat;
 }
 
 div.theme-selector-spinner {
-  height: 130px;
-  background-image: url(../../../images/loading-big.gif);
-  background-position: center center;
-  background-repeat: no-repeat;
-  margin: 0 auto;
+	height: 130px;
+	background-image: url(../../../images/loading-big.gif);
+	background-position: center center;
+	background-repeat: no-repeat;
+	margin: 0 auto;
 }
 
 /**
@@ -973,69 +973,69 @@
  */
 
 div.sbedit-block {
-  background-color: #f7f7f7;
-  border: 1px solid #c2c2c2;
-  padding: 4px;
-  width: 150px;
-  margin: 0 7px 5px 0;
+	background-color: #f7f7f7;
+	border: 1px solid #c2c2c2;
+	padding: 4px;
+	width: 150px;
+	margin: 0 7px 5px 0;
 }
 
 div.sbedit-block.disabled {
-  background-color: #ffe2e2;
-  border-color: #c7a1a1;
+	background-color: #ffe2e2;
+	border-color: #c7a1a1;
 }
 
 div.sbedit-handle {
-  background-color: #c7c7c7;
-  border: 1px solid #909090;
-  padding: 2px;
-  margin-bottom: 3px;
-  cursor: move;
+	background-color: #c7c7c7;
+	border: 1px solid #909090;
+	padding: 2px;
+	margin-bottom: 3px;
+	cursor: move;
 }
 
 div.sbedit-block.disabled div.sbedit-handle {
-  background-color: #c77272;
-  border-color: #aa6060;
+	background-color: #c77272;
+	border-color: #aa6060;
 }
 
 div.sbedit-handle input {
-  display: none;
-  width: 96%;
+	display: none;
+	width: 96%;
 }
 
 div.sbedit-float {
-  position: absolute;
-  top: 20px;
-  left: 20px;
+	position: absolute;
+	top: 20px;
+	left: 20px;
 }
 
 td.sbedit-column {
-  vertical-align: top;
-  /* 150 + 4*2 + 7 + 3 (the 3 being a trial-and-error computation) */
-  width: 168px;
+	vertical-align: top;
+	/* 150 + 4*2 + 7 + 3 (the 3 being a trial-and-error computation) */
+	width: 168px;
 }
 
 .ui-sortable-placeholder {
-  background-color: #e2e2e2 !important;
-  border: 1px dashed #b7b7b7 !important;
-  visibility: visible !important;
-  height: 50px !important;
+	background-color: #e2e2e2 !important;
+	border: 1px dashed #b7b7b7 !important;
+	visibility: visible !important;
+	height: 50px !important;
 }
 
 .ui-sortable-helper {
-  opacity: 0.6;
-  filter: alpha(opacity=60);
+	opacity: 0.6;
+	filter: alpha(opacity=60);
 }
 
 .emptymessage {
-  line-height: 140px;
-  color: #a0a0a0;
-  border-bottom-width: 0 !important;
-  text-align: center;
-  font-size: xx-large;
-  font-weight: normal;
+	line-height: 140px;
+	color: #a0a0a0;
+	border-bottom-width: 0 !important;
+	text-align: center;
+	font-size: xx-large;
+	font-weight: normal;
 }
 
 .log_addfilter {
-  display: none;
+	display: none;
 }
--- a/includes/clientside/jscompress.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/jscompress.php	Sun Mar 28 23:10:46 2010 -0400
@@ -21,11 +21,11 @@
 // First check to see if something already declared this function.... it happens often.
 if ( !function_exists('microtime_float') )
 {
-  function microtime_float()
-  {
-    list($usec, $sec) = explode(" ", microtime());
-    return ((float)$usec + (float)$sec);
-  }
+	function microtime_float()
+	{
+		list($usec, $sec) = explode(" ", microtime());
+		return ((float)$usec + (float)$sec);
+	}
 }
 
 $local_start = microtime_float();
@@ -43,25 +43,25 @@
 // .enanodev is found in the Enano root (not /repo/).
 if ( strpos(__FILE__, '/repo/') && ( file_exists('../../.enanodev') || file_exists('../../../.enanodev') ) )
 {
-  // We have a development directory. Remove /repo/ from the picture.
-  $filename = str_replace('/repo/', '/', __FILE__);
+	// We have a development directory. Remove /repo/ from the picture.
+	$filename = str_replace('/repo/', '/', __FILE__);
 }
 else
 {
-  // Standard Enano installation
-  $filename = __FILE__;
+	// Standard Enano installation
+	$filename = __FILE__;
 }
 
 // ENANO_ROOT is sometimes defined by plugins like AjIM that need the constant before the Enano API is initialized
 if ( !defined('ENANO_ROOT') )
-  define('ENANO_ROOT', dirname(dirname(dirname($filename))));
+	define('ENANO_ROOT', dirname(dirname(dirname($filename))));
 
 chdir(ENANO_ROOT);
 
 require('includes/common.php');
 if ( !defined('ENANO_CLI') )
 {
-  die_friendly('Not for web use', '<p>This script is designed to be run from a command-line environment.</p>');
+	die_friendly('Not for web use', '<p>This script is designed to be run from a command-line environment.</p>');
 }
 
 // if ( !getConfig('cdn_path') )
@@ -79,7 +79,7 @@
 $path = ( isset($_SERVER['PATH']) ) ? $_SERVER['PATH'] : false;
 if ( !$path )
 {
-  die_semicritical('Can\'t get your PATH', 'Unable to get the PATH environment variable');
+	die_semicritical('Can\'t get your PATH', 'Unable to get the PATH environment variable');
 }
 
 $path = ( strtolower(PHP_OS) === 'win32' ) ? explode(';', $path) : explode(':', $path);
@@ -87,18 +87,18 @@
 
 foreach ( $path as $dir )
 {
-  if ( file_exists("$dir/zip$pathext") )
-  {
-    $have_zip = true;
-    break;
-  }
+	if ( file_exists("$dir/zip$pathext") )
+	{
+		$have_zip = true;
+		break;
+	}
 }
 
 if ( !$have_zip )
 {
-  // no zupport zor zipping ziles
-  echo "\x1B[31;1mnot found\x1B[0m\n\x1B[1mPlease install the zip utility using your distribution's package manager\nand then rerun this script.\x1B[0m";
-  exit(1);
+	// no zupport zor zipping ziles
+	echo "\x1B[31;1mnot found\x1B[0m\n\x1B[1mPlease install the zip utility using your distribution's package manager\nand then rerun this script.\x1B[0m";
+	exit(1);
 }
 
 echo "\x1B[1mall good\x1B[0m\n";
@@ -106,8 +106,8 @@
 
 if ( !@mkdir('includes/clientside/staticmin') )
 {
-  echo "\x1B[31;1mcouldn't create temp directory\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create includes/clientside/staticmin.\x1B[0m";
-  exit(1);
+	echo "\x1B[31;1mcouldn't create temp directory\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create includes/clientside/staticmin.\x1B[0m";
+	exit(1);
 }
 
 require('includes/clientside/jsres.php');
@@ -120,8 +120,8 @@
 $handle = @fopen('./enano-lib-basic.js', 'w');
 if ( !$handle )
 {
-  echo "\x1B[31;1mcouldn't open file\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create a file inside includes/clientside/staticmin.\x1B[0m";
-  exit(1);
+	echo "\x1B[31;1mcouldn't open file\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create a file inside includes/clientside/staticmin.\x1B[0m";
+	exit(1);
 }
 
 fwrite($handle, $everything);
@@ -130,29 +130,29 @@
 // for each JS file in includes/clientside/static, compress & write
 if ( $dr = @opendir('../static') )
 {
-  while ( $dh = @readdir($dr) )
-  {
-    if ( !preg_match('/\.js$/', $dh) || $dh === 'enano-lib-basic.js' )
-      continue;
-    
-    $contents = @file_get_contents("../static/$dh");
-    $compressed = jsres_cache_check($dh, $contents);
-    $compressed = str_replace('/* JavaScriptCompressor 0.8 [www.devpro.it], thanks to Dean Edwards for idea [dean.edwards.name] */' . "\r\n", '', $compressed);
-    
-    $handle = @fopen("./$dh", 'w');
-    if ( !$handle )
-    {
-      echo "\x1B[31;1mcouldn't open file\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create a file inside includes/clientside/staticmin.\x1B[0m";
-      exit(1);
-    }
-    fwrite($handle, $compressed);
-    fclose($handle);
-  }
+	while ( $dh = @readdir($dr) )
+	{
+		if ( !preg_match('/\.js$/', $dh) || $dh === 'enano-lib-basic.js' )
+			continue;
+		
+		$contents = @file_get_contents("../static/$dh");
+		$compressed = jsres_cache_check($dh, $contents);
+		$compressed = str_replace('/* JavaScriptCompressor 0.8 [www.devpro.it], thanks to Dean Edwards for idea [dean.edwards.name] */' . "\r\n", '', $compressed);
+		
+		$handle = @fopen("./$dh", 'w');
+		if ( !$handle )
+		{
+			echo "\x1B[31;1mcouldn't open file\x1B[0m\n\x1B[1mCheck permissions please, we couldn't create a file inside includes/clientside/staticmin.\x1B[0m";
+			exit(1);
+		}
+		fwrite($handle, $compressed);
+		fclose($handle);
+	}
 }
 else
 {
-  echo "\x1B[31;1mcouldn't open includes directory\x1B[0m\n\x1B[1mUnable to get our hands into includes/clientside/static/ to compress everything.\x1B[0m";
-  exit(1);
+	echo "\x1B[31;1mcouldn't open includes directory\x1B[0m\n\x1B[1mUnable to get our hands into includes/clientside/static/ to compress everything.\x1B[0m";
+	exit(1);
 }
 
 echo "\x1B[1mdone\x1B[0m\n";
@@ -161,9 +161,9 @@
 $result = system('zip -yrq9 ../enano-lib.zip *.js');
 if ( $result != 0 )
 {
-  // failure
-  echo "\x1B[31;1mzip creation failed\x1B[0m\n\x1B[1mzip returned result $result\x1B[0m";
-  exit(1);
+	// failure
+	echo "\x1B[31;1mzip creation failed\x1B[0m\n\x1B[1mzip returned result $result\x1B[0m";
+	exit(1);
 }
 
 echo "\x1B[1mdone\x1B[0m\n";
@@ -175,11 +175,11 @@
 
 if ( $dr = @opendir('./staticmin') )
 {
-  while ( $dh = @readdir($dr) )
-  {
-    if ( preg_match('/\.js$/', $dh) )
-      unlink("./staticmin/$dh");
-  }
+	while ( $dh = @readdir($dr) )
+	{
+		if ( preg_match('/\.js$/', $dh) )
+			unlink("./staticmin/$dh");
+	}
 }
 
 @rmdir('./staticmin');
--- a/includes/clientside/jsres.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/jsres.php	Sun Mar 28 23:10:46 2010 -0400
@@ -16,7 +16,7 @@
 
 // if Enano's already loaded, we've been included from a helper script
 if ( defined('ENANO_CONFIG_FETCHED') )
-  define('ENANO_JSRES_SETUP_ONLY', 1);
+	define('ENANO_JSRES_SETUP_ONLY', 1);
 
 if ( !defined('ENANO_JSRES_SETUP_ONLY') ):
 
@@ -29,11 +29,11 @@
 // First check to see if something already declared this function.... it happens often.
 if ( !function_exists('microtime_float') )
 {
-  function microtime_float()
-  {
-    list($usec, $sec) = explode(" ", microtime());
-    return ((float)$usec + (float)$sec);
-  }
+	function microtime_float()
+	{
+		list($usec, $sec) = explode(" ", microtime());
+		return ((float)$usec + (float)$sec);
+	}
 }
 
 $local_start = microtime_float();
@@ -51,18 +51,18 @@
 // development server using the script found on hg.enanocms.org.
 if ( strpos(__FILE__, '/repo/') && ( file_exists('../../.enanodev') || file_exists('../../../.enanodev') ) )
 {
-  // We have a development directory. Remove /repo/ from the picture.
-  $filename = str_replace('/repo/', '/', __FILE__);
+	// We have a development directory. Remove /repo/ from the picture.
+	$filename = str_replace('/repo/', '/', __FILE__);
 }
 else
 {
-  // Standard Enano installation
-  $filename = __FILE__;
+	// Standard Enano installation
+	$filename = __FILE__;
 }
 
 // ENANO_ROOT is sometimes defined by plugins like AjIM that need the constant before the Enano API is initialized
 if ( !defined('ENANO_ROOT') )
-  define('ENANO_ROOT', dirname(dirname(dirname($filename))));
+	define('ENANO_ROOT', dirname(dirname(dirname($filename))));
 
 chdir(ENANO_ROOT);
 
@@ -76,25 +76,25 @@
 
 // Files safe to run full (aggressive) compression on
 $full_compress_safe = array(
-  // Sorted by file size, descending (du -b *.js | sort -n)
-  'crypto.js',
-  'ajax.js',
-  'editor.js',
-  'functions.js',
-  'login.js',
-  'acl.js',
-  'misc.js',
-  'comments.js',
-  'autofill.js',
-  'dropdown.js',
-  'paginate.js',
-  'enano-lib-basic.js',
-  'pwstrength.js',
-  'flyin.js',
-  'rank-manager.js',
-  'userpage.js',
-  'template-compiler.js',
-  'toolbar.js',
+	// Sorted by file size, descending (du -b *.js | sort -n)
+	'crypto.js',
+	'ajax.js',
+	'editor.js',
+	'functions.js',
+	'login.js',
+	'acl.js',
+	'misc.js',
+	'comments.js',
+	'autofill.js',
+	'dropdown.js',
+	'paginate.js',
+	'enano-lib-basic.js',
+	'pwstrength.js',
+	'flyin.js',
+	'rank-manager.js',
+	'userpage.js',
+	'template-compiler.js',
+	'toolbar.js',
 );
 
 // Files that should NOT be compressed due to already being compressed, licensing, or invalid produced code
@@ -107,13 +107,13 @@
 $do_gzip = false;
 if ( isset($_SERVER['HTTP_ACCEPT_ENCODING']) && getConfig('gzip_output', false) == 1 )
 {
-  $acceptenc = str_replace(' ', '', strtolower($_SERVER['HTTP_ACCEPT_ENCODING']));
-  $acceptenc = explode(',', $acceptenc);
-  if ( in_array('gzip', $acceptenc) )
-  {
-    $do_gzip = true;
-    ob_start();
-  }
+	$acceptenc = str_replace(' ', '', strtolower($_SERVER['HTTP_ACCEPT_ENCODING']));
+	$acceptenc = explode(',', $acceptenc);
+	if ( in_array('gzip', $acceptenc) )
+	{
+		$do_gzip = true;
+		ob_start();
+	}
 }
 
 // Output format will always be JS
@@ -129,16 +129,16 @@
 // note - obfuscated for optimization purposes. The exact same code except properly indented is in enano-lib-basic.
 if ( isset($_GET['early']) )
 {
-  header('ETag: enanocms-lib-early-r3');
-  header('Expires: Wed, 1 Jan 2020 00:00:00 GMT');
-  
-  echo <<<JSEOF
+	header('ETag: enanocms-lib-early-r3');
+	header('Expires: Wed, 1 Jan 2020 00:00:00 GMT');
+	
+	echo <<<JSEOF
 window.loaded_components = window.loaded_components || {};
 window.onload_complete = false;
 var onload_hooks = new Array();function addOnloadHook(func){if ( typeof ( func ) == 'function' ){if ( typeof(onload_hooks.push) == 'function' ){onload_hooks.push(func);}else{onload_hooks[onload_hooks.length] = func;};};}
 JSEOF;
-  
-  exit();
+	
+	exit();
 }
 
 // Load and parse enano_lib_basic
@@ -149,7 +149,7 @@
 
 if ( !$pos_start_includes || !$pos_end_includes )
 {
-  die('// Error: enano-lib-basic does not have required metacomments');
+	die('// Error: enano-lib-basic does not have required metacomments');
 }
 
 $pos_end_includes += strlen('/*!END_INCLUDER*/');
@@ -157,16 +157,16 @@
 preg_match('/var thefiles = (\[([^\]]+?)\]);/', $file, $match);
 
 if ( empty($match) )
-  die('// Error: could not retrieve file list from enano-lib-basic');
+	die('// Error: could not retrieve file list from enano-lib-basic');
 
 // Decode file list
 try
 {
-  $file_list = enano_json_decode($match[1]);
+	$file_list = enano_json_decode($match[1]);
 }
 catch ( Exception $e )
 {
-  die("// Exception caught during file list parsing");
+	die("// Exception caught during file list parsing");
 }
 
 $apex = filemtime('includes/clientside/static/enano-lib-basic.js');
@@ -176,64 +176,64 @@
 
 if ( isset($_GET['f']) )
 {
-  // requested a single file
-  $js_file =& $_GET['f'];
-  if ( strstr($js_file, ',') )
-  {
-    $filelist = explode(',', $js_file);
-    unset($js_file);
-    $everything = '';
-    foreach ( $filelist as $js_file )
-    {
-      if ( !preg_match('/^[a-z0-9_-]+\.js$/i', $js_file) )
-      {
-        header('HTTP/1.1 404 Not Found');
-        exit('Not found');
-      }
-      
-      $apex = filemtime("includes/clientside/static/$js_file");
-      
-      $file_contents = file_get_contents("includes/clientside/static/$js_file");
-      $everything .= jsres_cache_check($js_file, $file_contents) . ' loaded_components[\'' . $js_file . '\'] = true;';
-    }
-    $everything .= 'if ( onload_complete ) { runOnloadHooks(); onload_hooks = []; };';
-  }
-  else
-  {
-    if ( !preg_match('/^[a-z0-9_-]+\.js$/i', $js_file) )
-    {
-      header('HTTP/1.1 404 Not Found');
-      exit('Not found');
-    }
-    
-    $apex = filemtime("includes/clientside/static/$js_file");
-    
-    $file_contents = file_get_contents("includes/clientside/static/$js_file");
-    $everything = jsres_cache_check($js_file, $file_contents) . ' loaded_components[\'' . $js_file . '\'] = true; if ( onload_complete ) { runOnloadHooks(); onload_hooks = []; };';
-  }
+	// requested a single file
+	$js_file =& $_GET['f'];
+	if ( strstr($js_file, ',') )
+	{
+		$filelist = explode(',', $js_file);
+		unset($js_file);
+		$everything = '';
+		foreach ( $filelist as $js_file )
+		{
+			if ( !preg_match('/^[a-z0-9_-]+\.js$/i', $js_file) )
+			{
+				header('HTTP/1.1 404 Not Found');
+				exit('Not found');
+			}
+			
+			$apex = filemtime("includes/clientside/static/$js_file");
+			
+			$file_contents = file_get_contents("includes/clientside/static/$js_file");
+			$everything .= jsres_cache_check($js_file, $file_contents) . ' loaded_components[\'' . $js_file . '\'] = true;';
+		}
+		$everything .= 'if ( onload_complete ) { runOnloadHooks(); onload_hooks = []; };';
+	}
+	else
+	{
+		if ( !preg_match('/^[a-z0-9_-]+\.js$/i', $js_file) )
+		{
+			header('HTTP/1.1 404 Not Found');
+			exit('Not found');
+		}
+		
+		$apex = filemtime("includes/clientside/static/$js_file");
+		
+		$file_contents = file_get_contents("includes/clientside/static/$js_file");
+		$everything = jsres_cache_check($js_file, $file_contents) . ' loaded_components[\'' . $js_file . '\'] = true; if ( onload_complete ) { runOnloadHooks(); onload_hooks = []; };';
+	}
 }
 else
 {
-  // compress enano-lib-basic
-  $libbasic = "$before_includes\n$after_includes";
-  $libbasic = jsres_cache_check('enano-lib-basic.js', $libbasic);
-  $everything .= $libbasic;
-  
-  // $everything .= $before_includes;
-  // $everything .= $after_includes;
-  
-  foreach ( $file_list as $js_file )
-  {
-    $file_contents = file_get_contents("includes/clientside/static/$js_file");
-    $time = filemtime("includes/clientside/static/$js_file");
-    if ( $time > $apex )
-      $apex = $time;
-    
-    $file_contents = jsres_cache_check($js_file, $file_contents);
-    
-    $everything .= "\n\n// $js_file\n";
-    $everything .= "\n" . $file_contents;
-  }
+	// compress enano-lib-basic
+	$libbasic = "$before_includes\n$after_includes";
+	$libbasic = jsres_cache_check('enano-lib-basic.js', $libbasic);
+	$everything .= $libbasic;
+	
+	// $everything .= $before_includes;
+	// $everything .= $after_includes;
+	
+	foreach ( $file_list as $js_file )
+	{
+		$file_contents = file_get_contents("includes/clientside/static/$js_file");
+		$time = filemtime("includes/clientside/static/$js_file");
+		if ( $time > $apex )
+			$apex = $time;
+		
+		$file_contents = jsres_cache_check($js_file, $file_contents);
+		
+		$everything .= "\n\n// $js_file\n";
+		$everything .= "\n" . $file_contents;
+	}
 }
 
 // generate ETag
@@ -241,11 +241,11 @@
 
 if ( isset($_SERVER['HTTP_IF_NONE_MATCH']) )
 {
-  if ( "\"$etag\"" == $_SERVER['HTTP_IF_NONE_MATCH'] )
-  {
-    header('HTTP/1.1 304 Not Modified');
-    exit();
-  }
+	if ( "\"$etag\"" == $_SERVER['HTTP_IF_NONE_MATCH'] )
+	{
+		header('HTTP/1.1 304 Not Modified');
+		exit();
+	}
 }
 
 // generate expires header
@@ -257,7 +257,7 @@
 
 if ( defined('ENANO_JSRES_SETUP_ONLY') )
 {
-  return; // we're done setting up, break out
+	return; // we're done setting up, break out
 }
 
 header("Date: $date");
@@ -265,7 +265,7 @@
 header("ETag: \"$etag\"");
 header("Expires: $expires");
 if ( !$do_gzip )
-  header("Content-Length: " . strlen($everything));
+	header("Content-Length: " . strlen($everything));
 
 $local_end = microtime_float();
 $local_gentime = $local_end - $local_start;
@@ -276,7 +276,7 @@
 
 if ( $do_gzip )
 {
-  gzip_output();
+	gzip_output();
 }
 
 /**
@@ -288,68 +288,68 @@
 
 function jsres_cache_check($js_file, $file_contents)
 {
-  global $full_compress_safe, $compress_unsafe;
-  global $disable_compress;
-  
-  if ( $disable_compress )
-    return $file_contents;
-  
-  $file_md5 = md5($file_contents);
-  
-  // Is this file cached?
-  $cache_path = ENANO_ROOT . "/cache/jsres_$js_file.json";
-  $loaded_cache = false;
-  
-  if ( file_exists($cache_path) )
-  {
-    // Load the cache file and parse it.
-    $cache_file = file_get_contents($cache_path);
-    try
-    {
-      $cache_file = enano_json_decode($cache_file);
-    }
-    catch ( Exception $e )
-    {
-      // Don't do anything - let our fallbacks come into place
-    }
-    if ( is_array($cache_file) && isset($cache_file['md5']) && isset($cache_file['src']) )
-    {
-      if ( $cache_file['md5'] === $file_md5 )
-      {
-        @header("X-Cache-Status: cache HIT, hash $file_md5");
-        $loaded_cache = true;
-        $file_contents = $cache_file['src'];
-      }
-    }
-  }
-  if ( !$loaded_cache && getConfig('cache_thumbs') == '1' )
-  {
-    // Try to open the cache file and write to it. If we can't do that, just don't compress the code.
-    $handle = @fopen($cache_path, 'w');
-    if ( $handle )
-    {
-      $aggressive = in_array($js_file, $full_compress_safe);
-      if ( !in_array($js_file, $compress_unsafe) )
-        $file_contents = perform_js_compress($file_contents, $aggressive);
-      
-      $payload = enano_json_encode(array(
-          'md5' => $file_md5,
-          'src' => $file_contents
-        ));
-      fwrite($handle, $payload);
-      fclose($handle);
-      @header("X-Cache-Status: cache MISS, new generated");
-    }
-    else
-    {
-      @header("X-Cache-Status: cache MISS, not generated");
-    }
-  }
-  else if ( !$loaded_cache )
-  {
-    @header("X-Cache-Status: cache MISS, not generated");
-  }
-  
-  return $file_contents;
+	global $full_compress_safe, $compress_unsafe;
+	global $disable_compress;
+	
+	if ( $disable_compress )
+		return $file_contents;
+	
+	$file_md5 = md5($file_contents);
+	
+	// Is this file cached?
+	$cache_path = ENANO_ROOT . "/cache/jsres_$js_file.json";
+	$loaded_cache = false;
+	
+	if ( file_exists($cache_path) )
+	{
+		// Load the cache file and parse it.
+		$cache_file = file_get_contents($cache_path);
+		try
+		{
+			$cache_file = enano_json_decode($cache_file);
+		}
+		catch ( Exception $e )
+		{
+			// Don't do anything - let our fallbacks come into place
+		}
+		if ( is_array($cache_file) && isset($cache_file['md5']) && isset($cache_file['src']) )
+		{
+			if ( $cache_file['md5'] === $file_md5 )
+			{
+				@header("X-Cache-Status: cache HIT, hash $file_md5");
+				$loaded_cache = true;
+				$file_contents = $cache_file['src'];
+			}
+		}
+	}
+	if ( !$loaded_cache && getConfig('cache_thumbs') == '1' )
+	{
+		// Try to open the cache file and write to it. If we can't do that, just don't compress the code.
+		$handle = @fopen($cache_path, 'w');
+		if ( $handle )
+		{
+			$aggressive = in_array($js_file, $full_compress_safe);
+			if ( !in_array($js_file, $compress_unsafe) )
+				$file_contents = perform_js_compress($file_contents, $aggressive);
+			
+			$payload = enano_json_encode(array(
+					'md5' => $file_md5,
+					'src' => $file_contents
+				));
+			fwrite($handle, $payload);
+			fclose($handle);
+			@header("X-Cache-Status: cache MISS, new generated");
+		}
+		else
+		{
+			@header("X-Cache-Status: cache MISS, not generated");
+		}
+	}
+	else if ( !$loaded_cache )
+	{
+		@header("X-Cache-Status: cache MISS, not generated");
+	}
+	
+	return $file_contents;
 }
 
--- a/includes/clientside/static/acl.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/acl.js	Sun Mar 28 23:10:46 2010 -0400
@@ -6,1836 +6,1836 @@
 
 function ajaxOpenACLManager(page_id, namespace)
 {
-  // touch these to make them available to child functions
-  void(page_id);
-  void(namespace);
-  
-  // require re-auth
-  if ( auth_level <= USER_LEVEL_MEMBER )
-  {
-    load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
-    ajaxDynamicReauth(function(key)
-      {
-        ajaxOpenACLManager(page_id, namespace);
-      }, user_level);
-    
-    return false;
-  }
-  
-  load_component(['l10n', 'messagebox', 'fadefilter', 'template-compiler', 'jquery', 'jquery-ui', 'autofill']);
-  
-  if(!page_id || !namespace)
-  {
-    var data = strToPageID(title);
-    var page_id = data[0];
-    var namespace = data[1];
-  }
-  var params = {
-      'mode' : 'listgroups',
-      'page_id' : page_id,
-      'namespace' : namespace
-    };
-  params = toJSONString(params);
-  params = ajaxEscape(params);
-  ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(ajax.responseText);
-          return false;
-        }
-        try {
-          var groups = parseJSON(ajax.responseText);
-        } catch(e) {
-          handle_invalid_json(ajax.responseText);
-        }
-        __aclBuildWizardWindow();
-        if ( groups.mode == 'error' )
-        {
-          alert(groups.error);
-          killACLManager();
-          return false;
-        }
-        aclDataCache = groups;
-        __aclBuildSelector(groups);
-      }
-    }, true);
-  return false;
+	// touch these to make them available to child functions
+	void(page_id);
+	void(namespace);
+	
+	// require re-auth
+	if ( auth_level <= USER_LEVEL_MEMBER )
+	{
+		load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
+		ajaxDynamicReauth(function(key)
+			{
+				ajaxOpenACLManager(page_id, namespace);
+			}, user_level);
+		
+		return false;
+	}
+	
+	load_component(['l10n', 'messagebox', 'fadefilter', 'template-compiler', 'jquery', 'jquery-ui', 'autofill']);
+	
+	if(!page_id || !namespace)
+	{
+		var data = strToPageID(title);
+		var page_id = data[0];
+		var namespace = data[1];
+	}
+	var params = {
+			'mode' : 'listgroups',
+			'page_id' : page_id,
+			'namespace' : namespace
+		};
+	params = toJSONString(params);
+	params = ajaxEscape(params);
+	ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(ajax.responseText);
+					return false;
+				}
+				try {
+					var groups = parseJSON(ajax.responseText);
+				} catch(e) {
+					handle_invalid_json(ajax.responseText);
+				}
+				__aclBuildWizardWindow();
+				if ( groups.mode == 'error' )
+				{
+					alert(groups.error);
+					killACLManager();
+					return false;
+				}
+				aclDataCache = groups;
+				__aclBuildSelector(groups);
+			}
+		}, true);
+	return false;
 }
 
 function ajaxOpenDirectACLRule(rule_id)
 {
-  load_component(['l10n', 'messagebox', 'fadefilter', 'template-compiler', 'autofill']);
-  
-  var params = {
-    target_id: rule_id,
-    mode: 'seltarget_id'
-  };
-  params = ajaxEscape(toJSONString(params));
-  ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(ajax.responseText);
-          return false;
-        }
-        try
-        {
-          response = parseJSON(response);
-        }
-        catch(e)
-        {
-          handle_invalid_json(response);
-        }
-        if ( !document.getElementById(aclManagerID) )
-        {
-          __aclBuildWizardWindow();
-          var main = document.getElementById(aclManagerID + '_main');
-          main.style.padding = '10px';
-        }
-        else
-        {
-          var main = document.getElementById(aclManagerID + '_main');
-          main.style.backgroundImage = 'none';
-        }
-        if ( response.mode == 'error' )
-        {
-          alert(response.error);
-          killACLManager();
-          return false;
-        }
-        aclDataCache = response;
-        aclBuildRuleEditor(response, true);
-      }
-    }, true);
+	load_component(['l10n', 'messagebox', 'fadefilter', 'template-compiler', 'autofill']);
+	
+	var params = {
+		target_id: rule_id,
+		mode: 'seltarget_id'
+	};
+	params = ajaxEscape(toJSONString(params));
+	ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(ajax.responseText);
+					return false;
+				}
+				try
+				{
+					response = parseJSON(response);
+				}
+				catch(e)
+				{
+					handle_invalid_json(response);
+				}
+				if ( !document.getElementById(aclManagerID) )
+				{
+					__aclBuildWizardWindow();
+					var main = document.getElementById(aclManagerID + '_main');
+					main.style.padding = '10px';
+				}
+				else
+				{
+					var main = document.getElementById(aclManagerID + '_main');
+					main.style.backgroundImage = 'none';
+				}
+				if ( response.mode == 'error' )
+				{
+					alert(response.error);
+					killACLManager();
+					return false;
+				}
+				aclDataCache = response;
+				aclBuildRuleEditor(response, true);
+			}
+		}, true);
 }
 
 function ajaxACLSwitchToSelector()
 {
-  params = {
-      'mode' : 'listgroups'
-    };
-  if ( aclDataCache.page_id && aclDataCache.namespace )
-  {
-    params.page_id   = aclDataCache.page_id;
-    params.namespace = aclDataCache.namespace;
-  }
-  params = toJSONString(params);
-  params = ajaxEscape(params);
-  ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        document.getElementById(aclManagerID+'_main').innerHTML = '';
-        document.getElementById(aclManagerID + '_back').style.display = 'none';
-        document.getElementById(aclManagerID + '_next').value = $lang.get('etc_wizard_next');
-        var groups = parseJSON(ajax.responseText);
-        if ( groups.mode == 'error' )
-        {
-          alert(groups.error);
-          killACLManager();
-          return false;
-        }
-        aclDataCache = groups;
-        thispage = strToPageID(title);
-        groups.page_id = thispage[0];
-        groups.namespace = thispage[1];
-        __aclBuildSelector(groups);
-      }
-    }, true);
+	params = {
+			'mode' : 'listgroups'
+		};
+	if ( aclDataCache.page_id && aclDataCache.namespace )
+	{
+		params.page_id   = aclDataCache.page_id;
+		params.namespace = aclDataCache.namespace;
+	}
+	params = toJSONString(params);
+	params = ajaxEscape(params);
+	ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				document.getElementById(aclManagerID+'_main').innerHTML = '';
+				document.getElementById(aclManagerID + '_back').style.display = 'none';
+				document.getElementById(aclManagerID + '_next').value = $lang.get('etc_wizard_next');
+				var groups = parseJSON(ajax.responseText);
+				if ( groups.mode == 'error' )
+				{
+					alert(groups.error);
+					killACLManager();
+					return false;
+				}
+				aclDataCache = groups;
+				thispage = strToPageID(title);
+				groups.page_id = thispage[0];
+				groups.namespace = thispage[1];
+				__aclBuildSelector(groups);
+			}
+		}, true);
 }
 
 function __aclBuildSelector(groups)
 {
-  thispage = strToPageID(title);
-  do_scopesel = ( thispage[0] == groups.page_id && thispage[1] == groups.namespace );
-  
-  document.getElementById(aclManagerID + '_next').style.display = 'inline';
-  
-  seed = Math.floor(Math.random() * 1000000);
-        
-  main = document.getElementById(aclManagerID + '_main');
-  main.style.padding = '10px';
-  main.style.backgroundImage = 'none';
-  
-  // the "edit existing" button
-  var editbtn_wrapper = document.createElement('div');
-  editbtn_wrapper.style.styleFloat = 'right';
-  editbtn_wrapper.style.cssFloat = 'right';
-  editbtn_wrapper.style.fontSize = 'smaller';
-  var editbtn = document.createElement('a');
-  editbtn.href = '#';
-  editbtn.innerHTML = $lang.get('acl_btn_show_existing');
-  editbtn_wrapper.appendChild(editbtn);
-  
-  // tracer button
-  var tracebtn = document.createElement('a');
-  tracebtn.href = '#';
-  tracebtn.innerHTML = $lang.get('acl_btn_view_effective');
-  editbtn_wrapper.appendChild(document.createElement('br'));
-  editbtn_wrapper.appendChild(tracebtn);
-  
-  main.appendChild(editbtn_wrapper);
-  
-  editbtn.onclick = function()
-  {
-    aclSetViewListExisting();
-    return false;
-  }
-  
-  tracebtn.onclick = function()
-  {
-    aclSetViewDebugTools();
-    return false;
-  }
-  
-  selector = document.createElement('div');
-  
-  var grpsel = __aclBuildGroupsHTML(groups);
-  grpsel.name = 'group_id';
-  
-  span = document.createElement('div');
-  span.id = "enACL_grpbox_"+seed+"";
-  
-  // Build the selector
-  grpb = document.createElement('input');
-  grpb.type = 'radio';
-  grpb.name  = 'target_type';
-  grpb.value = '1'; // ACL_TYPE_GROUP
-  grpb.checked = 'checked';
-  grpb.className = seed;
-  grpb.onclick = function() { seed = this.className; document.getElementById('enACL_grpbox_'+seed).style.display = 'block'; document.getElementById('enACL_usrbox_'+seed).style.display = 'none'; };
-  lbl = document.createElement('label');
-  lbl.appendChild(grpb);
-  lbl.appendChild(document.createTextNode($lang.get('acl_radio_usergroup')));
-  lbl.style.display = 'block';
-  span.appendChild(grpsel);
-  
-  anoninfo = document.createElement('div');
-  anoninfo.className = 'info-box-mini';
-  anoninfo.appendChild(document.createTextNode($lang.get('acl_msg_guest_howto')));
-  span.appendChild(document.createElement('br'));
-  span.appendChild(anoninfo);
-  
-  usrb = document.createElement('input');
-  usrb.type = 'radio';
-  usrb.name  = 'target_type';
-  usrb.value = '2'; // ACL_TYPE_USER
-  usrb.className = seed;
-  usrb.onclick = function() { seed = this.className; document.getElementById('enACL_grpbox_'+seed).style.display = 'none'; document.getElementById('enACL_usrbox_'+seed).style.display = 'block'; };
-  lbl2 = document.createElement('label');
-  lbl2.appendChild(usrb);
-  lbl2.appendChild(document.createTextNode($lang.get('acl_radio_user')));
-  lbl2.style.display = 'block';
-  
-  usrsel = document.createElement('input');
-  usrsel.type = 'text';
-  usrsel.name = 'username';
-  usrsel.className = 'autofill username';
-  usrsel.id = 'userfield_' + aclManagerID;
-  try {
-    usrsel.setAttribute("autocomplete","off");
-  } catch(e) {};
-  
-  span2 = document.createElement('div');
-  span2.id = "enACL_usrbox_"+seed+"";
-  span2.style.display = 'none';
-  span2.appendChild(usrsel);
-  
-  // Scope selector
-  if(do_scopesel)
-  {
-    scopediv1 = document.createElement('div');
-    scopediv2 = document.createElement('div');
-    scopediv3 = document.createElement('div');
-    scopeRadioPage = document.createElement('input');
-      scopeRadioPage.type = 'radio';
-      scopeRadioPage.name = 'scope';
-      scopeRadioPage.value = 'page';
-      scopeRadioPage.checked = 'checked';
-      scopeRadioPage.className = '1048576';
-      if ( groups.page_groups.length > 0 ) scopeRadioPage.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; };
-    scopeRadioGlobal = document.createElement('input');
-      scopeRadioGlobal.type = 'radio';
-      scopeRadioGlobal.name = 'scope';
-      scopeRadioGlobal.value = 'global';
-      scopeRadioGlobal.className = '1048576';
-      if ( groups.page_groups.length > 0 ) scopeRadioGlobal.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; };
-    scopeRadioGroup = document.createElement('input');
-      scopeRadioGroup.type = 'radio';
-      scopeRadioGroup.name = 'scope';
-      scopeRadioGroup.value = 'group';
-      scopeRadioGroup.className = '1048576';
-      if ( groups.page_groups.length > 0 ) scopeRadioGroup.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'block'; };
-    lblPage = document.createElement('label');
-      lblPage.style.display = 'block';
-      lblPage.appendChild(scopeRadioPage);
-      lblPage.appendChild(document.createTextNode($lang.get('acl_radio_scope_thispage')));
-    lblGlobal = document.createElement('label');
-      lblGlobal.style.display = 'block';
-      lblGlobal.appendChild(scopeRadioGlobal);
-      lblGlobal.appendChild(document.createTextNode($lang.get('acl_radio_scope_wholesite')));
-    lblGroup = document.createElement('label');
-      lblGroup.style.display = 'block';
-      lblGroup.appendChild(scopeRadioGroup);
-      lblGroup.appendChild(document.createTextNode($lang.get('acl_radio_scope_pagegroup')));
-    scopediv1.appendChild(lblPage);
-    scopediv2.appendChild(lblGroup);
-    scopediv3.appendChild(lblGlobal);
-    
-    scopedesc = document.createElement('p');
-    scopedesc.appendChild(document.createTextNode($lang.get('acl_lbl_scope')));
-    
-    scopePGrp = document.createElement('select');
-    scopePGrp.style.marginLeft = '13px';
-    scopePGrp.style.display = 'none';
-    scopePGrp.id = "enACL_pgsel_1048576";
-    
-    var opt;
-    for ( var i = 0; i < groups.page_groups.length; i++ )
-    {
-      opt = document.createElement('option');
-      opt.value = groups.page_groups[i].id;
-      opt.appendChild(document.createTextNode(groups.page_groups[i].name));
-      scopePGrp.appendChild(opt);
-    }
-    
-    scopediv2.appendChild(scopePGrp);
-    
-  }
-  
-  // Styles
-  span.style.marginLeft = '13px';
-  span.style.padding = '5px 0';
-  span2.style.marginLeft = '13px';
-  span2.style.padding = '5px 0';
-  
-  selector.appendChild(lbl);
-  selector.appendChild(span);
-  
-  selector.appendChild(lbl2);
-  selector.appendChild(span2);
-  
-  container = document.createElement('div');
-  container.style.margin = 'auto';
-  container.style.width = '360px';
-  container.style.paddingTop = '50px';
-  
-  head = document.createElement('h2');
-  head.appendChild(document.createTextNode($lang.get('acl_lbl_welcome_title')));
-  
-  desc = document.createElement('p');
-  desc.appendChild(document.createTextNode($lang.get('acl_lbl_welcome_body')));
-  
-  container.appendChild(head);
-  container.appendChild(desc);
-  container.appendChild(selector);
-  
-  if(do_scopesel)
-  {
-    container.appendChild(scopedesc);
-    container.appendChild(scopediv1);
-    if ( groups.page_groups.length > 0 )
-    {
-      container.appendChild(scopediv2);
-    }
-    container.appendChild(scopediv3);
-  }
-  
-  main.appendChild(container);
-  
-  var mode = document.createElement('input');
-  mode.name = 'mode';
-  mode.type = 'hidden';
-  mode.id = aclManagerID + '_mode';
-  mode.value = 'seltarget';
-  
-  var theform = document.getElementById(aclManagerID + '_formobj_id');
-  if ( !theform.mode )
-  {
-    theform.appendChild(mode);
-  }
-  else
-  {
-    theform.removeChild(theform.mode);
-    theform.appendChild(mode);
-  }
-  
-  autofill_init_element(usrsel, {
-      allow_anon: true
-    });
+	thispage = strToPageID(title);
+	do_scopesel = ( thispage[0] == groups.page_id && thispage[1] == groups.namespace );
+	
+	document.getElementById(aclManagerID + '_next').style.display = 'inline';
+	
+	seed = Math.floor(Math.random() * 1000000);
+				
+	main = document.getElementById(aclManagerID + '_main');
+	main.style.padding = '10px';
+	main.style.backgroundImage = 'none';
+	
+	// the "edit existing" button
+	var editbtn_wrapper = document.createElement('div');
+	editbtn_wrapper.style.styleFloat = 'right';
+	editbtn_wrapper.style.cssFloat = 'right';
+	editbtn_wrapper.style.fontSize = 'smaller';
+	var editbtn = document.createElement('a');
+	editbtn.href = '#';
+	editbtn.innerHTML = $lang.get('acl_btn_show_existing');
+	editbtn_wrapper.appendChild(editbtn);
+	
+	// tracer button
+	var tracebtn = document.createElement('a');
+	tracebtn.href = '#';
+	tracebtn.innerHTML = $lang.get('acl_btn_view_effective');
+	editbtn_wrapper.appendChild(document.createElement('br'));
+	editbtn_wrapper.appendChild(tracebtn);
+	
+	main.appendChild(editbtn_wrapper);
+	
+	editbtn.onclick = function()
+	{
+		aclSetViewListExisting();
+		return false;
+	}
+	
+	tracebtn.onclick = function()
+	{
+		aclSetViewDebugTools();
+		return false;
+	}
+	
+	selector = document.createElement('div');
+	
+	var grpsel = __aclBuildGroupsHTML(groups);
+	grpsel.name = 'group_id';
+	
+	span = document.createElement('div');
+	span.id = "enACL_grpbox_"+seed+"";
+	
+	// Build the selector
+	grpb = document.createElement('input');
+	grpb.type = 'radio';
+	grpb.name  = 'target_type';
+	grpb.value = '1'; // ACL_TYPE_GROUP
+	grpb.checked = 'checked';
+	grpb.className = seed;
+	grpb.onclick = function() { seed = this.className; document.getElementById('enACL_grpbox_'+seed).style.display = 'block'; document.getElementById('enACL_usrbox_'+seed).style.display = 'none'; };
+	lbl = document.createElement('label');
+	lbl.appendChild(grpb);
+	lbl.appendChild(document.createTextNode($lang.get('acl_radio_usergroup')));
+	lbl.style.display = 'block';
+	span.appendChild(grpsel);
+	
+	anoninfo = document.createElement('div');
+	anoninfo.className = 'info-box-mini';
+	anoninfo.appendChild(document.createTextNode($lang.get('acl_msg_guest_howto')));
+	span.appendChild(document.createElement('br'));
+	span.appendChild(anoninfo);
+	
+	usrb = document.createElement('input');
+	usrb.type = 'radio';
+	usrb.name  = 'target_type';
+	usrb.value = '2'; // ACL_TYPE_USER
+	usrb.className = seed;
+	usrb.onclick = function() { seed = this.className; document.getElementById('enACL_grpbox_'+seed).style.display = 'none'; document.getElementById('enACL_usrbox_'+seed).style.display = 'block'; };
+	lbl2 = document.createElement('label');
+	lbl2.appendChild(usrb);
+	lbl2.appendChild(document.createTextNode($lang.get('acl_radio_user')));
+	lbl2.style.display = 'block';
+	
+	usrsel = document.createElement('input');
+	usrsel.type = 'text';
+	usrsel.name = 'username';
+	usrsel.className = 'autofill username';
+	usrsel.id = 'userfield_' + aclManagerID;
+	try {
+		usrsel.setAttribute("autocomplete","off");
+	} catch(e) {};
+	
+	span2 = document.createElement('div');
+	span2.id = "enACL_usrbox_"+seed+"";
+	span2.style.display = 'none';
+	span2.appendChild(usrsel);
+	
+	// Scope selector
+	if(do_scopesel)
+	{
+		scopediv1 = document.createElement('div');
+		scopediv2 = document.createElement('div');
+		scopediv3 = document.createElement('div');
+		scopeRadioPage = document.createElement('input');
+			scopeRadioPage.type = 'radio';
+			scopeRadioPage.name = 'scope';
+			scopeRadioPage.value = 'page';
+			scopeRadioPage.checked = 'checked';
+			scopeRadioPage.className = '1048576';
+			if ( groups.page_groups.length > 0 ) scopeRadioPage.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; };
+		scopeRadioGlobal = document.createElement('input');
+			scopeRadioGlobal.type = 'radio';
+			scopeRadioGlobal.name = 'scope';
+			scopeRadioGlobal.value = 'global';
+			scopeRadioGlobal.className = '1048576';
+			if ( groups.page_groups.length > 0 ) scopeRadioGlobal.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; };
+		scopeRadioGroup = document.createElement('input');
+			scopeRadioGroup.type = 'radio';
+			scopeRadioGroup.name = 'scope';
+			scopeRadioGroup.value = 'group';
+			scopeRadioGroup.className = '1048576';
+			if ( groups.page_groups.length > 0 ) scopeRadioGroup.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'block'; };
+		lblPage = document.createElement('label');
+			lblPage.style.display = 'block';
+			lblPage.appendChild(scopeRadioPage);
+			lblPage.appendChild(document.createTextNode($lang.get('acl_radio_scope_thispage')));
+		lblGlobal = document.createElement('label');
+			lblGlobal.style.display = 'block';
+			lblGlobal.appendChild(scopeRadioGlobal);
+			lblGlobal.appendChild(document.createTextNode($lang.get('acl_radio_scope_wholesite')));
+		lblGroup = document.createElement('label');
+			lblGroup.style.display = 'block';
+			lblGroup.appendChild(scopeRadioGroup);
+			lblGroup.appendChild(document.createTextNode($lang.get('acl_radio_scope_pagegroup')));
+		scopediv1.appendChild(lblPage);
+		scopediv2.appendChild(lblGroup);
+		scopediv3.appendChild(lblGlobal);
+		
+		scopedesc = document.createElement('p');
+		scopedesc.appendChild(document.createTextNode($lang.get('acl_lbl_scope')));
+		
+		scopePGrp = document.createElement('select');
+		scopePGrp.style.marginLeft = '13px';
+		scopePGrp.style.display = 'none';
+		scopePGrp.id = "enACL_pgsel_1048576";
+		
+		var opt;
+		for ( var i = 0; i < groups.page_groups.length; i++ )
+		{
+			opt = document.createElement('option');
+			opt.value = groups.page_groups[i].id;
+			opt.appendChild(document.createTextNode(groups.page_groups[i].name));
+			scopePGrp.appendChild(opt);
+		}
+		
+		scopediv2.appendChild(scopePGrp);
+		
+	}
+	
+	// Styles
+	span.style.marginLeft = '13px';
+	span.style.padding = '5px 0';
+	span2.style.marginLeft = '13px';
+	span2.style.padding = '5px 0';
+	
+	selector.appendChild(lbl);
+	selector.appendChild(span);
+	
+	selector.appendChild(lbl2);
+	selector.appendChild(span2);
+	
+	container = document.createElement('div');
+	container.style.margin = 'auto';
+	container.style.width = '360px';
+	container.style.paddingTop = '50px';
+	
+	head = document.createElement('h2');
+	head.appendChild(document.createTextNode($lang.get('acl_lbl_welcome_title')));
+	
+	desc = document.createElement('p');
+	desc.appendChild(document.createTextNode($lang.get('acl_lbl_welcome_body')));
+	
+	container.appendChild(head);
+	container.appendChild(desc);
+	container.appendChild(selector);
+	
+	if(do_scopesel)
+	{
+		container.appendChild(scopedesc);
+		container.appendChild(scopediv1);
+		if ( groups.page_groups.length > 0 )
+		{
+			container.appendChild(scopediv2);
+		}
+		container.appendChild(scopediv3);
+	}
+	
+	main.appendChild(container);
+	
+	var mode = document.createElement('input');
+	mode.name = 'mode';
+	mode.type = 'hidden';
+	mode.id = aclManagerID + '_mode';
+	mode.value = 'seltarget';
+	
+	var theform = document.getElementById(aclManagerID + '_formobj_id');
+	if ( !theform.mode )
+	{
+		theform.appendChild(mode);
+	}
+	else
+	{
+		theform.removeChild(theform.mode);
+		theform.appendChild(mode);
+	}
+	
+	autofill_init_element(usrsel, {
+			allow_anon: true
+		});
 }
 
 var aclDebugWin = false;
 
 function aclDebug(text)
 {
-  if(!aclDebugWin)
-    aclDebugWin = pseudoWindowOpen("data:text/html;plain,<html><head><title>debug win</title></head><body><h1>Debug window</h1></body></html>", "aclDebugWin");
-    setTimeout(function() {
-  aclDebugWin.pre = aclDebugWin.document.createElement('pre');
-  aclDebugWin.pre.appendChild(aclDebugWin.document.createTextNode(text));
-  aclDebugWin.b = aclDebugWin.document.getElementsByTagName('body')[0];
-    aclDebugWin.b.appendChild(aclDebugWin.pre);}, 1000);
+	if(!aclDebugWin)
+		aclDebugWin = pseudoWindowOpen("data:text/html;plain,<html><head><title>debug win</title></head><body><h1>Debug window</h1></body></html>", "aclDebugWin");
+		setTimeout(function() {
+	aclDebugWin.pre = aclDebugWin.document.createElement('pre');
+	aclDebugWin.pre.appendChild(aclDebugWin.document.createTextNode(text));
+	aclDebugWin.b = aclDebugWin.document.getElementsByTagName('body')[0];
+		aclDebugWin.b.appendChild(aclDebugWin.pre);}, 1000);
 }
 
 var pseudoWindows = new Object();
 
 function pseudoWindowOpen(url, id)
 {
-  if(pseudoWindows[id])
-  {
-    document.getElementById('pseudowin_ifr_'+id).src = url;
-  }
-  else
-  {
-    win = document.createElement('iframe');
-    win.style.position='fixed';
-    win.style.width = '640px';
-    win.style.height = '480px';
-    win.style.top = '0px';
-    win.style.left = '0px';
-    win.style.zIndex = getHighestZ() + 1;
-    win.style.backgroundColor = '#FFFFFF';
-    win.name = 'pseudo_ifr_'+id;
-    win.id = 'pseudowindow_ifr_'+id;
-    win.src = url;
-    body = document.getElementsByTagName('body')[0];
-    body.appendChild(win);
-  }
-  win_obj = eval("( pseudo_ifr_"+id+" )");
-  return win_obj;
+	if(pseudoWindows[id])
+	{
+		document.getElementById('pseudowin_ifr_'+id).src = url;
+	}
+	else
+	{
+		win = document.createElement('iframe');
+		win.style.position='fixed';
+		win.style.width = '640px';
+		win.style.height = '480px';
+		win.style.top = '0px';
+		win.style.left = '0px';
+		win.style.zIndex = getHighestZ() + 1;
+		win.style.backgroundColor = '#FFFFFF';
+		win.name = 'pseudo_ifr_'+id;
+		win.id = 'pseudowindow_ifr_'+id;
+		win.src = url;
+		body = document.getElementsByTagName('body')[0];
+		body.appendChild(win);
+	}
+	win_obj = eval("( pseudo_ifr_"+id+" )");
+	return win_obj;
 }
 
 function __aclJSONSubmitAjaxHandler(params)
 {
-  params = toJSONString(params);
-  params = ajaxEscape(params);
-  ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(ajax.responseText);
-          return false;
-        }
-        try
-        {
-          var data = parseJSON(ajax.responseText);
-        }
-        catch(e)
-        {
-          handle_invalid_json(ajax.responseText);
-          return false;
-        }
-        aclDataCache = data;
-        switch(data.mode)
-        {
-          case 'seltarget':
-            
-            // Build the ACL edit form
-            aclBuildRuleEditor(data);
-            
-            break;
-          case 'success':
-            var note = document.createElement('div');
-            note.className = 'info-box';
-            note.style.marginLeft = '0';
-            var b = document.createElement('b');
-            b.appendChild(document.createTextNode($lang.get('acl_lbl_save_success_title')));
-            note.appendChild(b);
-            note.appendChild(document.createElement('br'));
-            note.appendChild(document.createTextNode($lang.get('acl_lbl_save_success_body', { target_name: data.target_name })));
-            note.appendChild(document.createElement('br'));
-            
-            /*
-            var a = document.createElement('a');
-            a.href = '#';
-            a.id = aclManagerID + '_btn_dismiss';
-            a.appendChild(document.createTextNode('[ ' + $lang.get('acl_btn_success_dismiss') + ' :'));
-            note.appendChild(a);
-            var a2 = document.createElement('a');
-            a2.href = '#';
-            a.id = aclManagerID + '_btn_close';
-            a2.appendChild(document.createTextNode(': ' + $lang.get('acl_btn_success_close') + ' ]'));
-            note.appendChild(a2);
-            */
-            
-            var a_dismiss = document.createElement('a');
-            a_dismiss.href = '#';
-            a_dismiss.appendChild(document.createTextNode('[ ' + $lang.get('acl_btn_success_dismiss') + ' :'));
-            note.appendChild(a_dismiss);
-            
-            var a_close = document.createElement('a');
-            a_close.href = '#';
-            a_close.appendChild(document.createTextNode(': ' + $lang.get('acl_btn_success_close') + ' ]'));
-            note.appendChild(a_close);
-            
-            document.getElementById(aclManagerID + '_main').insertBefore(note, document.getElementById(aclManagerID + '_main').firstChild);
-            
-            a_dismiss.setAttribute('onclick', 'var parent = this.parentNode.parentNode; parent.removeChild(this.parentNode); return false;');
-            a_close.setAttribute('onclick', 'killACLManager(); return false;');
-            
-            if ( !document.getElementById(aclManagerID+'_deletelnk') )
-            {
-              var p = document.createElement('p');
-              p.innerHTML = '<a href="#delete_acl_rule" onclick="if(confirm(\'' + $lang.get('acl_msg_deleterule_confirm') + '\')) __aclDeleteRule(); return false;" style="color: red;">' + $lang.get('acl_lbl_deleterule') + '</a>';
-              p.id = aclManagerID + '_deletelnk';
-              p.style.textAlign = 'right';
-              
-              document.getElementById(aclManagerID + '_main').appendChild(p);
-            }
-            
-            document.getElementById(aclManagerID+'_main').scrollTop = 0;
-            document.getElementById(aclManagerID+'_main').style.backgroundImage = 'none';
-                        
-            aclDataCache.mode = 'save_edit';
-            break;
-          case 'delete':
-            
-            params = {
-              'mode' : 'listgroups'
-            };
-          params = toJSONString(params);
-          params = ajaxEscape(params);
-          ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
-              if ( ajax.readyState == 4 && ajax.status == 200 )
-              {
-                document.getElementById(aclManagerID+'_main').innerHTML = '';
-                document.getElementById(aclManagerID + '_back').style.display = 'none';
-                document.getElementById(aclManagerID + '_next').value = $lang.get('etc_wizard_next');
-                ajaxACLSwitchToSelector();
-                
-                // note
-                var note = document.createElement('div');
-                note.className = 'info-box-mini';
-                note.appendChild(document.createTextNode($lang.get('acl_lbl_delete_success')));
-                
-                // button: dismiss note
-                var a_dismiss = document.createElement('a');
-                a_dismiss.href = '#';
-                a_dismiss.onclick = function()
-                {
-                  var p = this.parentNode;
-                  domOpacity(p, 100, 0, 500);
-                  window.setTimeout(function()
-                    {
-                      p.parentNode.removeChild(p);
-                    }, 600);
-                  return false;
-                }
-                a_dismiss.appendChild(document.createTextNode($lang.get('acl_btn_success_dismiss')));
-                note.appendChild(a_dismiss);
-                // add a space
-                note.appendChild(document.createTextNode(' / '));
-                
-                // button: dismiss note
-                var a_close = document.createElement('a');
-                a_close.href = '#';
-                a_close.onclick = function()
-                {
-                  killACLManager();
-                  return false;
-                }
-                a_close.appendChild(document.createTextNode($lang.get('acl_btn_success_close')));
-                note.appendChild(a_close);
-                
-                // style note
-                domObjChangeOpac(note, 0);
-                note.style.position = 'absolute';
-                // icon padding L + icon padding R + icon width + right padding + border width L + border width R
-                note.style.width = ($dynano(aclManagerID + '_main').Width() - ( 5 + 5 + 16 + 4 + 1 + 1 )) + 'px';
-                
-                // make tangible, then calculate height and position right above button panel
-                var panel = document.getElementById(aclManagerID + '_panel');
-                panel.parentNode.parentNode.appendChild(note);
-                note.style.top = '401px';
-                note.style.left = '0px';
-                
-                opacity(note, 0, 100, 500);
-              }
-            }, true);
-            
-            break;
-          case 'error':
-            alert("Server side processing error:\n"+data.error);
-            break;
-          case 'debug':
-            aclDebug(data.text);
-            break;
-          case 'list_existing':
-            aclSetViewListExistingRespond(data);
-            break;
-          case 'trace':
-            aclDrawTraceWrapper(data);
-            break;
-          default:
-            handle_invalid_json(ajax.responseText);
-            break;
-        }
-      }
-    }, true);
+	params = toJSONString(params);
+	params = ajaxEscape(params);
+	ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(ajax.responseText);
+					return false;
+				}
+				try
+				{
+					var data = parseJSON(ajax.responseText);
+				}
+				catch(e)
+				{
+					handle_invalid_json(ajax.responseText);
+					return false;
+				}
+				aclDataCache = data;
+				switch(data.mode)
+				{
+					case 'seltarget':
+						
+						// Build the ACL edit form
+						aclBuildRuleEditor(data);
+						
+						break;
+					case 'success':
+						var note = document.createElement('div');
+						note.className = 'info-box';
+						note.style.marginLeft = '0';
+						var b = document.createElement('b');
+						b.appendChild(document.createTextNode($lang.get('acl_lbl_save_success_title')));
+						note.appendChild(b);
+						note.appendChild(document.createElement('br'));
+						note.appendChild(document.createTextNode($lang.get('acl_lbl_save_success_body', { target_name: data.target_name })));
+						note.appendChild(document.createElement('br'));
+						
+						/*
+						var a = document.createElement('a');
+						a.href = '#';
+						a.id = aclManagerID + '_btn_dismiss';
+						a.appendChild(document.createTextNode('[ ' + $lang.get('acl_btn_success_dismiss') + ' :'));
+						note.appendChild(a);
+						var a2 = document.createElement('a');
+						a2.href = '#';
+						a.id = aclManagerID + '_btn_close';
+						a2.appendChild(document.createTextNode(': ' + $lang.get('acl_btn_success_close') + ' ]'));
+						note.appendChild(a2);
+						*/
+						
+						var a_dismiss = document.createElement('a');
+						a_dismiss.href = '#';
+						a_dismiss.appendChild(document.createTextNode('[ ' + $lang.get('acl_btn_success_dismiss') + ' :'));
+						note.appendChild(a_dismiss);
+						
+						var a_close = document.createElement('a');
+						a_close.href = '#';
+						a_close.appendChild(document.createTextNode(': ' + $lang.get('acl_btn_success_close') + ' ]'));
+						note.appendChild(a_close);
+						
+						document.getElementById(aclManagerID + '_main').insertBefore(note, document.getElementById(aclManagerID + '_main').firstChild);
+						
+						a_dismiss.setAttribute('onclick', 'var parent = this.parentNode.parentNode; parent.removeChild(this.parentNode); return false;');
+						a_close.setAttribute('onclick', 'killACLManager(); return false;');
+						
+						if ( !document.getElementById(aclManagerID+'_deletelnk') )
+						{
+							var p = document.createElement('p');
+							p.innerHTML = '<a href="#delete_acl_rule" onclick="if(confirm(\'' + $lang.get('acl_msg_deleterule_confirm') + '\')) __aclDeleteRule(); return false;" style="color: red;">' + $lang.get('acl_lbl_deleterule') + '</a>';
+							p.id = aclManagerID + '_deletelnk';
+							p.style.textAlign = 'right';
+							
+							document.getElementById(aclManagerID + '_main').appendChild(p);
+						}
+						
+						document.getElementById(aclManagerID+'_main').scrollTop = 0;
+						document.getElementById(aclManagerID+'_main').style.backgroundImage = 'none';
+												
+						aclDataCache.mode = 'save_edit';
+						break;
+					case 'delete':
+						
+						params = {
+							'mode' : 'listgroups'
+						};
+					params = toJSONString(params);
+					params = ajaxEscape(params);
+					ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function(ajax) {
+							if ( ajax.readyState == 4 && ajax.status == 200 )
+							{
+								document.getElementById(aclManagerID+'_main').innerHTML = '';
+								document.getElementById(aclManagerID + '_back').style.display = 'none';
+								document.getElementById(aclManagerID + '_next').value = $lang.get('etc_wizard_next');
+								ajaxACLSwitchToSelector();
+								
+								// note
+								var note = document.createElement('div');
+								note.className = 'info-box-mini';
+								note.appendChild(document.createTextNode($lang.get('acl_lbl_delete_success')));
+								
+								// button: dismiss note
+								var a_dismiss = document.createElement('a');
+								a_dismiss.href = '#';
+								a_dismiss.onclick = function()
+								{
+									var p = this.parentNode;
+									domOpacity(p, 100, 0, 500);
+									window.setTimeout(function()
+										{
+											p.parentNode.removeChild(p);
+										}, 600);
+									return false;
+								}
+								a_dismiss.appendChild(document.createTextNode($lang.get('acl_btn_success_dismiss')));
+								note.appendChild(a_dismiss);
+								// add a space
+								note.appendChild(document.createTextNode(' / '));
+								
+								// button: dismiss note
+								var a_close = document.createElement('a');
+								a_close.href = '#';
+								a_close.onclick = function()
+								{
+									killACLManager();
+									return false;
+								}
+								a_close.appendChild(document.createTextNode($lang.get('acl_btn_success_close')));
+								note.appendChild(a_close);
+								
+								// style note
+								domObjChangeOpac(note, 0);
+								note.style.position = 'absolute';
+								// icon padding L + icon padding R + icon width + right padding + border width L + border width R
+								note.style.width = ($dynano(aclManagerID + '_main').Width() - ( 5 + 5 + 16 + 4 + 1 + 1 )) + 'px';
+								
+								// make tangible, then calculate height and position right above button panel
+								var panel = document.getElementById(aclManagerID + '_panel');
+								panel.parentNode.parentNode.appendChild(note);
+								note.style.top = '401px';
+								note.style.left = '0px';
+								
+								opacity(note, 0, 100, 500);
+							}
+						}, true);
+						
+						break;
+					case 'error':
+						alert("Server side processing error:\n"+data.error);
+						break;
+					case 'debug':
+						aclDebug(data.text);
+						break;
+					case 'list_existing':
+						aclSetViewListExistingRespond(data);
+						break;
+					case 'trace':
+						aclDrawTraceWrapper(data);
+						break;
+					default:
+						handle_invalid_json(ajax.responseText);
+						break;
+				}
+			}
+		}, true);
 }
 
 function aclBuildRuleEditor(data, from_direct)
 {
-  var act_desc = ( data.type == 'new' ) ? $lang.get('acl_lbl_editwin_title_create') : $lang.get('acl_lbl_editwin_title_edit');
-  var target_type_t = ( data.target_type == 1 ) ? $lang.get('acl_target_type_group') : $lang.get('acl_target_type_user');
-  var target_name_t = data.target_name;
-  var scope_type = ( data.page_id == false && data.namespace == false ) ? $lang.get('acl_scope_type_wholesite') : ( data.namespace == '__PageGroup' ) ? $lang.get('acl_scope_type_pagegroup') : $lang.get('acl_scope_type_thispage');
-  
-  document.getElementById(aclManagerID + '_next').style.display = 'inline';
-  
-  html = '<h2>'+act_desc+'</h2>';
-  html += '<p>' + $lang.get('acl_lbl_editwin_body', { target_type: target_type_t, target: target_name_t, scope_type: scope_type }) + '</p>';
-  
-  // preset management
-  var load_flags = 'href="#" onclick="aclShowPresetLoader(); return false;"';
-  var save_flags = 'href="#" onclick="aclShowPresetSave(); return false;"';
-  html += '<div style="float: right;">';
-  html += $lang.get('acl_btn_edit_presets', { load_flags: load_flags, save_flags: save_flags });
-  html += '</div>';
-  html += '<div style="clear: both;"></div>';
-  
-  parser = new templateParser(data.template.acl_field_begin);
-  html += parser.run();
-  
-  cls = 'row2';
-  for(var i in data.acl_types)
-  {
-    if(typeof(data.acl_types[i]) == 'number')
-    {
-      cls = ( cls == 'row1' ) ? 'row2' : 'row1';
-      p = new templateParser(data.template.acl_field_item);
-      vars = new Object();
-      if ( data.acl_descs[i].match(/^([a-z0-9_]+)$/) )
-      {
-        vars['FIELD_DESC'] = $lang.get(data.acl_descs[i]);
-      }
-      else
-      {
-        vars['FIELD_DESC'] = data.acl_descs[i];
-      }
-      vars['FIELD_INHERIT_CHECKED'] = '';
-      vars['FIELD_DENY_CHECKED'] = '';
-      vars['FIELD_DISALLOW_CHECKED'] = '';
-      vars['FIELD_WIKIMODE_CHECKED'] = '';
-      vars['FIELD_ALLOW_CHECKED'] = '';
-      vars['FIELD_NAME'] = i;
-      if ( !data.current_perms[i] )
-      {
-        data.current_perms[i] = 'i';
-      }
-      switch(data.current_perms[i])
-      {
-        case 'i':
-        default:
-          vars['FIELD_INHERIT_CHECKED'] = 'checked="checked"';
-          break;
-        case 1:
-          vars['FIELD_DENY_CHECKED'] = 'checked="checked"';
-          break;
-        case 2:
-          vars['FIELD_DISALLOW_CHECKED'] = 'checked="checked"';
-          break;
-        case 3:
-          vars['FIELD_WIKIMODE_CHECKED'] = 'checked="checked"';
-          break;
-        case 4:
-          vars['FIELD_ALLOW_CHECKED'] = 'checked="checked"';
-          break;
-      }
-      vars['ROW_CLASS'] = cls;
-      p.assign_vars(vars);
-      html += p.run();
-    }
-  }
-  
-  var parser = new templateParser(data.template.acl_field_end);
-  html += parser.run();
-  
-  if(data.type == 'edit')
-    html += '<p id="'+aclManagerID+'_deletelnk" style="text-align: right;"><a href="#delete_acl_rule" onclick="if(confirm(\'' + $lang.get('acl_msg_deleterule_confirm') + '\')) __aclDeleteRule(); return false;" style="color: red;">' + $lang.get('acl_lbl_deleterule') + '</a></p>';
-  
-  var main = document.getElementById(aclManagerID + '_main');
-  main.innerHTML = html;
-  
-  var form = document.getElementById(aclManagerID + '_formobj_id');
-  
-  if ( from_direct )
-  {
-    var modeobj = document.getElementById(aclManagerID + '_mode');
-    modeobj.value = 'save_edit';
-  }
-  else
-  {
-    var modeobj = form_fetch_field(form, 'mode');
-    if ( modeobj )
-      modeobj.value = 'save_' + data.type;
-    else
-      alert('modeobj is invalid: '+modeobj);
-  }
-  
-  aclPermList = array_keys(data.acl_types);
-  
-  document.getElementById(aclManagerID + '_back').style.display = 'inline';
-  document.getElementById(aclManagerID + '_next').value = $lang.get('etc_save_changes');
+	var act_desc = ( data.type == 'new' ) ? $lang.get('acl_lbl_editwin_title_create') : $lang.get('acl_lbl_editwin_title_edit');
+	var target_type_t = ( data.target_type == 1 ) ? $lang.get('acl_target_type_group') : $lang.get('acl_target_type_user');
+	var target_name_t = data.target_name;
+	var scope_type = ( data.page_id == false && data.namespace == false ) ? $lang.get('acl_scope_type_wholesite') : ( data.namespace == '__PageGroup' ) ? $lang.get('acl_scope_type_pagegroup') : $lang.get('acl_scope_type_thispage');
+	
+	document.getElementById(aclManagerID + '_next').style.display = 'inline';
+	
+	html = '<h2>'+act_desc+'</h2>';
+	html += '<p>' + $lang.get('acl_lbl_editwin_body', { target_type: target_type_t, target: target_name_t, scope_type: scope_type }) + '</p>';
+	
+	// preset management
+	var load_flags = 'href="#" onclick="aclShowPresetLoader(); return false;"';
+	var save_flags = 'href="#" onclick="aclShowPresetSave(); return false;"';
+	html += '<div style="float: right;">';
+	html += $lang.get('acl_btn_edit_presets', { load_flags: load_flags, save_flags: save_flags });
+	html += '</div>';
+	html += '<div style="clear: both;"></div>';
+	
+	parser = new templateParser(data.template.acl_field_begin);
+	html += parser.run();
+	
+	cls = 'row2';
+	for(var i in data.acl_types)
+	{
+		if(typeof(data.acl_types[i]) == 'number')
+		{
+			cls = ( cls == 'row1' ) ? 'row2' : 'row1';
+			p = new templateParser(data.template.acl_field_item);
+			vars = new Object();
+			if ( data.acl_descs[i].match(/^([a-z0-9_]+)$/) )
+			{
+				vars['FIELD_DESC'] = $lang.get(data.acl_descs[i]);
+			}
+			else
+			{
+				vars['FIELD_DESC'] = data.acl_descs[i];
+			}
+			vars['FIELD_INHERIT_CHECKED'] = '';
+			vars['FIELD_DENY_CHECKED'] = '';
+			vars['FIELD_DISALLOW_CHECKED'] = '';
+			vars['FIELD_WIKIMODE_CHECKED'] = '';
+			vars['FIELD_ALLOW_CHECKED'] = '';
+			vars['FIELD_NAME'] = i;
+			if ( !data.current_perms[i] )
+			{
+				data.current_perms[i] = 'i';
+			}
+			switch(data.current_perms[i])
+			{
+				case 'i':
+				default:
+					vars['FIELD_INHERIT_CHECKED'] = 'checked="checked"';
+					break;
+				case 1:
+					vars['FIELD_DENY_CHECKED'] = 'checked="checked"';
+					break;
+				case 2:
+					vars['FIELD_DISALLOW_CHECKED'] = 'checked="checked"';
+					break;
+				case 3:
+					vars['FIELD_WIKIMODE_CHECKED'] = 'checked="checked"';
+					break;
+				case 4:
+					vars['FIELD_ALLOW_CHECKED'] = 'checked="checked"';
+					break;
+			}
+			vars['ROW_CLASS'] = cls;
+			p.assign_vars(vars);
+			html += p.run();
+		}
+	}
+	
+	var parser = new templateParser(data.template.acl_field_end);
+	html += parser.run();
+	
+	if(data.type == 'edit')
+		html += '<p id="'+aclManagerID+'_deletelnk" style="text-align: right;"><a href="#delete_acl_rule" onclick="if(confirm(\'' + $lang.get('acl_msg_deleterule_confirm') + '\')) __aclDeleteRule(); return false;" style="color: red;">' + $lang.get('acl_lbl_deleterule') + '</a></p>';
+	
+	var main = document.getElementById(aclManagerID + '_main');
+	main.innerHTML = html;
+	
+	var form = document.getElementById(aclManagerID + '_formobj_id');
+	
+	if ( from_direct )
+	{
+		var modeobj = document.getElementById(aclManagerID + '_mode');
+		modeobj.value = 'save_edit';
+	}
+	else
+	{
+		var modeobj = form_fetch_field(form, 'mode');
+		if ( modeobj )
+			modeobj.value = 'save_' + data.type;
+		else
+			alert('modeobj is invalid: '+modeobj);
+	}
+	
+	aclPermList = array_keys(data.acl_types);
+	
+	document.getElementById(aclManagerID + '_back').style.display = 'inline';
+	document.getElementById(aclManagerID + '_next').value = $lang.get('etc_save_changes');
 }
 
 function __aclBuildGroupsHTML(groups)
 {
-  var groups = groups.groups;
-  select = document.createElement('select');
-  for(var i in groups)
-  {
-    if(typeof(groups[i]['name']) == 'string' && i != 'toJSONString')
-    {
-      o = document.createElement('option');
-      o.value = groups[i]['id'];
-      t = document.createTextNode(groups[i]['name']);
-      o.appendChild(t);
-      select.appendChild(o);
-    }
-  }
-  return select;
+	var groups = groups.groups;
+	select = document.createElement('select');
+	for(var i in groups)
+	{
+		if(typeof(groups[i]['name']) == 'string' && i != 'toJSONString')
+		{
+			o = document.createElement('option');
+			o.value = groups[i]['id'];
+			t = document.createTextNode(groups[i]['name']);
+			o.appendChild(t);
+			select.appendChild(o);
+		}
+	}
+	return select;
 }
 
 function __aclBuildWizardWindow()
 {
-  darken(aclDisableTransitionFX, 70, 'acldarkener');
-  var box = document.createElement('div');
-  box.style.width = '640px'
-  box.style.height = IE ? '500px' : '440px';
-  box.style.position = 'fixed';
-  width = getWidth();
-  height = getHeight();
-  box.style.left = ( width / 2 - 320 ) + 'px';
-  box.style.top = ( height / 2 - 250 ) + 'px';
-  box.style.backgroundColor = 'white';
-  box.style.zIndex = getHighestZ() + 1;
-  box.id = aclManagerID;
-  box.style.opacity = '0';
-  box.style.filter = 'alpha(opacity=0)';
-  box.style.display = 'none';
-  
-  var mainwin = document.createElement('div');
-  mainwin.id = aclManagerID + '_main';
-  mainwin.style.clip = 'rect(0px,640px,440px,0px)';
-  mainwin.style.overflow = 'auto';
-  mainwin.style.width = '620px';
-  mainwin.style.height = '420px';
-  
-  var panel = document.createElement('div');
-  panel.style.width = '620px';
-  panel.style.padding = '10px';
-  panel.style.lineHeight = '40px';
-  panel.style.textAlign = 'right';
-  panel.style.position = 'fixed';
-  if ( IE )
-  {
-    panel.style.left = '0px';
-    panel.style.top = '440px';
-  }
-  else
-  {
-    panel.style.left = ( width / 2 - 320 ) + 'px';
-    panel.style.top = ( height / 2 + 190 ) + 'px';
-  }
-  panel.style.backgroundColor = '#D0D0D0';
-  panel.style.opacity = '0';
-  panel.style.filter = 'alpha(opacity=0)';
-  panel.id = aclManagerID + '_panel';
-  
-  var form = document.createElement('form');
-  form.method = 'post';
-  form.action = 'javascript:void(0)';
-  form.onsubmit = function() { if(this.username && !submitAuthorized) return false; __aclSubmitManager(this); return false; };
-  form.name = aclManagerID + '_formobj';
-  form.id   = aclManagerID + '_formobj_id';
-  
-  var back = document.createElement('input');
-  back.type = 'button';
-  back.value = $lang.get('etc_wizard_back');
-  back.style.fontWeight = 'normal';
-  back.onclick = function() { ajaxACLSwitchToSelector(); return false; };
-  back.style.display = 'none';
-  back.id = aclManagerID + '_back';
-  
-  var saver = document.createElement('input');
-  saver.type = 'submit';
-  saver.value = $lang.get('etc_wizard_next');
-  saver.style.fontWeight = 'bold';
-  saver.id = aclManagerID + '_next';
-  
-  var closer = document.createElement('input');
-  closer.type = 'button';
-  closer.value = $lang.get('etc_cancel_changes');
-  closer.onclick = function()
-  {
-    miniPromptMessage({
-      title: $lang.get('acl_msg_closeacl_confirm_title'),
-      message: $lang.get('acl_msg_closeacl_confirm_body'),
-      buttons: [
-        {
-          text: $lang.get('acl_btn_close'),
-          color: 'red',
-          style: {
-            fontWeight: 'bold'
-          },
-          onclick: function(e)
-          {
-            killACLManager();
-            miniPromptDestroy(this);
-          }
-        },
-        {
-          text: $lang.get('etc_cancel'),
-          onclick: function(e)
-          {
-            miniPromptDestroy(this);
-          }
-        }
-      ]
-    });
-    return false;
-  }
-  
-  var spacer1 = document.createTextNode('  ');
-  var spacer2 = document.createTextNode('  ');
-  
-  panel.appendChild(back);
-  panel.appendChild(spacer1);
-  panel.appendChild(saver);
-  panel.appendChild(spacer2);
-  panel.appendChild(closer);
-  form.appendChild(mainwin);
-  form.appendChild(panel);
-  box.appendChild(form);
-  
-  var body = document.getElementsByTagName('body')[0];
-  body.appendChild(box);
-  if ( aclDisableTransitionFX )
-  {
-    document.getElementById(aclManagerID).style.display = 'block';
-    changeOpac(100, aclManagerID);
-    changeOpac(100, aclManagerID + '_panel');
-  }
-  else
-  {
-    setTimeout("document.getElementById('"+aclManagerID+"').style.display = 'block'; opacity('"+aclManagerID+"', 0, 100, 250); opacity('"+aclManagerID + '_panel'+"', 0, 100, 250);", 500);
-  }
-  
-  console.debug(panel);
+	darken(aclDisableTransitionFX, 70, 'acldarkener');
+	var box = document.createElement('div');
+	box.style.width = '640px'
+	box.style.height = IE ? '500px' : '440px';
+	box.style.position = 'fixed';
+	width = getWidth();
+	height = getHeight();
+	box.style.left = ( width / 2 - 320 ) + 'px';
+	box.style.top = ( height / 2 - 250 ) + 'px';
+	box.style.backgroundColor = 'white';
+	box.style.zIndex = getHighestZ() + 1;
+	box.id = aclManagerID;
+	box.style.opacity = '0';
+	box.style.filter = 'alpha(opacity=0)';
+	box.style.display = 'none';
+	
+	var mainwin = document.createElement('div');
+	mainwin.id = aclManagerID + '_main';
+	mainwin.style.clip = 'rect(0px,640px,440px,0px)';
+	mainwin.style.overflow = 'auto';
+	mainwin.style.width = '620px';
+	mainwin.style.height = '420px';
+	
+	var panel = document.createElement('div');
+	panel.style.width = '620px';
+	panel.style.padding = '10px';
+	panel.style.lineHeight = '40px';
+	panel.style.textAlign = 'right';
+	panel.style.position = 'fixed';
+	if ( IE )
+	{
+		panel.style.left = '0px';
+		panel.style.top = '440px';
+	}
+	else
+	{
+		panel.style.left = ( width / 2 - 320 ) + 'px';
+		panel.style.top = ( height / 2 + 190 ) + 'px';
+	}
+	panel.style.backgroundColor = '#D0D0D0';
+	panel.style.opacity = '0';
+	panel.style.filter = 'alpha(opacity=0)';
+	panel.id = aclManagerID + '_panel';
+	
+	var form = document.createElement('form');
+	form.method = 'post';
+	form.action = 'javascript:void(0)';
+	form.onsubmit = function() { if(this.username && !submitAuthorized) return false; __aclSubmitManager(this); return false; };
+	form.name = aclManagerID + '_formobj';
+	form.id   = aclManagerID + '_formobj_id';
+	
+	var back = document.createElement('input');
+	back.type = 'button';
+	back.value = $lang.get('etc_wizard_back');
+	back.style.fontWeight = 'normal';
+	back.onclick = function() { ajaxACLSwitchToSelector(); return false; };
+	back.style.display = 'none';
+	back.id = aclManagerID + '_back';
+	
+	var saver = document.createElement('input');
+	saver.type = 'submit';
+	saver.value = $lang.get('etc_wizard_next');
+	saver.style.fontWeight = 'bold';
+	saver.id = aclManagerID + '_next';
+	
+	var closer = document.createElement('input');
+	closer.type = 'button';
+	closer.value = $lang.get('etc_cancel_changes');
+	closer.onclick = function()
+	{
+		miniPromptMessage({
+			title: $lang.get('acl_msg_closeacl_confirm_title'),
+			message: $lang.get('acl_msg_closeacl_confirm_body'),
+			buttons: [
+				{
+					text: $lang.get('acl_btn_close'),
+					color: 'red',
+					style: {
+						fontWeight: 'bold'
+					},
+					onclick: function(e)
+					{
+						killACLManager();
+						miniPromptDestroy(this);
+					}
+				},
+				{
+					text: $lang.get('etc_cancel'),
+					onclick: function(e)
+					{
+						miniPromptDestroy(this);
+					}
+				}
+			]
+		});
+		return false;
+	}
+	
+	var spacer1 = document.createTextNode('  ');
+	var spacer2 = document.createTextNode('  ');
+	
+	panel.appendChild(back);
+	panel.appendChild(spacer1);
+	panel.appendChild(saver);
+	panel.appendChild(spacer2);
+	panel.appendChild(closer);
+	form.appendChild(mainwin);
+	form.appendChild(panel);
+	box.appendChild(form);
+	
+	var body = document.getElementsByTagName('body')[0];
+	body.appendChild(box);
+	if ( aclDisableTransitionFX )
+	{
+		document.getElementById(aclManagerID).style.display = 'block';
+		changeOpac(100, aclManagerID);
+		changeOpac(100, aclManagerID + '_panel');
+	}
+	else
+	{
+		setTimeout("document.getElementById('"+aclManagerID+"').style.display = 'block'; opacity('"+aclManagerID+"', 0, 100, 250); opacity('"+aclManagerID + '_panel'+"', 0, 100, 250);", 500);
+	}
+	
+	console.debug(panel);
 }
 
 function killACLManager()
 {
-  el = document.getElementById(aclManagerID);
-  if(el)
-  {
-    if ( aclDisableTransitionFX )
-    {
-      enlighten(true, 'acldarkener');
-      el.parentNode.removeChild(el);
-    }
-    else
-    {
-      opacity(aclManagerID, 100, 0, 500);
-      setTimeout('var el = document.getElementById(aclManagerID); el.parentNode.removeChild(el); enlighten(false, "acldarkener");', 750);
-    }
-  }
+	el = document.getElementById(aclManagerID);
+	if(el)
+	{
+		if ( aclDisableTransitionFX )
+		{
+			enlighten(true, 'acldarkener');
+			el.parentNode.removeChild(el);
+		}
+		else
+		{
+			opacity(aclManagerID, 100, 0, 500);
+			setTimeout('var el = document.getElementById(aclManagerID); el.parentNode.removeChild(el); enlighten(false, "acldarkener");', 750);
+		}
+	}
 }
 
 function __aclSubmitManager(form)
 {
-  console.debug(form);
-  var thefrm = form;
-  // var thefrm = document.forms[form.name];
-  var modeobj = form_fetch_field(thefrm, 'mode');
-  if ( typeof(modeobj) == 'object' )
-  {
-    var mode = (thefrm.mode.value) ? thefrm.mode.value : 'cant_get';
-  }
-  else
-  {
-    var mode = '';
-  }
-  switch(mode)
-  {
-    case 'cant_get':
-      alert('BUG: can\'t get the state value from the form field.');
-      break;
-    case 'seltarget':
-      var target_type = parseInt(getRadioState(thefrm, 'target_type', ['1', '2']));
-      if(isNaN(target_type))
-      {
-        alert($lang.get('acl_err_pleaseselect_targettype'));
-        return false;
-      }
-      target_id = ( target_type == 1 ) ? parseInt(thefrm.group_id.value) : thefrm.username.value;
-      
-      obj = { 'mode' : mode, 'target_type' : target_type, 'target_id' : target_id };
-      
-      thispage = strToPageID(title);
-      do_scopesel = ( thispage[0] == aclDataCache.page_id && thispage[1] == aclDataCache.namespace );
-      
-      if(do_scopesel)
-      {
-        scope = getRadioState(thefrm, 'scope', ['page', 'group', 'global']);
-        if(scope == 'page')
-        {
-          pageid = strToPageID(title);
-          obj['page_id'] = pageid[0];
-          obj['namespace'] = pageid[1];
-        }
-        else if(scope == 'global')
-        {
-          obj['page_id'] = false;
-          obj['namespace'] = false;
-        }
-        else if(scope == 'group')
-        {
-          obj['page_id'] = document.getElementById('enACL_pgsel_1048576').value;
-          obj['namespace'] = '__PageGroup';
-        }
-        else
-        {
-          alert('Invalid scope');
-          return false;
-        }
-      }
-      else
-      {
-        obj['page_id'] = aclDataCache.page_id;
-        obj['namespace'] = aclDataCache.namespace;
-      }
-      if(target_id == '')
-      {
-        alert($lang.get('acl_err_pleaseselect_username'));
-        return false;
-      }
-      __aclJSONSubmitAjaxHandler(obj);
-      break;
-    case 'save_edit':
-    case 'save_new':
-      var form = document.forms[aclManagerID + '_formobj'];
-      selections = new Object();
-      var dbg = '';
-      var warned_everyone = false;
-      for(var i in aclPermList)
-      {
-        selections[aclPermList[i]] = getRadioState(form, aclPermList[i], ['i', 1, 2, 3, 4]);
-        // If we're editing permissions for everyone on the entire site and the
-        // admin selected to deny privileges, give a stern warning about it.
-        if ( selections[aclPermList[i]] == 1 && aclDataCache.target_type == 1 /* ACL_TYPE_GROUP */ && aclDataCache.target_id == 1 && !warned_everyone )
-        {
-          warned_everyone = true;
-          if ( !confirm($lang.get('acl_msg_deny_everyone_confirm')) )
-          {
-            return false;
-          }
-        }
-        dbg += aclPermList[i] + ': ' + selections[aclPermList[i]] + "\n";
-        if(!selections[aclPermList[i]])
-        {
-          alert("Invalid return from getRadioState: "+i+": "+selections[i]+" ("+typeof(selections[i])+")");
-          return false;
-        }
-      }
-      obj = new Object();
-      obj['perms'] = selections;
-      obj['mode'] = mode;
-      obj['target_type'] = aclDataCache.target_type;
-      obj['target_id'] = aclDataCache.target_id;
-      obj['target_name'] = aclDataCache.target_name;
-      obj['page_id'] = aclDataCache.page_id;
-      obj['namespace'] = aclDataCache.namespace;
-      __aclJSONSubmitAjaxHandler(obj);
-      break;
-    case 'trace':
-      var params = {
-        mode: 'trace',
-        user: document.getElementById(aclManagerID + 'trace_user').value,
-        page: document.getElementById(aclManagerID + 'trace_page').value
-      };
-      __aclJSONSubmitAjaxHandler(params);
-      break;
-    default:
-      alert("JSON form submit: invalid mode string "+mode+", stopping execution");
-      return false;
-      break;
-  }
+	console.debug(form);
+	var thefrm = form;
+	// var thefrm = document.forms[form.name];
+	var modeobj = form_fetch_field(thefrm, 'mode');
+	if ( typeof(modeobj) == 'object' )
+	{
+		var mode = (thefrm.mode.value) ? thefrm.mode.value : 'cant_get';
+	}
+	else
+	{
+		var mode = '';
+	}
+	switch(mode)
+	{
+		case 'cant_get':
+			alert('BUG: can\'t get the state value from the form field.');
+			break;
+		case 'seltarget':
+			var target_type = parseInt(getRadioState(thefrm, 'target_type', ['1', '2']));
+			if(isNaN(target_type))
+			{
+				alert($lang.get('acl_err_pleaseselect_targettype'));
+				return false;
+			}
+			target_id = ( target_type == 1 ) ? parseInt(thefrm.group_id.value) : thefrm.username.value;
+			
+			obj = { 'mode' : mode, 'target_type' : target_type, 'target_id' : target_id };
+			
+			thispage = strToPageID(title);
+			do_scopesel = ( thispage[0] == aclDataCache.page_id && thispage[1] == aclDataCache.namespace );
+			
+			if(do_scopesel)
+			{
+				scope = getRadioState(thefrm, 'scope', ['page', 'group', 'global']);
+				if(scope == 'page')
+				{
+					pageid = strToPageID(title);
+					obj['page_id'] = pageid[0];
+					obj['namespace'] = pageid[1];
+				}
+				else if(scope == 'global')
+				{
+					obj['page_id'] = false;
+					obj['namespace'] = false;
+				}
+				else if(scope == 'group')
+				{
+					obj['page_id'] = document.getElementById('enACL_pgsel_1048576').value;
+					obj['namespace'] = '__PageGroup';
+				}
+				else
+				{
+					alert('Invalid scope');
+					return false;
+				}
+			}
+			else
+			{
+				obj['page_id'] = aclDataCache.page_id;
+				obj['namespace'] = aclDataCache.namespace;
+			}
+			if(target_id == '')
+			{
+				alert($lang.get('acl_err_pleaseselect_username'));
+				return false;
+			}
+			__aclJSONSubmitAjaxHandler(obj);
+			break;
+		case 'save_edit':
+		case 'save_new':
+			var form = document.forms[aclManagerID + '_formobj'];
+			selections = new Object();
+			var dbg = '';
+			var warned_everyone = false;
+			for(var i in aclPermList)
+			{
+				selections[aclPermList[i]] = getRadioState(form, aclPermList[i], ['i', 1, 2, 3, 4]);
+				// If we're editing permissions for everyone on the entire site and the
+				// admin selected to deny privileges, give a stern warning about it.
+				if ( selections[aclPermList[i]] == 1 && aclDataCache.target_type == 1 /* ACL_TYPE_GROUP */ && aclDataCache.target_id == 1 && !warned_everyone )
+				{
+					warned_everyone = true;
+					if ( !confirm($lang.get('acl_msg_deny_everyone_confirm')) )
+					{
+						return false;
+					}
+				}
+				dbg += aclPermList[i] + ': ' + selections[aclPermList[i]] + "\n";
+				if(!selections[aclPermList[i]])
+				{
+					alert("Invalid return from getRadioState: "+i+": "+selections[i]+" ("+typeof(selections[i])+")");
+					return false;
+				}
+			}
+			obj = new Object();
+			obj['perms'] = selections;
+			obj['mode'] = mode;
+			obj['target_type'] = aclDataCache.target_type;
+			obj['target_id'] = aclDataCache.target_id;
+			obj['target_name'] = aclDataCache.target_name;
+			obj['page_id'] = aclDataCache.page_id;
+			obj['namespace'] = aclDataCache.namespace;
+			__aclJSONSubmitAjaxHandler(obj);
+			break;
+		case 'trace':
+			var params = {
+				mode: 'trace',
+				user: document.getElementById(aclManagerID + 'trace_user').value,
+				page: document.getElementById(aclManagerID + 'trace_page').value
+			};
+			__aclJSONSubmitAjaxHandler(params);
+			break;
+		default:
+			alert("JSON form submit: invalid mode string "+mode+", stopping execution");
+			return false;
+			break;
+	}
 }
 
 function getRadioState(form, name, valArray)
 {
-  // Konqueror/Safari fix
-  if ( form[name] )
-  {
-    var formitem = form[name];
-    if ( String(formitem) == '[object DOMNamedNodesCollection]' || is_Safari )
-    {
-      var i = 0;
-      var radios = new Array();
-      var radioids = new Array();
-      while(true)
-      {
-        var elem = formitem[i];
-        if ( !elem )
-          break;
-        radios.push(elem);
-        if ( !elem.id )
-        {
-          elem.id = 'autoRadioBtn_' + Math.floor(Math.random() * 1000000);
-        }
-        radioids.push(elem.id);
-        i++;
-      }
-      var cr;
-      for ( var i = 0; i < radios.length; i++ )
-      {
-        cr = document.getElementById(radioids[i]);
-        if ( cr.value == 'on' || cr.checked == true )
-        {
-          try {
-            return ( typeof ( valArray[i] ) != 'undefined' ) ? valArray[i] : false;
-          } catch(e) {
-            // alert('Didn\'t get value for index: ' + i);
-            return false;
-          }
-        }
-      }
-      return false;
-    }
-  }
-  inputs = form.getElementsByTagName('input');
-  radios = new Array();
-  for(var i in inputs)
-  {
-    if(inputs[i]) if(inputs[i].type == 'radio')
-      radios.push(inputs[i]);
-  }
-  for(var i in radios)
-  {
-    if(radios[i].checked && radios[i].name == name)
-      return radios[i].value;
-  }
-  return false;
+	// Konqueror/Safari fix
+	if ( form[name] )
+	{
+		var formitem = form[name];
+		if ( String(formitem) == '[object DOMNamedNodesCollection]' || is_Safari )
+		{
+			var i = 0;
+			var radios = new Array();
+			var radioids = new Array();
+			while(true)
+			{
+				var elem = formitem[i];
+				if ( !elem )
+					break;
+				radios.push(elem);
+				if ( !elem.id )
+				{
+					elem.id = 'autoRadioBtn_' + Math.floor(Math.random() * 1000000);
+				}
+				radioids.push(elem.id);
+				i++;
+			}
+			var cr;
+			for ( var i = 0; i < radios.length; i++ )
+			{
+				cr = document.getElementById(radioids[i]);
+				if ( cr.value == 'on' || cr.checked == true )
+				{
+					try {
+						return ( typeof ( valArray[i] ) != 'undefined' ) ? valArray[i] : false;
+					} catch(e) {
+						// alert('Didn\'t get value for index: ' + i);
+						return false;
+					}
+				}
+			}
+			return false;
+		}
+	}
+	inputs = form.getElementsByTagName('input');
+	radios = new Array();
+	for(var i in inputs)
+	{
+		if(inputs[i]) if(inputs[i].type == 'radio')
+			radios.push(inputs[i]);
+	}
+	for(var i in radios)
+	{
+		if(radios[i].checked && radios[i].name == name)
+			return radios[i].value;
+	}
+	return false;
 }
 
 function __aclSetAllRadios(val, valArray)
 {
-  val = String(val);
-  var form = document.forms[aclManagerID + '_formobj'];
-  if (!form)
-  {
-    return false;
-  }
-  var inputs = form.getElementsByTagName('input');
-  var radios = new Array();
-  var dbg = '';
-  for(var i = 0; i < inputs.length; i++)
-  {
-    dbg += String(inputs[i]) + "\n";
-    if(inputs[i].type == 'radio')
-      radios.push(inputs[i]);
-  }
-  for(var i in radios)
-  {
-    if(radios[i].value == val)
-      radios[i].checked = true;
-    else
-      radios[i].checked = false;
-  }
+	val = String(val);
+	var form = document.forms[aclManagerID + '_formobj'];
+	if (!form)
+	{
+		return false;
+	}
+	var inputs = form.getElementsByTagName('input');
+	var radios = new Array();
+	var dbg = '';
+	for(var i = 0; i < inputs.length; i++)
+	{
+		dbg += String(inputs[i]) + "\n";
+		if(inputs[i].type == 'radio')
+			radios.push(inputs[i]);
+	}
+	for(var i in radios)
+	{
+		if(radios[i].value == val)
+			radios[i].checked = true;
+		else
+			radios[i].checked = false;
+	}
 }
 
 function __aclDeleteRule()
 {
-  if(!aclDataCache) 
-  {
-    if ( window.console )
-    {
-      try{ console.error('ACL editor: can\'t load data cache on delete'); } catch(e) {};
-    }
-    return false;
-  }
-  if(aclDataCache.mode != 'seltarget' && aclDataCache.mode != 'save_new' && aclDataCache.mode != 'save_edit')
-  {
-    if ( window.console )
-    {
-      try{ console.error('ACL editor: wrong mode on aclDataCache: ' + aclDataCache.mode); } catch(e) {};
-    }
-    return false;
-  }
-  parms = {
-    'target_type' : aclDataCache.target_type,
-    'target_id' : aclDataCache.target_id,
-    'target_name' : aclDataCache.target_name,
-    'page_id' : aclDataCache.page_id,
-    'namespace' : aclDataCache.namespace,
-    'mode' : 'delete'
-  };
-  __aclJSONSubmitAjaxHandler(parms);
+	if(!aclDataCache) 
+	{
+		if ( window.console )
+		{
+			try{ console.error('ACL editor: can\'t load data cache on delete'); } catch(e) {};
+		}
+		return false;
+	}
+	if(aclDataCache.mode != 'seltarget' && aclDataCache.mode != 'save_new' && aclDataCache.mode != 'save_edit')
+	{
+		if ( window.console )
+		{
+			try{ console.error('ACL editor: wrong mode on aclDataCache: ' + aclDataCache.mode); } catch(e) {};
+		}
+		return false;
+	}
+	parms = {
+		'target_type' : aclDataCache.target_type,
+		'target_id' : aclDataCache.target_id,
+		'target_name' : aclDataCache.target_name,
+		'page_id' : aclDataCache.page_id,
+		'namespace' : aclDataCache.namespace,
+		'mode' : 'delete'
+	};
+	__aclJSONSubmitAjaxHandler(parms);
 }
 
 function aclSetViewListExisting()
 {
-  if ( !document.getElementById(aclManagerID) )
-  {
-    return false;
-  }
-  
-  var main = document.getElementById(aclManagerID + '_main');
-  main.innerHTML = '';
-  main.style.backgroundImage = 'url(' + scriptPath + '/images/loading-big.gif)';
-  main.style.backgroundRepeat = 'no-repeat';
-  main.style.backgroundPosition = 'center center';
-  
-  var parms = {
-    'mode' : 'list_existing'
-  };
-  __aclJSONSubmitAjaxHandler(parms);
+	if ( !document.getElementById(aclManagerID) )
+	{
+		return false;
+	}
+	
+	var main = document.getElementById(aclManagerID + '_main');
+	main.innerHTML = '';
+	main.style.backgroundImage = 'url(' + scriptPath + '/images/loading-big.gif)';
+	main.style.backgroundRepeat = 'no-repeat';
+	main.style.backgroundPosition = 'center center';
+	
+	var parms = {
+		'mode' : 'list_existing'
+	};
+	__aclJSONSubmitAjaxHandler(parms);
 }
 
 function aclSetViewListExistingRespond(data)
 {
-  var main = document.getElementById(aclManagerID + '_main');
-  main.style.padding = '10px';
-  main.innerHTML = '';
-  
-  var heading = document.createElement('h3');
-  heading.appendChild(document.createTextNode($lang.get('acl_msg_scale_intro_title')));
-  main.appendChild(heading);
-  
-  var p = document.createElement('p');
-  p.appendChild(document.createTextNode($lang.get('acl_msg_scale_intro_body')));
-  main.appendChild(p);
-  
-  
-  main.innerHTML += data.key;
-  main.style.backgroundImage = 'none';
-  
-  document.getElementById(aclManagerID + '_back').style.display = 'inline';
-  document.getElementById(aclManagerID + '_next').style.display = 'none';
-  
-  for ( var i = 0; i < data.rules.length; i++ )
-  {
-    var rule = data.rules[i];
-    // build the rule, this is just more boring DOM crap.
-    var div = document.createElement('div');
-    div.style.padding = '5px 3px';
-    div.style.backgroundColor = '#' + rule.color;
-    div.style.cursor = 'pointer';
-    div.rule_id = rule.rule_id;
-    div.onclick = function()
-    {
-      var main = document.getElementById(aclManagerID + '_main');
-      main.innerHTML = '';
-      main.style.backgroundImage = 'url(' + scriptPath + '/images/loading-big.gif)';
-      ajaxOpenDirectACLRule(parseInt(this.rule_id));
-    }
-    div.innerHTML = rule.score_string;
-    main.appendChild(div);
-  }
+	var main = document.getElementById(aclManagerID + '_main');
+	main.style.padding = '10px';
+	main.innerHTML = '';
+	
+	var heading = document.createElement('h3');
+	heading.appendChild(document.createTextNode($lang.get('acl_msg_scale_intro_title')));
+	main.appendChild(heading);
+	
+	var p = document.createElement('p');
+	p.appendChild(document.createTextNode($lang.get('acl_msg_scale_intro_body')));
+	main.appendChild(p);
+	
+	
+	main.innerHTML += data.key;
+	main.style.backgroundImage = 'none';
+	
+	document.getElementById(aclManagerID + '_back').style.display = 'inline';
+	document.getElementById(aclManagerID + '_next').style.display = 'none';
+	
+	for ( var i = 0; i < data.rules.length; i++ )
+	{
+		var rule = data.rules[i];
+		// build the rule, this is just more boring DOM crap.
+		var div = document.createElement('div');
+		div.style.padding = '5px 3px';
+		div.style.backgroundColor = '#' + rule.color;
+		div.style.cursor = 'pointer';
+		div.rule_id = rule.rule_id;
+		div.onclick = function()
+		{
+			var main = document.getElementById(aclManagerID + '_main');
+			main.innerHTML = '';
+			main.style.backgroundImage = 'url(' + scriptPath + '/images/loading-big.gif)';
+			ajaxOpenDirectACLRule(parseInt(this.rule_id));
+		}
+		div.innerHTML = rule.score_string;
+		main.appendChild(div);
+	}
 }
 
 function aclSetViewDebugTools()
 {
-  // selection window for viewing effective permissions
-  var main = document.getElementById(aclManagerID + '_main');
-  main.innerHTML = '';
+	// selection window for viewing effective permissions
+	var main = document.getElementById(aclManagerID + '_main');
+	main.innerHTML = '';
  
-  // set the submission handler to trace
-  var thefrm = document.forms[aclManagerID + '_formobj'];
-  var modeobj = form_fetch_field(thefrm, 'mode');
-  modeobj.value = 'trace';
-  
-  // show the back button
-  document.getElementById(aclManagerID + '_back').style.display = 'inline';
-  
-  //
-  // start building
-  //
-  
-  // selection interface
-  var selector = document.createElement('div');
-  
-    var table = document.createElement('table');
-    
-    // username
-    var tr_user = document.createElement('tr');
-    var td_user_l = document.createElement('td');
-    var lbl_user = document.createElement('label');
-    lbl_user.setAttribute('for', aclManagerID + 'trace_user');
-    lbl_user.appendChild(document.createTextNode($lang.get('acl_lbl_trace_user')));
-    td_user_l.appendChild(lbl_user);
-    tr_user.appendChild(td_user_l);
-    
-    var td_user_i = document.createElement('td');
-    var i_user = document.createElement('input');
-    i_user.type = 'text';
-    i_user.id = aclManagerID + 'trace_user';
-    i_user.onkeyup = function() { new AutofillUsername(this, true); };
-    i_user.size = '20';
-    td_user_i.appendChild(i_user);
-    tr_user.appendChild(td_user_i);
-    
-    table.appendChild(tr_user);
-    
-    // page
-    var tr_page = document.createElement('tr');
-    var td_page_l = document.createElement('td');
-    var lbl_page = document.createElement('label');
-    lbl_page.setAttribute('for', aclManagerID + 'trace_page');
-    lbl_page.appendChild(document.createTextNode($lang.get('acl_lbl_trace_page')));
-    td_page_l.appendChild(lbl_page);
-    tr_page.appendChild(td_page_l);
-    
-    var td_page_i = document.createElement('td');
-    var i_page = document.createElement('input');
-    i_page.type = 'text';
-    i_page.id = aclManagerID + 'trace_page';
-    i_page.onkeyup = function() { new AutofillPage(this); };
-    i_page.size = '20';
-    td_page_i.appendChild(i_page);
-    tr_page.appendChild(td_page_i);
-    
-    table.appendChild(tr_page);
-    
-    selector.appendChild(table);
-  
-  // wrapper
-  
-  var container = document.createElement('div');
-  
-    container.style.margin = 'auto';
-    container.style.width = '360px';
-    container.style.paddingTop = '90px';
-    
-    var head = document.createElement('h2');
-    head.appendChild(document.createTextNode($lang.get('acl_lbl_trace_title')));
-    
-    var desc = document.createElement('p');
-    desc.innerHTML = $lang.get('acl_lbl_trace_body');
-    
-    container.appendChild(head);
-    container.appendChild(desc);
-    container.appendChild(selector);
-  
-  main.appendChild(container);
+	// set the submission handler to trace
+	var thefrm = document.forms[aclManagerID + '_formobj'];
+	var modeobj = form_fetch_field(thefrm, 'mode');
+	modeobj.value = 'trace';
+	
+	// show the back button
+	document.getElementById(aclManagerID + '_back').style.display = 'inline';
+	
+	//
+	// start building
+	//
+	
+	// selection interface
+	var selector = document.createElement('div');
+	
+		var table = document.createElement('table');
+		
+		// username
+		var tr_user = document.createElement('tr');
+		var td_user_l = document.createElement('td');
+		var lbl_user = document.createElement('label');
+		lbl_user.setAttribute('for', aclManagerID + 'trace_user');
+		lbl_user.appendChild(document.createTextNode($lang.get('acl_lbl_trace_user')));
+		td_user_l.appendChild(lbl_user);
+		tr_user.appendChild(td_user_l);
+		
+		var td_user_i = document.createElement('td');
+		var i_user = document.createElement('input');
+		i_user.type = 'text';
+		i_user.id = aclManagerID + 'trace_user';
+		i_user.onkeyup = function() { new AutofillUsername(this, true); };
+		i_user.size = '20';
+		td_user_i.appendChild(i_user);
+		tr_user.appendChild(td_user_i);
+		
+		table.appendChild(tr_user);
+		
+		// page
+		var tr_page = document.createElement('tr');
+		var td_page_l = document.createElement('td');
+		var lbl_page = document.createElement('label');
+		lbl_page.setAttribute('for', aclManagerID + 'trace_page');
+		lbl_page.appendChild(document.createTextNode($lang.get('acl_lbl_trace_page')));
+		td_page_l.appendChild(lbl_page);
+		tr_page.appendChild(td_page_l);
+		
+		var td_page_i = document.createElement('td');
+		var i_page = document.createElement('input');
+		i_page.type = 'text';
+		i_page.id = aclManagerID + 'trace_page';
+		i_page.onkeyup = function() { new AutofillPage(this); };
+		i_page.size = '20';
+		td_page_i.appendChild(i_page);
+		tr_page.appendChild(td_page_i);
+		
+		table.appendChild(tr_page);
+		
+		selector.appendChild(table);
+	
+	// wrapper
+	
+	var container = document.createElement('div');
+	
+		container.style.margin = 'auto';
+		container.style.width = '360px';
+		container.style.paddingTop = '90px';
+		
+		var head = document.createElement('h2');
+		head.appendChild(document.createTextNode($lang.get('acl_lbl_trace_title')));
+		
+		var desc = document.createElement('p');
+		desc.innerHTML = $lang.get('acl_lbl_trace_body');
+		
+		container.appendChild(head);
+		container.appendChild(desc);
+		container.appendChild(selector);
+	
+	main.appendChild(container);
 }
 
 function aclTraceKey()
 {
-  var div = document.createElement('div');
-  $(div).addClass('tblholder');
-  var table = document.createElement('table');
-  $(table).attr('cellspacing', '1').attr('cellpadding', '4');
-  
-  var inherit_list = ['enano_default', 'global_everyone', 'global_group', 'global_user', 'pg_everyone', 'pg_group', 'pg_user', 'local_everyone', 'local_group', 'local_user'];
-  for ( var i = 0; i < inherit_list.length; i++ )
-  {
-    var t = inherit_list[i];
-    var tr = document.createElement('tr');
-    var td_key = document.createElement('td');
-    $(td_key).addClass('acl_' + t).addClass('acl_inherit_key');
-    tr.appendChild(td_key);
-    var td_explain = document.createElement('td');
-    $(td_explain).addClass(i % 2 == 0 ? 'row1' : 'row2');
-    td_explain.appendChild(document.createTextNode($lang.get('acl_inherit_key_' + t)));
-    tr.appendChild(td_explain);
-    table.appendChild(tr);
-  }
-  div.appendChild(table);
-  return div;
+	var div = document.createElement('div');
+	$(div).addClass('tblholder');
+	var table = document.createElement('table');
+	$(table).attr('cellspacing', '1').attr('cellpadding', '4');
+	
+	var inherit_list = ['enano_default', 'global_everyone', 'global_group', 'global_user', 'pg_everyone', 'pg_group', 'pg_user', 'local_everyone', 'local_group', 'local_user'];
+	for ( var i = 0; i < inherit_list.length; i++ )
+	{
+		var t = inherit_list[i];
+		var tr = document.createElement('tr');
+		var td_key = document.createElement('td');
+		$(td_key).addClass('acl_' + t).addClass('acl_inherit_key');
+		tr.appendChild(td_key);
+		var td_explain = document.createElement('td');
+		$(td_explain).addClass(i % 2 == 0 ? 'row1' : 'row2');
+		td_explain.appendChild(document.createTextNode($lang.get('acl_inherit_key_' + t)));
+		tr.appendChild(td_explain);
+		table.appendChild(tr);
+	}
+	div.appendChild(table);
+	return div;
 }
 
 function aclTraceModalKey()
 {
-  load_component('messagebox');
-  miniPrompt(function(parent)
-    {
-      // heading
-      var h3 = document.createElement('h3');
-      h3.appendChild(document.createTextNode($lang.get('acl_msg_trace_key')));
-      parent.appendChild(h3);
-      
-      var key = aclTraceKey();
-      parent.appendChild(key);
-      
-      var p = document.createElement('p');
-      $(p).css('text-align', 'center');
-      
-      var closer = document.createElement('a');
-      $(closer).addClass('abutton').addClass('abutton_red').css('font-weight', 'bold');
-      closer.appendChild(document.createTextNode($lang.get('etc_close')));
-      closer.href = '#';
-      $(closer).click(function(e)
-        {
-          miniPromptDestroy(this);
-          return false;
-        });
-      
-      p.appendChild(closer);
-      parent.appendChild(p);
-    });
+	load_component('messagebox');
+	miniPrompt(function(parent)
+		{
+			// heading
+			var h3 = document.createElement('h3');
+			h3.appendChild(document.createTextNode($lang.get('acl_msg_trace_key')));
+			parent.appendChild(h3);
+			
+			var key = aclTraceKey();
+			parent.appendChild(key);
+			
+			var p = document.createElement('p');
+			$(p).css('text-align', 'center');
+			
+			var closer = document.createElement('a');
+			$(closer).addClass('abutton').addClass('abutton_red').css('font-weight', 'bold');
+			closer.appendChild(document.createTextNode($lang.get('etc_close')));
+			closer.href = '#';
+			$(closer).click(function(e)
+				{
+					miniPromptDestroy(this);
+					return false;
+				});
+			
+			p.appendChild(closer);
+			parent.appendChild(p);
+		});
 }
 
 function aclDrawTraceWrapper(data)
 {
-  // hide the next button
-  document.getElementById(aclManagerID + '_next').style.display = 'none';
-  
-  var trace_by_perm = aclDrawTraceByPerm(data);
-  var trace_by_rule = aclDrawTraceByRule(data);
-  
-  trace_by_perm.id = 'aclDebugTraceViewPerm';
-  trace_by_rule.id = 'aclDebugTraceViewRule';
-  
-  var start_with_rule = ( readCookie('acl_trace_view') == 'rule' );
-  
-  if ( start_with_rule )
-  {
-    trace_by_perm.style.display = 'none';
-  }
-  else
-  {
-    trace_by_rule.style.display = 'none';
-  }
-  
-  // selection window for viewing effective permissions
-  var main = document.getElementById(aclManagerID + '_main');
-  main.innerHTML = '';
-  
-  var wrapper = document.createElement('div');
-  $(wrapper).css('padding-bottom', '20px');
-  
-  var floatlink = document.createElement('div');
-  $(floatlink).css('float', 'right').css('margin-left', '20px').css('margin-bottom', '20px').css('text-align', 'right');
-  var a_toggle = document.createElement('a');
-  $(a_toggle).attr('id', 'aclDebugTraceViewToggle');
-  a_toggle.innerHTML = '&raquo; ';
-  a_toggle.innerHTML += start_with_rule ? $lang.get('acl_btn_sort_perm') : $lang.get('acl_btn_sort_rule');
-  a_toggle.href = '#';
-  floatlink.appendChild(a_toggle);
-  floatlink.appendChild(document.createElement('br'));
-  var a_key = document.createElement('a');
-  $(a_key).css('font-size', 'smaller');
-  a_key.innerHTML = '&raquo; ';
-  a_key.innerHTML += $lang.get('acl_btn_view_key');
-  a_key.href = '#';
-  floatlink.appendChild(a_key);
-  wrapper.appendChild(floatlink);
-  
-  var h3 = document.createElement('h3');
-  h3.appendChild(document.createTextNode($lang.get('acl_msg_debug_main_title')));
-  wrapper.appendChild(h3);
-  var p = document.createElement('p');
-  p.appendChild(document.createTextNode($lang.get('acl_msg_debug_main_body')));
-  wrapper.appendChild(p);
-  
-  wrapper.appendChild(trace_by_perm);
-  wrapper.appendChild(trace_by_rule);
-  
-  main.appendChild(wrapper);
-  
-  $(a_toggle).click(function(e)
-    {
-      aclTraceToggleViews();
-      return false;
-    });
-  
-  $(a_key).click(function(e)
-    {
-      aclTraceModalKey();
-      return false;
-    });
+	// hide the next button
+	document.getElementById(aclManagerID + '_next').style.display = 'none';
+	
+	var trace_by_perm = aclDrawTraceByPerm(data);
+	var trace_by_rule = aclDrawTraceByRule(data);
+	
+	trace_by_perm.id = 'aclDebugTraceViewPerm';
+	trace_by_rule.id = 'aclDebugTraceViewRule';
+	
+	var start_with_rule = ( readCookie('acl_trace_view') == 'rule' );
+	
+	if ( start_with_rule )
+	{
+		trace_by_perm.style.display = 'none';
+	}
+	else
+	{
+		trace_by_rule.style.display = 'none';
+	}
+	
+	// selection window for viewing effective permissions
+	var main = document.getElementById(aclManagerID + '_main');
+	main.innerHTML = '';
+	
+	var wrapper = document.createElement('div');
+	$(wrapper).css('padding-bottom', '20px');
+	
+	var floatlink = document.createElement('div');
+	$(floatlink).css('float', 'right').css('margin-left', '20px').css('margin-bottom', '20px').css('text-align', 'right');
+	var a_toggle = document.createElement('a');
+	$(a_toggle).attr('id', 'aclDebugTraceViewToggle');
+	a_toggle.innerHTML = '&raquo; ';
+	a_toggle.innerHTML += start_with_rule ? $lang.get('acl_btn_sort_perm') : $lang.get('acl_btn_sort_rule');
+	a_toggle.href = '#';
+	floatlink.appendChild(a_toggle);
+	floatlink.appendChild(document.createElement('br'));
+	var a_key = document.createElement('a');
+	$(a_key).css('font-size', 'smaller');
+	a_key.innerHTML = '&raquo; ';
+	a_key.innerHTML += $lang.get('acl_btn_view_key');
+	a_key.href = '#';
+	floatlink.appendChild(a_key);
+	wrapper.appendChild(floatlink);
+	
+	var h3 = document.createElement('h3');
+	h3.appendChild(document.createTextNode($lang.get('acl_msg_debug_main_title')));
+	wrapper.appendChild(h3);
+	var p = document.createElement('p');
+	p.appendChild(document.createTextNode($lang.get('acl_msg_debug_main_body')));
+	wrapper.appendChild(p);
+	
+	wrapper.appendChild(trace_by_perm);
+	wrapper.appendChild(trace_by_rule);
+	
+	main.appendChild(wrapper);
+	
+	$(a_toggle).click(function(e)
+		{
+			aclTraceToggleViews();
+			return false;
+		});
+	
+	$(a_key).click(function(e)
+		{
+			aclTraceModalKey();
+			return false;
+		});
 }
 
 function aclTraceToggleViews()
 {
-  var trace_by_perm = document.getElementById('aclDebugTraceViewPerm');
-  var trace_by_rule = document.getElementById('aclDebugTraceViewRule');
-  
-  var toggler = document.getElementById('aclDebugTraceViewToggle');
-  var newtext;
-  
-  if ( trace_by_perm.style.display == 'none' )
-  {
-    newtext = $lang.get('acl_btn_sort_rule');
-    $(trace_by_rule).hide('blind', {}, 750, function()
-      {
-        $(trace_by_perm).show('blind', {}, 750);
-      });
-    createCookie('acl_trace_view', 'perm');
-  }
-  else
-  {
-    newtext = $lang.get('acl_btn_sort_perm');
-    $(trace_by_perm).hide('blind', {}, 750, function()
-      {
-        $(trace_by_rule).show('blind', {}, 750);
-      });
-    createCookie('acl_trace_view', 'rule');
-  }
-  $(toggler).fadeOut(500, function()
-    {
-      this.innerHTML = '&raquo; ' + newtext;
-      $(this).fadeIn(500);
-    });
+	var trace_by_perm = document.getElementById('aclDebugTraceViewPerm');
+	var trace_by_rule = document.getElementById('aclDebugTraceViewRule');
+	
+	var toggler = document.getElementById('aclDebugTraceViewToggle');
+	var newtext;
+	
+	if ( trace_by_perm.style.display == 'none' )
+	{
+		newtext = $lang.get('acl_btn_sort_rule');
+		$(trace_by_rule).hide('blind', {}, 750, function()
+			{
+				$(trace_by_perm).show('blind', {}, 750);
+			});
+		createCookie('acl_trace_view', 'perm');
+	}
+	else
+	{
+		newtext = $lang.get('acl_btn_sort_perm');
+		$(trace_by_perm).hide('blind', {}, 750, function()
+			{
+				$(trace_by_rule).show('blind', {}, 750);
+			});
+		createCookie('acl_trace_view', 'rule');
+	}
+	$(toggler).fadeOut(500, function()
+		{
+			this.innerHTML = '&raquo; ' + newtext;
+			$(this).fadeIn(500);
+		});
 }
 
 function aclDrawTraceByPerm(data)
 {
-  var wrapper = document.createElement('div');
-  // wrapper.style.display = 'none';
-  
-  // temporarily append wrapper to body to allow onclick to work
-  // var body = document.getElementsByTagName('body')[0];
-  // body.appendChild(wrapper);  
-  
-  for ( var i in data.perms )
-  {
-    var perm = data.perms[i];
-    var item = document.createElement('div');
-    item.className = perm.divclass;
-    
-    // first row - permission name + current setting
-    // use innerHTML here to allow for HTML in localized permission types
-    item.innerHTML += '<b>' + perm.perm_name + ' - ' + perm.perm_value + '</b>';
-    item.appendChild(document.createElement('br'));
-    
-    // second row - permission localized name + rule ID
-    var sm = document.createElement('small');
-    sm.innerHTML = perm.perm_src;
-    
-    item.appendChild(sm);
-    
-    wrapper.appendChild(item);
-    
-    // whole row is now in the document
-    if ( perm.rule_id != -1 )
-    {
-      sm.innerHTML += ' [';
-      // rule is editable
-      var editlink = document.createElement('a');
-      editlink.href = 'javascript:ajaxOpenDirectACLRule(' + perm.rule_id + ');';
-      editlink.appendChild(document.createTextNode($lang.get('acl_btn_edit_rule')));
-      sm.appendChild(editlink);
-      sm.innerHTML += ']';
-    }
-    
-    if ( perm.bad_deps.length > 0 )
-    {
-      var bd = document.createElement('span');
-      $(bd).addClass('acl_failed_deps');
-      var failed_deps = '';
-      for ( var i = 0; i < perm.bad_deps.length; i++ )
-      {
-        if ( i > 0 )
-          failed_deps += ', ';
-        failed_deps += data.perms[perm.bad_deps[i]].perm_name;
-      }
-      var title = document.createElement('span');
-      $(title).addClass('title');
-      title.appendChild(document.createTextNode($lang.get('acl_msg_failed_deps')));
-      bd.appendChild(title);
-      bd.appendChild(document.createTextNode(failed_deps));
-      
-      item.appendChild(document.createElement('br'));
-      item.appendChild(bd);
-    }
-  }
-  
-  // var ret = wrapper.cloneNode(true);
-  // body.removeChild(wrapper);
-  // wrapper = false;
-  // ret.style.display = 'block';
-  // console.debug(ret);
-  // return ret;
-  return wrapper;
+	var wrapper = document.createElement('div');
+	// wrapper.style.display = 'none';
+	
+	// temporarily append wrapper to body to allow onclick to work
+	// var body = document.getElementsByTagName('body')[0];
+	// body.appendChild(wrapper);  
+	
+	for ( var i in data.perms )
+	{
+		var perm = data.perms[i];
+		var item = document.createElement('div');
+		item.className = perm.divclass;
+		
+		// first row - permission name + current setting
+		// use innerHTML here to allow for HTML in localized permission types
+		item.innerHTML += '<b>' + perm.perm_name + ' - ' + perm.perm_value + '</b>';
+		item.appendChild(document.createElement('br'));
+		
+		// second row - permission localized name + rule ID
+		var sm = document.createElement('small');
+		sm.innerHTML = perm.perm_src;
+		
+		item.appendChild(sm);
+		
+		wrapper.appendChild(item);
+		
+		// whole row is now in the document
+		if ( perm.rule_id != -1 )
+		{
+			sm.innerHTML += ' [';
+			// rule is editable
+			var editlink = document.createElement('a');
+			editlink.href = 'javascript:ajaxOpenDirectACLRule(' + perm.rule_id + ');';
+			editlink.appendChild(document.createTextNode($lang.get('acl_btn_edit_rule')));
+			sm.appendChild(editlink);
+			sm.innerHTML += ']';
+		}
+		
+		if ( perm.bad_deps.length > 0 )
+		{
+			var bd = document.createElement('span');
+			$(bd).addClass('acl_failed_deps');
+			var failed_deps = '';
+			for ( var i = 0; i < perm.bad_deps.length; i++ )
+			{
+				if ( i > 0 )
+					failed_deps += ', ';
+				failed_deps += data.perms[perm.bad_deps[i]].perm_name;
+			}
+			var title = document.createElement('span');
+			$(title).addClass('title');
+			title.appendChild(document.createTextNode($lang.get('acl_msg_failed_deps')));
+			bd.appendChild(title);
+			bd.appendChild(document.createTextNode(failed_deps));
+			
+			item.appendChild(document.createElement('br'));
+			item.appendChild(bd);
+		}
+	}
+	
+	// var ret = wrapper.cloneNode(true);
+	// body.removeChild(wrapper);
+	// wrapper = false;
+	// ret.style.display = 'block';
+	// console.debug(ret);
+	// return ret;
+	return wrapper;
 }
 
 function aclDrawTraceByRule(data)
 {
-  var wrapper = document.createElement('div');
-  var groupdata = {};
-  
-  for ( var i in data.perms )
-  {
-    var perm = data.perms[i];
-    if ( !groupdata[perm['rule_id']] )
-    {
-      groupdata[perm['rule_id']] = {
-        meta: {
-          divclass: perm.divclass,
-          perm_src: perm.perm_src,
-          rule_id: perm.rule_id
-        },
-        rules: {}
-      };
-    }
-    groupdata[perm['rule_id']]['rules'][i] = perm;
-  }
-  
-  for ( var i in groupdata )
-  {
-    var group = groupdata[i];
-    var grp = document.createElement('div');
-    var head = document.createElement('div');
-    head.className = group.meta.divclass;
-    var span = document.createElement('span');
-    span.style.fontSize = 'larger';
-    span.appendChild(document.createTextNode(group.meta.perm_src));
-    head.appendChild(span);
-    if ( group.meta.rule_id != -1 )
-    {
-      head.innerHTML += ' [';
-      // rule is editable
-      var editlink = document.createElement('a');
-      editlink.href = 'javascript:ajaxOpenDirectACLRule(' + group.meta.rule_id + ');';
-      editlink.appendChild(document.createTextNode($lang.get('acl_btn_edit_rule')));
-      head.appendChild(editlink);
-      head.innerHTML += ']';
-    }
-    grp.appendChild(head);
-    for ( var i in group.rules )
-    {
-      var rule = group.rules[i];
-      var rulediv = document.createElement('div');
-      rulediv.style.padding = '3px 12px';
-      rulediv.innerHTML += rule.perm_name + ': ';
-      var b = document.createElement('strong');
-      b.appendChild(document.createTextNode(rule.perm_value));
-      rulediv.appendChild(b);
-      grp.appendChild(rulediv);
-      
-      if ( rule.bad_deps.length > 0 )
-      {
-        var bd = document.createElement('span');
-        $(bd).addClass('acl_failed_deps');
-        var failed_deps = '';
-        for ( var i = 0; i < rule.bad_deps.length; i++ )
-        {
-          if ( i > 0 )
-            failed_deps += ', ';
-          failed_deps += data.perms[rule.bad_deps[i]].perm_name;
-        }
-        var title = document.createElement('span');
-        $(title).addClass('title');
-        title.appendChild(document.createTextNode($lang.get('acl_msg_failed_deps')));
-        bd.appendChild(title);
-        bd.appendChild(document.createTextNode(failed_deps));
-        
-        rulediv.appendChild(document.createElement('br'));
-        rulediv.appendChild(bd);
-      }
-    }
-    wrapper.appendChild(grp);
-  }
-  
-  return wrapper;
+	var wrapper = document.createElement('div');
+	var groupdata = {};
+	
+	for ( var i in data.perms )
+	{
+		var perm = data.perms[i];
+		if ( !groupdata[perm['rule_id']] )
+		{
+			groupdata[perm['rule_id']] = {
+				meta: {
+					divclass: perm.divclass,
+					perm_src: perm.perm_src,
+					rule_id: perm.rule_id
+				},
+				rules: {}
+			};
+		}
+		groupdata[perm['rule_id']]['rules'][i] = perm;
+	}
+	
+	for ( var i in groupdata )
+	{
+		var group = groupdata[i];
+		var grp = document.createElement('div');
+		var head = document.createElement('div');
+		head.className = group.meta.divclass;
+		var span = document.createElement('span');
+		span.style.fontSize = 'larger';
+		span.appendChild(document.createTextNode(group.meta.perm_src));
+		head.appendChild(span);
+		if ( group.meta.rule_id != -1 )
+		{
+			head.innerHTML += ' [';
+			// rule is editable
+			var editlink = document.createElement('a');
+			editlink.href = 'javascript:ajaxOpenDirectACLRule(' + group.meta.rule_id + ');';
+			editlink.appendChild(document.createTextNode($lang.get('acl_btn_edit_rule')));
+			head.appendChild(editlink);
+			head.innerHTML += ']';
+		}
+		grp.appendChild(head);
+		for ( var i in group.rules )
+		{
+			var rule = group.rules[i];
+			var rulediv = document.createElement('div');
+			rulediv.style.padding = '3px 12px';
+			rulediv.innerHTML += rule.perm_name + ': ';
+			var b = document.createElement('strong');
+			b.appendChild(document.createTextNode(rule.perm_value));
+			rulediv.appendChild(b);
+			grp.appendChild(rulediv);
+			
+			if ( rule.bad_deps.length > 0 )
+			{
+				var bd = document.createElement('span');
+				$(bd).addClass('acl_failed_deps');
+				var failed_deps = '';
+				for ( var i = 0; i < rule.bad_deps.length; i++ )
+				{
+					if ( i > 0 )
+						failed_deps += ', ';
+					failed_deps += data.perms[rule.bad_deps[i]].perm_name;
+				}
+				var title = document.createElement('span');
+				$(title).addClass('title');
+				title.appendChild(document.createTextNode($lang.get('acl_msg_failed_deps')));
+				bd.appendChild(title);
+				bd.appendChild(document.createTextNode(failed_deps));
+				
+				rulediv.appendChild(document.createElement('br'));
+				rulediv.appendChild(bd);
+			}
+		}
+		wrapper.appendChild(grp);
+	}
+	
+	return wrapper;
 }
 
 function aclShowPresetLoader()
 {
-  var prompt = miniPrompt(function(parent)
-    {
-      parent.innerHTML = '<img style="display: block; margin: 0 auto;" src="' + cdnPath + '/images/loading-big.gif" />';
-    });
-  var request = toJSONString({
-      mode: 'list_presets'
-    });
-  ajaxPost(stdAjaxPrefix + '&_mode=acljson', 'acl_params=' + ajaxEscape(request), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        if ( !check_json_response(ajax.responseText) )
-        {
-          miniPromptDestroy(prompt);
-          return handle_invalid_json(ajax.responseText);
-        }
-        var response = parseJSON(ajax.responseText);
-        if ( response.mode == 'error' )
-        {
-          alert(response.error);
-          miniPromptDestroy(prompt);
-          return false;
-        }
-        prompt = prompt.firstChild.nextSibling;
-        prompt.style.textAlign = 'center';
-        prompt.innerHTML = '<h3>' + $lang.get('acl_lbl_preset_load_title') + '</h3>';
-        
-        if ( response.presets.length > 0 )
-        {
-          // selection box
-          var para = document.createElement('p');
-          var select = document.createElement('select');
-          
-          var option = document.createElement('option');
-          option.value = '0';
-          option.appendChild(document.createTextNode($lang.get('acl_lbl_preset_load')));
-          select.appendChild(option);
-          
-          for ( var i = 0; i < response.presets.length; i++ )
-          {
-            var preset = response.presets[i];
-            var option = document.createElement('option');
-            option.value = preset.rule_id;
-            option.preset_data = preset;
-            option.appendChild(document.createTextNode($lang.get(preset.preset_name)));
-            select.appendChild(option);
-          }
-          
-          para.appendChild(select);
-          prompt.appendChild(para);
-          
-          // buttons
-          var buttons = document.createElement('p');
-          
-          // load button
-          var btn_load = document.createElement('a');
-          btn_load.className = 'abutton abutton_green';
-          btn_load.style.fontWeight = 'bold';
-          btn_load.appendChild(document.createTextNode($lang.get('acl_btn_load_preset')));
-          btn_load.selectobj = select;
-          btn_load.onclick = function()
-          {
-            if ( this.selectobj.value == '0' )
-            {
-              alert($lang.get('acl_err_select_preset'));
-              return false;
-            }
-            // retrieve preset data
-            for ( var i = 0; i < this.selectobj.childNodes.length; i++ )
-            {
-              if ( this.selectobj.childNodes[i].tagName == 'OPTION' )
-              {
-                var node = this.selectobj.childNodes[i];
-                if ( node.value == this.selectobj.value )
-                {
-                  aclSetRulesAbsolute(node.preset_data.rules);
-                  break;
-                }
-              }
-            }
-            miniPromptDestroy(this);
-            return false;
-          }
-          btn_load.href = '#';
-          buttons.appendChild(btn_load);
-          
-          buttons.appendChild(document.createTextNode(' '));
-          
-          // cancel button
-          var btn_cancel = document.createElement('a');
-          btn_cancel.className = 'abutton';
-          btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
-          btn_cancel.onclick = function()
-          {
-            miniPromptDestroy(this);
-            return false;
-          }
-          btn_cancel.href = '#';
-          buttons.appendChild(btn_cancel);
-          
-          prompt.appendChild(buttons);
-        }
-        else
-        {
-          // "no presets"
-          prompt.innerHTML += '<p>' + $lang.get('acl_msg_no_presets', { close_flags: 'href="#" onclick="miniPromptDestroy(this); return false;"' }) + '</p>';
-        }
-      }
-    });
+	var prompt = miniPrompt(function(parent)
+		{
+			parent.innerHTML = '<img style="display: block; margin: 0 auto;" src="' + cdnPath + '/images/loading-big.gif" />';
+		});
+	var request = toJSONString({
+			mode: 'list_presets'
+		});
+	ajaxPost(stdAjaxPrefix + '&_mode=acljson', 'acl_params=' + ajaxEscape(request), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				if ( !check_json_response(ajax.responseText) )
+				{
+					miniPromptDestroy(prompt);
+					return handle_invalid_json(ajax.responseText);
+				}
+				var response = parseJSON(ajax.responseText);
+				if ( response.mode == 'error' )
+				{
+					alert(response.error);
+					miniPromptDestroy(prompt);
+					return false;
+				}
+				prompt = prompt.firstChild.nextSibling;
+				prompt.style.textAlign = 'center';
+				prompt.innerHTML = '<h3>' + $lang.get('acl_lbl_preset_load_title') + '</h3>';
+				
+				if ( response.presets.length > 0 )
+				{
+					// selection box
+					var para = document.createElement('p');
+					var select = document.createElement('select');
+					
+					var option = document.createElement('option');
+					option.value = '0';
+					option.appendChild(document.createTextNode($lang.get('acl_lbl_preset_load')));
+					select.appendChild(option);
+					
+					for ( var i = 0; i < response.presets.length; i++ )
+					{
+						var preset = response.presets[i];
+						var option = document.createElement('option');
+						option.value = preset.rule_id;
+						option.preset_data = preset;
+						option.appendChild(document.createTextNode($lang.get(preset.preset_name)));
+						select.appendChild(option);
+					}
+					
+					para.appendChild(select);
+					prompt.appendChild(para);
+					
+					// buttons
+					var buttons = document.createElement('p');
+					
+					// load button
+					var btn_load = document.createElement('a');
+					btn_load.className = 'abutton abutton_green';
+					btn_load.style.fontWeight = 'bold';
+					btn_load.appendChild(document.createTextNode($lang.get('acl_btn_load_preset')));
+					btn_load.selectobj = select;
+					btn_load.onclick = function()
+					{
+						if ( this.selectobj.value == '0' )
+						{
+							alert($lang.get('acl_err_select_preset'));
+							return false;
+						}
+						// retrieve preset data
+						for ( var i = 0; i < this.selectobj.childNodes.length; i++ )
+						{
+							if ( this.selectobj.childNodes[i].tagName == 'OPTION' )
+							{
+								var node = this.selectobj.childNodes[i];
+								if ( node.value == this.selectobj.value )
+								{
+									aclSetRulesAbsolute(node.preset_data.rules);
+									break;
+								}
+							}
+						}
+						miniPromptDestroy(this);
+						return false;
+					}
+					btn_load.href = '#';
+					buttons.appendChild(btn_load);
+					
+					buttons.appendChild(document.createTextNode(' '));
+					
+					// cancel button
+					var btn_cancel = document.createElement('a');
+					btn_cancel.className = 'abutton';
+					btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
+					btn_cancel.onclick = function()
+					{
+						miniPromptDestroy(this);
+						return false;
+					}
+					btn_cancel.href = '#';
+					buttons.appendChild(btn_cancel);
+					
+					prompt.appendChild(buttons);
+				}
+				else
+				{
+					// "no presets"
+					prompt.innerHTML += '<p>' + $lang.get('acl_msg_no_presets', { close_flags: 'href="#" onclick="miniPromptDestroy(this); return false;"' }) + '</p>';
+				}
+			}
+		});
 }
 
 function aclSetRulesAbsolute(rules)
 {
-  __aclSetAllRadios('i');
-  
-  var form = document.forms[aclManagerID + '_formobj'];
-  if (!form)
-  {
-    return false;
-  }
-  var inputs = form.getElementsByTagName('input');
-  var radios = new Array();
-  var dbg = '';
-  for(var i = 0; i < inputs.length; i++)
-  {
-    if(inputs[i].type == 'radio')
-      radios.push(inputs[i]);
-  }
-  for(var i in radios)
-  {
-    if ( typeof(rules[ radios[i]['name'] ]) == 'number' )
-    {
-      radios[i].checked = ( rules[radios[i]['name']] == radios[i].value );
-    }
-  }
+	__aclSetAllRadios('i');
+	
+	var form = document.forms[aclManagerID + '_formobj'];
+	if (!form)
+	{
+		return false;
+	}
+	var inputs = form.getElementsByTagName('input');
+	var radios = new Array();
+	var dbg = '';
+	for(var i = 0; i < inputs.length; i++)
+	{
+		if(inputs[i].type == 'radio')
+			radios.push(inputs[i]);
+	}
+	for(var i in radios)
+	{
+		if ( typeof(rules[ radios[i]['name'] ]) == 'number' )
+		{
+			radios[i].checked = ( rules[radios[i]['name']] == radios[i].value );
+		}
+	}
 }
 
 function aclShowPresetSave()
 {
-  miniPrompt(function(parent)
-    {
-      parent.style.textAlign = 'center';
-      
-      parent.innerHTML = '<h3>' + $lang.get('acl_lbl_preset_save_title') + '</h3>';
-      var input = document.createElement('input');
-      input.id = aclManagerID + '_preset_save';
-      input.type = 'text';
-      input.size = '30';
-      input.onkeypress = function(e)
-      {
-        // javascript sucks. IE and several others throw myriad errors unless it's done this way.
-        if ( e )
-        if ( e.keyCode )
-        if ( e.keyCode == 13 )
-        {
-          if ( aclSavePreset() )
-          {
-            if ( window.opera )
-            {
-              // damn weird opera bug.
-              var input = this;
-              setTimeout(function()
-                {
-                  miniPromptDestroy(input);
-                }, 10);
-            }
-            else
-            {
-              miniPromptDestroy(this);
-            }
-          }
-        }
-        else if ( e.keyCode == 27 )
-        {
-          miniPromptDestroy(this);
-        }
-      }
-      var para = document.createElement('p');
-      para.appendChild(input);
-      
-      parent.appendChild(para);
-      
-      // buttons
-      var buttons = document.createElement('p');
-      
-      // save button
-      var btn_save = document.createElement('a');
-      btn_save.className = 'abutton abutton_green';
-      btn_save.style.fontWeight = 'bold';
-      btn_save.appendChild(document.createTextNode($lang.get('acl_btn_save_preset')));
-      btn_save.selectobj = select;
-      btn_save.onclick = function()
-      {
-        if ( aclSavePreset() )
-        {
-          miniPromptDestroy(this);
-        }
-        return false;
-      }
-      btn_save.href = '#';
-      buttons.appendChild(btn_save);
-      
-      buttons.appendChild(document.createTextNode(' '));
-      
-      // cancel button
-      var btn_cancel = document.createElement('a');
-      btn_cancel.className = 'abutton';
-      btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
-      btn_cancel.onclick = function()
-      {
-        miniPromptDestroy(this);
-        return false;
-      }
-      btn_cancel.href = '#';
-      buttons.appendChild(btn_cancel);
-      
-      parent.appendChild(buttons);
-      
-      var timeout = ( aclDisableTransitionFX ) ? 10 : 1000;
-      setTimeout(function()
-        {
-          input.focus();
-        }, timeout);
-    });
+	miniPrompt(function(parent)
+		{
+			parent.style.textAlign = 'center';
+			
+			parent.innerHTML = '<h3>' + $lang.get('acl_lbl_preset_save_title') + '</h3>';
+			var input = document.createElement('input');
+			input.id = aclManagerID + '_preset_save';
+			input.type = 'text';
+			input.size = '30';
+			input.onkeypress = function(e)
+			{
+				// javascript sucks. IE and several others throw myriad errors unless it's done this way.
+				if ( e )
+				if ( e.keyCode )
+				if ( e.keyCode == 13 )
+				{
+					if ( aclSavePreset() )
+					{
+						if ( window.opera )
+						{
+							// damn weird opera bug.
+							var input = this;
+							setTimeout(function()
+								{
+									miniPromptDestroy(input);
+								}, 10);
+						}
+						else
+						{
+							miniPromptDestroy(this);
+						}
+					}
+				}
+				else if ( e.keyCode == 27 )
+				{
+					miniPromptDestroy(this);
+				}
+			}
+			var para = document.createElement('p');
+			para.appendChild(input);
+			
+			parent.appendChild(para);
+			
+			// buttons
+			var buttons = document.createElement('p');
+			
+			// save button
+			var btn_save = document.createElement('a');
+			btn_save.className = 'abutton abutton_green';
+			btn_save.style.fontWeight = 'bold';
+			btn_save.appendChild(document.createTextNode($lang.get('acl_btn_save_preset')));
+			btn_save.selectobj = select;
+			btn_save.onclick = function()
+			{
+				if ( aclSavePreset() )
+				{
+					miniPromptDestroy(this);
+				}
+				return false;
+			}
+			btn_save.href = '#';
+			buttons.appendChild(btn_save);
+			
+			buttons.appendChild(document.createTextNode(' '));
+			
+			// cancel button
+			var btn_cancel = document.createElement('a');
+			btn_cancel.className = 'abutton';
+			btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
+			btn_cancel.onclick = function()
+			{
+				miniPromptDestroy(this);
+				return false;
+			}
+			btn_cancel.href = '#';
+			buttons.appendChild(btn_cancel);
+			
+			parent.appendChild(buttons);
+			
+			var timeout = ( aclDisableTransitionFX ) ? 10 : 1000;
+			setTimeout(function()
+				{
+					input.focus();
+				}, timeout);
+		});
 }
 
 function aclSavePreset()
 {
-  var input = document.getElementById(aclManagerID + '_preset_save');
-  if ( trim(input.value) == '' )
-  {
-    alert($lang.get('acl_err_preset_name_empty'));
-    return false;
-  }
-  var form = document.forms[aclManagerID + '_formobj'], selections = {};
-  var dbg = '';
-  var warned_everyone = false;
-  for(var i in aclPermList)
-  {
-    selections[aclPermList[i]] = getRadioState(form, aclPermList[i], ['i', 1, 2, 3, 4]);
-    // If we're editing permissions for everyone on the entire site and the
-    // admin selected to deny privileges, give a stern warning about it.
-    if ( selections[aclPermList[i]] == 1 && aclDataCache.target_type == 1 /* ACL_TYPE_GROUP */ && aclDataCache.target_id == 1 && !warned_everyone )
-    {
-      warned_everyone = true;
-      if ( !confirm($lang.get('acl_msg_deny_everyone_confirm')) )
-      {
-        return false;
-      }
-    }
-    dbg += aclPermList[i] + ': ' + selections[aclPermList[i]] + "\n";
-    if(!selections[aclPermList[i]])
-    {
-      alert("Invalid return from getRadioState: "+i+": "+selections[i]+" ("+typeof(selections[i])+")");
-      return false;
-    }
-  }
-  
-  var packet = toJSONString({
-      mode: 'save_preset',
-      preset_name: input.value,
-      perms: selections
-    });
-  
-  var whitey = whiteOutElement(document.getElementById(aclManagerID));
-  
-  ajaxPost(stdAjaxPrefix + '&_mode=acljson', 'acl_params=' + ajaxEscape(packet), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        if ( !check_json_response(ajax.responseText) )
-        {
-          whitey.parentNode.removeChild(whitey);
-          return handle_invalid_json(ajax.responseText);
-        }
-        var response = parseJSON(ajax.responseText);
-        if ( response.mode == 'error' )
-        {
-          whitey.parentNode.removeChild(whitey);
-          alert(response.error);
-          return false;
-        }
-        whiteOutReportSuccess(whitey);
-      }
-    });
-  
-  return true;
+	var input = document.getElementById(aclManagerID + '_preset_save');
+	if ( trim(input.value) == '' )
+	{
+		alert($lang.get('acl_err_preset_name_empty'));
+		return false;
+	}
+	var form = document.forms[aclManagerID + '_formobj'], selections = {};
+	var dbg = '';
+	var warned_everyone = false;
+	for(var i in aclPermList)
+	{
+		selections[aclPermList[i]] = getRadioState(form, aclPermList[i], ['i', 1, 2, 3, 4]);
+		// If we're editing permissions for everyone on the entire site and the
+		// admin selected to deny privileges, give a stern warning about it.
+		if ( selections[aclPermList[i]] == 1 && aclDataCache.target_type == 1 /* ACL_TYPE_GROUP */ && aclDataCache.target_id == 1 && !warned_everyone )
+		{
+			warned_everyone = true;
+			if ( !confirm($lang.get('acl_msg_deny_everyone_confirm')) )
+			{
+				return false;
+			}
+		}
+		dbg += aclPermList[i] + ': ' + selections[aclPermList[i]] + "\n";
+		if(!selections[aclPermList[i]])
+		{
+			alert("Invalid return from getRadioState: "+i+": "+selections[i]+" ("+typeof(selections[i])+")");
+			return false;
+		}
+	}
+	
+	var packet = toJSONString({
+			mode: 'save_preset',
+			preset_name: input.value,
+			perms: selections
+		});
+	
+	var whitey = whiteOutElement(document.getElementById(aclManagerID));
+	
+	ajaxPost(stdAjaxPrefix + '&_mode=acljson', 'acl_params=' + ajaxEscape(packet), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				if ( !check_json_response(ajax.responseText) )
+				{
+					whitey.parentNode.removeChild(whitey);
+					return handle_invalid_json(ajax.responseText);
+				}
+				var response = parseJSON(ajax.responseText);
+				if ( response.mode == 'error' )
+				{
+					whitey.parentNode.removeChild(whitey);
+					alert(response.error);
+					return false;
+				}
+				whiteOutReportSuccess(whitey);
+			}
+		});
+	
+	return true;
 }
 
 function array_keys(obj)
 {
-  keys = new Array();
-  for(var i in obj)
-    keys.push(i);
-  return keys;
+	keys = new Array();
+	for(var i in obj)
+		keys.push(i);
+	return keys;
 }
--- a/includes/clientside/static/admin-menu.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/admin-menu.js	Sun Mar 28 23:10:46 2010 -0400
@@ -24,20 +24,20 @@
 
 if ( /admin_menu_state=/.test(document.cookie) )
 {
-  var ck = (String(document.cookie).match(/admin_menu_state=([0-9]+)/))[1];
-  if(ck)
-  {
-    ck = parseInt(ck);
-  }
-  else
-  {
-    ck = 0;
-  }
-  ck = ( isNaN(ck) ) ? 0 : ck;
+	var ck = (String(document.cookie).match(/admin_menu_state=([0-9]+)/))[1];
+	if(ck)
+	{
+		ck = parseInt(ck);
+	}
+	else
+	{
+		ck = 0;
+	}
+	ck = ( isNaN(ck) ) ? 0 : ck;
 }
 else
 {
-  var ck = 0;
+	var ck = 0;
 }
 
 function tree (a_items, a_template, s_target) {
@@ -63,26 +63,26 @@
 		}
 	
 	this.toggle = function (n_id,co) { var o_item = this.a_index[n_id]; o_item.open(o_item.b_opened,co); };
-  this.open   = function (n_id,co) { var o_item = this.a_index[n_id]; o_item.open(false,co); };
+	this.open   = function (n_id,co) { var o_item = this.a_index[n_id]; o_item.open(false,co); };
 	this.select = function (n_id)    { return this.a_index[n_id].select(); };
 	this.mout   = function (n_id)    { this.a_index[n_id].upstatus(true) };
 	this.mover  = function (n_id)    { this.a_index[n_id].upstatus() };
 
 	this.a_children = [];
 	for (var i = 0; i < a_items.length; i++)
-  {
+	{
 		new tree_item(this, i);
-  }
+	}
 
 	this.n_id = trees.length;
 	trees[this.n_id] = this;
 	
 	for (var i = 0; i < this.a_children.length; i++)
-  {
-    if ( s_target )
-      document.getElementById(s_target).innerHTML += this.a_children[i].init();
-    else
-      document.write(this.a_children[i].init());
+	{
+		if ( s_target )
+			document.getElementById(s_target).innerHTML += this.a_children[i].init();
+		else
+			document.write(this.a_children[i].init());
 		this.a_children[i].open(false, true);
 	}
 }
@@ -103,57 +103,57 @@
 
 	this.a_children = [];
 	for (var i = 0; i < this.a_config.length - 2; i++)
-  {
+	{
 		new tree_item(this, i);
-  }
-  
+	}
+	
 	this.get_icon = item_get_icon;
 	this.open     = item_open;
 	this.select   = item_select;
 	this.init     = item_init;
 	this.upstatus = item_upstatus;
 	this.is_last  = function () { return this.n_order == this.o_parent.a_children.length - 1 };
-  
-  // CODE MODIFICATION
-  // added:
-    // Do we need to open the branch?
-    n = Math.pow(2, this.n_id);
-    var disp = ( ck & n ) ? true : false;
-    s = ( disp ) ? 'open' : 'closed';
-    //if(s=='open') alert(this.n_id + ': ' + s);
-    if(disp) setTimeout('trees['+trees.length+'].open('+this.n_id+', true);', 10);
-  // END MODIFICATIONS
+	
+	// CODE MODIFICATION
+	// added:
+		// Do we need to open the branch?
+		n = Math.pow(2, this.n_id);
+		var disp = ( ck & n ) ? true : false;
+		s = ( disp ) ? 'open' : 'closed';
+		//if(s=='open') alert(this.n_id + ': ' + s);
+		if(disp) setTimeout('trees['+trees.length+'].open('+this.n_id+', true);', 10);
+	// END MODIFICATIONS
 }
 
 function item_open (b_close, nocookie) {
-  //alert('item_open('+this.n_id+');');
+	//alert('item_open('+this.n_id+');');
 	var o_idiv = get_element('i_div' + this.o_root.n_id + '_' + this.n_id);
 	if (!o_idiv) return;
 	
 	if (!o_idiv.innerHTML) {
 		var a_children = [];
 		for (var i = 0; i < this.a_children.length; i++)
-    {
+		{
 			a_children[i]= this.a_children[i].init();
-    }
+		}
 		o_idiv.innerHTML = a_children.join('');
 	}
 	o_idiv.style.display = (b_close ? 'none' : 'block');
-  
-  // CODE MODIFICATION
-  // added:
-    if(!nocookie)
-    {
-      // The idea here is to use a bitwise field. Nice 'n simple, right? Object of the game is to assemble
-      // a binary number that depicts the open/closed state of the entire menu in one cookie.
-      n = Math.pow(2, this.n_id);
-      ck = ( b_close ) ? ck-n : ck+n;
-      //alert('open(): doing the cookie routine for id '+this.n_id+"\nResult for bitwise op: "+ck);
-      createCookie('admin_menu_state', ck, 365);
-    } else {
-      //alert('open(): NOT doing the cookie routine for id '+this.n_id);
-    }
-  // END MODIFICATIONS
+	
+	// CODE MODIFICATION
+	// added:
+		if(!nocookie)
+		{
+			// The idea here is to use a bitwise field. Nice 'n simple, right? Object of the game is to assemble
+			// a binary number that depicts the open/closed state of the entire menu in one cookie.
+			n = Math.pow(2, this.n_id);
+			ck = ( b_close ) ? ck-n : ck+n;
+			//alert('open(): doing the cookie routine for id '+this.n_id+"\nResult for bitwise op: "+ck);
+			createCookie('admin_menu_state', ck, 365);
+		} else {
+			//alert('open(): NOT doing the cookie routine for id '+this.n_id);
+		}
+	// END MODIFICATIONS
 	
 	this.b_opened = !b_close;
 	var o_jicon = document.images['j_img' + this.o_root.n_id + '_' + this.n_id],
@@ -191,14 +191,14 @@
 	return '<table cellpadding="0" cellspacing="0" border="0"><tr><td nowrap="nowrap">' + (this.n_depth ? a_offset.join('') + (this.a_children.length
 		? '<a href="javascript: trees[' + this.o_root.n_id + '].toggle(' + this.n_id + ')" onmouseover="trees[' + this.o_root.n_id + '].mover(' + this.n_id + ')" onmouseout="trees[' + this.o_root.n_id + '].mout(' + this.n_id + ')"><img src="' + this.get_icon(true) + '" border="0" align="absbottom" name="j_img' + this.o_root.n_id + '_' + this.n_id + '"></a>'
 		: '<img src="' + this.get_icon(true) + '" border="0" align="absbottom">') : '')
-  // CODE MODIFICATION
-  // [7/20/08: removed ondblclick property (unneeded)]
-  // removed: 
+	// CODE MODIFICATION
+	// [7/20/08: removed ondblclick property (unneeded)]
+	// removed: 
 	//	+ '<a href="' + this.a_config[1] + '" target="' + this.o_root.a_tpl['target'] + '" onclick="return trees[' + this.o_root.n_id + '].select(' + this.n_id + ')" ondblclick="trees[' + this.o_root.n_id + '].toggle(' + this.n_id + ')" onmouseover="trees[' + this.o_root.n_id + '].mover(' + this.n_id + ')" onmouseout="trees[' + this.o_root.n_id + '].mout(' + this.n_id + ')" class="t' + this.o_root.n_id + 'i" id="i_txt' + this.o_root.n_id + '_' + this.n_id + '"><img src="' + this.get_icon() + '" border="0" align="absbottom" name="i_img' + this.o_root.n_id + '_' + this.n_id + '" class="t' + this.o_root.n_id + 'im">' + this.a_config[0] + '</a></td></tr></table>' + (this.a_children.length ? '<div id="i_div' + this.o_root.n_id + '_' + this.n_id + '" style="display:none"></div>' : '');
-  // added:
-  + '<a href="' + this.a_config[1] + '" target="' + this.o_root.a_tpl['target'] + '" onclick="return trees[' + this.o_root.n_id + '].select(' + this.n_id + ')" onmouseover="trees[' + this.o_root.n_id + '].mover(' + this.n_id + ')" onmouseout="trees[' + this.o_root.n_id + '].mout(' + this.n_id + ')" class="t' + this.o_root.n_id + 'i" id="i_txt' + this.o_root.n_id + '_' + this.n_id + '">' + this.a_config[0] + '</a></td></tr></table>' + (this.a_children.length ? '<div id="i_div' + this.o_root.n_id + '_' + this.n_id + '" style="display:none"></div>' : '');
-  // END MODIFICATIONS
-  alert('i_div' + this.o_root.n_id + '_' + this.n_id);
+	// added:
+	+ '<a href="' + this.a_config[1] + '" target="' + this.o_root.a_tpl['target'] + '" onclick="return trees[' + this.o_root.n_id + '].select(' + this.n_id + ')" onmouseover="trees[' + this.o_root.n_id + '].mover(' + this.n_id + ')" onmouseout="trees[' + this.o_root.n_id + '].mout(' + this.n_id + ')" class="t' + this.o_root.n_id + 'i" id="i_txt' + this.o_root.n_id + '_' + this.n_id + '">' + this.a_config[0] + '</a></td></tr></table>' + (this.a_children.length ? '<div id="i_div' + this.o_root.n_id + '_' + this.n_id + '" style="display:none"></div>' : '');
+	// END MODIFICATIONS
+	alert('i_div' + this.o_root.n_id + '_' + this.n_id);
 }
 
 function item_get_icon (b_junction) {
@@ -212,9 +212,9 @@
 
 function addslashes(text)
 {
-  text = text.replace(/\\/g, '\\\\');
-  text = text.replace(/"/g, '\\"');
-  return text;
+	text = text.replace(/\\/g, '\\\\');
+	text = text.replace(/"/g, '\\"');
+	return text;
 }
 
 // *******************************************
@@ -223,81 +223,81 @@
 
 function admin_table_onload(page)
 {
-  if ( page != namespace_list['Admin'] + 'GeneralConfig' )
-  {
-    return true;
-  }
-  var collapse_state = admin_table_get_cookie(page);
-  if ( collapse_state == 0 )
-    collapse_state = 0xffffffff;
-  $('#ajaxPageContainer > form > div.tblholder > table').each(function(i, table)
-    {
-      // skip if this is a one-row table
-      if ( $('tr:first', table).get(0) == $('tr:last', table).get(0) )
-        return;
-      
-      var open = (collapse_state >> i) & 1 > 0 ? true : false;
-      
-      var ypos = open ? 0 : 12;
-      
-      var div = document.createElement('div');
-      $(div).html(gen_sprite_html(scriptPath + '/themes/admin/images/thcollapse.png', 12, 12, ypos, 0));
-      $(div).click(function()
-        {
-          admin_table_click(this);
-        }).css('cursor', 'pointer').css('float', 'right');
-      div.thetable = table;
-      div.index = i;
-      div.thepage = page;
-      div.openstate = open;
-      $('tr > th:first', table).prepend(div);
-      if ( !open )
-        admin_table_collapse(table, true);
-    });
+	if ( page != namespace_list['Admin'] + 'GeneralConfig' )
+	{
+		return true;
+	}
+	var collapse_state = admin_table_get_cookie(page);
+	if ( collapse_state == 0 )
+		collapse_state = 0xffffffff;
+	$('#ajaxPageContainer > form > div.tblholder > table').each(function(i, table)
+		{
+			// skip if this is a one-row table
+			if ( $('tr:first', table).get(0) == $('tr:last', table).get(0) )
+				return;
+			
+			var open = (collapse_state >> i) & 1 > 0 ? true : false;
+			
+			var ypos = open ? 0 : 12;
+			
+			var div = document.createElement('div');
+			$(div).html(gen_sprite_html(scriptPath + '/themes/admin/images/thcollapse.png', 12, 12, ypos, 0));
+			$(div).click(function()
+				{
+					admin_table_click(this);
+				}).css('cursor', 'pointer').css('float', 'right');
+			div.thetable = table;
+			div.index = i;
+			div.thepage = page;
+			div.openstate = open;
+			$('tr > th:first', table).prepend(div);
+			if ( !open )
+				admin_table_collapse(table, true);
+		});
 }
 
 function admin_table_click(mydiv)
 {
-  var table = mydiv.thetable;
-  var i = mydiv.index;
-  var page = mydiv.thepage;
-  var collapse_state = admin_table_get_cookie(page);
-  
-  if ( mydiv.openstate )
-  {
-    $('img', mydiv).css('background-position', '0px -12px');
-    var new_collapse_state = collapse_state & ~Math.pow(2, i);
-    console.debug(new_collapse_state);
-    mydiv.openstate = false;
-    admin_table_collapse(table);
-  }
-  else
-  {
-    $('img', mydiv).css('background-position', '0px 0px');
-    var new_collapse_state = collapse_state | Math.pow(2, i);
-    console.debug(new_collapse_state);
-    mydiv.openstate = true;
-    admin_table_expand(table);
-  }
-  createCookie('admin_th:' + page, new_collapse_state, 3650);
+	var table = mydiv.thetable;
+	var i = mydiv.index;
+	var page = mydiv.thepage;
+	var collapse_state = admin_table_get_cookie(page);
+	
+	if ( mydiv.openstate )
+	{
+		$('img', mydiv).css('background-position', '0px -12px');
+		var new_collapse_state = collapse_state & ~Math.pow(2, i);
+		console.debug(new_collapse_state);
+		mydiv.openstate = false;
+		admin_table_collapse(table);
+	}
+	else
+	{
+		$('img', mydiv).css('background-position', '0px 0px');
+		var new_collapse_state = collapse_state | Math.pow(2, i);
+		console.debug(new_collapse_state);
+		mydiv.openstate = true;
+		admin_table_expand(table);
+	}
+	createCookie('admin_th:' + page, new_collapse_state, 3650);
 }
 
 function admin_table_get_cookie(page)
 {
-  var cookievalue = parseInt(readCookie('admin_th:' + page));
-  if ( isNaN(cookievalue) )
-    cookievalue = 0;
-  return cookievalue;
+	var cookievalue = parseInt(readCookie('admin_th:' + page));
+	if ( isNaN(cookievalue) )
+		cookievalue = 0;
+	return cookievalue;
 }
 
 function admin_table_collapse(table, noanim)
 {
-  var targetheight = $('tr > th:first', table).height();
-  $('tr', table).hide();
-  $('tr:first', table).show();
+	var targetheight = $('tr > th:first', table).height();
+	$('tr', table).hide();
+	$('tr:first', table).show();
 }
 
 function admin_table_expand(table)
 {
-  $('tr', table).show();
+	$('tr', table).show();
 }
--- a/includes/clientside/static/ajax.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/ajax.js	Sun Mar 28 23:10:46 2010 -0400
@@ -4,602 +4,602 @@
  
 window.ajaxReset = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  var ns_id = strToPageID(physical_title);
-  if ( ns_id[1] == 'Special' || ns_id[1] == 'Admin' )
-    return false;
-  enableUnload();
-  setAjaxLoading();
-  var redir = ( disable_redirect ) ? '&redirect=no' : '';
-  ajaxGet(append_sid(scriptPath + '/ajax.php?title=' + physical_title +'&_mode=getpage&noheaders' + redir), function(ajax) {
-    // Allow for 404 here, it's generated by the "page not found" error message
-    // (even with noheaders specified, probably should be fixed)
-    if ( ajax.readyState == 4 && ( ajax.status == 200 || ajax.status == 404 ) ) {
-      unsetAjaxLoading();
-      document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
-      selectButtonMajor('article');
-      unselectAllButtonsMinor();
-      // if we're on a userpage, call the onload function to rebuild the tabs
-      if ( typeof(userpage_onload) == 'function' )
-      {
-        window.userpage_blocks = [];
-        userpage_onload();
-      }
-    }
-  });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	var ns_id = strToPageID(physical_title);
+	if ( ns_id[1] == 'Special' || ns_id[1] == 'Admin' )
+		return false;
+	enableUnload();
+	setAjaxLoading();
+	var redir = ( disable_redirect ) ? '&redirect=no' : '';
+	ajaxGet(append_sid(scriptPath + '/ajax.php?title=' + physical_title +'&_mode=getpage&noheaders' + redir), function(ajax) {
+		// Allow for 404 here, it's generated by the "page not found" error message
+		// (even with noheaders specified, probably should be fixed)
+		if ( ajax.readyState == 4 && ( ajax.status == 200 || ajax.status == 404 ) ) {
+			unsetAjaxLoading();
+			document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
+			selectButtonMajor('article');
+			unselectAllButtonsMinor();
+			// if we're on a userpage, call the onload function to rebuild the tabs
+			if ( typeof(userpage_onload) == 'function' )
+			{
+				window.userpage_blocks = [];
+				userpage_onload();
+			}
+		}
+	});
 }
 
 // Miscellaneous AJAX applets
 
 window.ajaxProtect = function(existing_level)
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  
-  // touch this variable to allow it to be used in child functions
-  void(existing_level);
-  
-  // require re-auth
-  if ( auth_level <= USER_LEVEL_MEMBER )
-  {
-    load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
-    ajaxDynamicReauth(function(key)
-      {
-        ajaxProtect(existing_level);
-      }, user_level);
-    
-    return false;
-  }
-  
-  load_component(['messagebox', 'jquery', 'jquery-ui', 'l10n', 'fadefilter', 'flyin']);
-  
-  // preload language
-  $lang.get('meta_meta');
-  
-  var mp = miniPrompt(function(parent)
-    {
-      var icon_full = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 0, 0);
-      var icon_semi = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 22, 0);
-      var icon_none = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 44, 0);
-      
-      $(parent).append('<h3>' + $lang.get('onpage_protect_heading') + '</h3>');
-      $(parent).append('<p>' + $lang.get('onpage_protect_msg_select_level') + '</p>');
-      
-      $(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_1" name="protect_level" /> ' + icon_full + ' ' + $lang.get('onpage_protect_btn_full') + '</label></div>');
-      $(parent).append('<div class="protectlevel_hint" id="protect_level_1_hint">' + $lang.get('onpage_protect_btn_full_hint') + '</div>');
-      $(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_2" name="protect_level" /> ' + icon_semi + ' ' + $lang.get('onpage_protect_btn_semi') + '</label></div>');
-      $(parent).append('<div class="protectlevel_hint" id="protect_level_2_hint">' + $lang.get('onpage_protect_btn_semi_hint') + '</div>');
-      $(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_0" name="protect_level" /> ' + icon_none + ' ' + $lang.get('onpage_protect_btn_none') + '</label></div>');
-      $(parent).append('<div class="protectlevel_hint" id="protect_level_0_hint">' + $lang.get('onpage_protect_btn_none_hint') + '</div>');
-      
-      $(parent).append('<table class="protectreason"><tr><td valign="top">' + $lang.get('onpage_protect_lbl_reason') + '</td><td><input id="protect_reason" size="30" type="text" /><br /><small>' + $lang.get('onpage_protect_lbl_reason_hint') + '</small></td></tr></table>');
-      
-      $(parent).append('<p class="buttons"><a class="submitbutton abutton abutton_green" style="font-weight: bold;" href="#" onclick="ajaxProtectSubmit(this); return false;">' + $lang.get('onpage_protect_btn_submit') + '</a> <a class="submitbutton abutton" href="#" onclick="miniPromptDestroy(this); return false;">' + $lang.get('etc_cancel') + '</a></p>');
-      
-      $('.protectlevel', parent).css('line-height', '22px');
-      $('h3', parent).css('text-align', 'center');
-      $('.protectlevel_hint', parent)
-        .css('font-size', 'smaller')
-        .css('margin-left', '52px')
-        .hide();
-      $('p.buttons', parent).css('margin-top', '15px').css('text-align', 'center');
-      
-      if ( typeof(existing_level) == 'number' )
-      {
-        $('#protect_level_' + existing_level, parent).attr('checked', 'checked');
-        $('#protect_level_' + existing_level + '_hint', parent).show();
-        $('#protect_level_' + existing_level, parent).parent().append(' <small><span style="color: #050; background-color: #eee; padding: 0 4px;">' + $lang.get('onpage_protect_lbl_current') + '</span></small>');
-      }
-      
-      $('input:radio', parent).click(function()
-        {
-          var mp = miniPromptGetParent(this);
-          $('.protectlevel_hint:visible', mp).hide('blind', 150);
-          $('#' + this.id + '_hint').show('blind', 150);
-          $('#protect_reason').focus();
-        });
-      $('input:text', parent).keyup(function(e)
-        {
-          if ( e.keyCode == 13 )
-            ajaxProtectSubmit(this);
-        });
-    });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	
+	// touch this variable to allow it to be used in child functions
+	void(existing_level);
+	
+	// require re-auth
+	if ( auth_level <= USER_LEVEL_MEMBER )
+	{
+		load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
+		ajaxDynamicReauth(function(key)
+			{
+				ajaxProtect(existing_level);
+			}, user_level);
+		
+		return false;
+	}
+	
+	load_component(['messagebox', 'jquery', 'jquery-ui', 'l10n', 'fadefilter', 'flyin']);
+	
+	// preload language
+	$lang.get('meta_meta');
+	
+	var mp = miniPrompt(function(parent)
+		{
+			var icon_full = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 0, 0);
+			var icon_semi = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 22, 0);
+			var icon_none = gen_sprite_html(cdnPath + '/images/protect-icons.png', 22, 22, 44, 0);
+			
+			$(parent).append('<h3>' + $lang.get('onpage_protect_heading') + '</h3>');
+			$(parent).append('<p>' + $lang.get('onpage_protect_msg_select_level') + '</p>');
+			
+			$(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_1" name="protect_level" /> ' + icon_full + ' ' + $lang.get('onpage_protect_btn_full') + '</label></div>');
+			$(parent).append('<div class="protectlevel_hint" id="protect_level_1_hint">' + $lang.get('onpage_protect_btn_full_hint') + '</div>');
+			$(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_2" name="protect_level" /> ' + icon_semi + ' ' + $lang.get('onpage_protect_btn_semi') + '</label></div>');
+			$(parent).append('<div class="protectlevel_hint" id="protect_level_2_hint">' + $lang.get('onpage_protect_btn_semi_hint') + '</div>');
+			$(parent).append('<div class="protectlevel"><label><input type="radio" id="protect_level_0" name="protect_level" /> ' + icon_none + ' ' + $lang.get('onpage_protect_btn_none') + '</label></div>');
+			$(parent).append('<div class="protectlevel_hint" id="protect_level_0_hint">' + $lang.get('onpage_protect_btn_none_hint') + '</div>');
+			
+			$(parent).append('<table class="protectreason"><tr><td valign="top">' + $lang.get('onpage_protect_lbl_reason') + '</td><td><input id="protect_reason" size="30" type="text" /><br /><small>' + $lang.get('onpage_protect_lbl_reason_hint') + '</small></td></tr></table>');
+			
+			$(parent).append('<p class="buttons"><a class="submitbutton abutton abutton_green" style="font-weight: bold;" href="#" onclick="ajaxProtectSubmit(this); return false;">' + $lang.get('onpage_protect_btn_submit') + '</a> <a class="submitbutton abutton" href="#" onclick="miniPromptDestroy(this); return false;">' + $lang.get('etc_cancel') + '</a></p>');
+			
+			$('.protectlevel', parent).css('line-height', '22px');
+			$('h3', parent).css('text-align', 'center');
+			$('.protectlevel_hint', parent)
+				.css('font-size', 'smaller')
+				.css('margin-left', '52px')
+				.hide();
+			$('p.buttons', parent).css('margin-top', '15px').css('text-align', 'center');
+			
+			if ( typeof(existing_level) == 'number' )
+			{
+				$('#protect_level_' + existing_level, parent).attr('checked', 'checked');
+				$('#protect_level_' + existing_level + '_hint', parent).show();
+				$('#protect_level_' + existing_level, parent).parent().append(' <small><span style="color: #050; background-color: #eee; padding: 0 4px;">' + $lang.get('onpage_protect_lbl_current') + '</span></small>');
+			}
+			
+			$('input:radio', parent).click(function()
+				{
+					var mp = miniPromptGetParent(this);
+					$('.protectlevel_hint:visible', mp).hide('blind', 150);
+					$('#' + this.id + '_hint').show('blind', 150);
+					$('#protect_reason').focus();
+				});
+			$('input:text', parent).keyup(function(e)
+				{
+					if ( e.keyCode == 13 )
+						ajaxProtectSubmit(this);
+				});
+		});
 }
 
 window.ajaxProtectSubmit = function(el)
 {
-  var mp = miniPromptGetParent(el);
-  
-  var reason = trim($('#protect_reason', mp).attr('value'));
-  if ( reason == '' )
-  {
-    var oldbg = $('#protect_reason').css('background-color');
-    if ( jQuery.fx.off )
-    {
-      $('#protect_reason').css('background-color', '#a00');
-      setTimeout(function()
-        {
-          $('#protect_reason').css('background-color', oldbg);
-        }, 1000);
-    }
-    else
-    {
-      $('#protect_reason').css('background-color', '#a00').animate({ backgroundColor: oldbg }, 1000);
-    }
-    return false;
-  }
-  
-  var level = 0;
-  if ( $('#protect_level_1', mp).attr('checked') )
-    level = 1;
-  if ( $('#protect_level_2', mp).attr('checked') )
-    level = 2;
-  
-  var whitey = whiteOutMiniPrompt(mp);
-  $.post(stdAjaxPrefix + '&_mode=protect', { level: level, reason: reason }, function(response, statustext)
-    {
-      if ( response.success )
-      {
-        whiteOutReportSuccess(whitey);
-        // update protect button
-        var btn = $('#tb_ajax_protect_btn').get(0);
-        btn.level = level;
-        btn.setAttribute('onclick', null);
-        btn.onclick = null;
-        $(btn).click(function()
-          {
-            ajaxProtect(this.level);
-            return false;
-          });
-        var status = '';
-        switch(level)
-        {
-          case 1: status = $lang.get('onpage_btn_protect_on'); break;
-          case 0: status = $lang.get('onpage_btn_protect_off'); break;
-          case 2: status = $lang.get('onpage_btn_protect_semi'); break;
-        }
-        $('#tb_ajax_protect_status').text(status);
-      }
-      else
-      {
-        whiteOutReportFailure(whitey);
-        alert($lang.get('page_err_' + response.error));
-      }
-    }, 'json');
+	var mp = miniPromptGetParent(el);
+	
+	var reason = trim($('#protect_reason', mp).attr('value'));
+	if ( reason == '' )
+	{
+		var oldbg = $('#protect_reason').css('background-color');
+		if ( jQuery.fx.off )
+		{
+			$('#protect_reason').css('background-color', '#a00');
+			setTimeout(function()
+				{
+					$('#protect_reason').css('background-color', oldbg);
+				}, 1000);
+		}
+		else
+		{
+			$('#protect_reason').css('background-color', '#a00').animate({ backgroundColor: oldbg }, 1000);
+		}
+		return false;
+	}
+	
+	var level = 0;
+	if ( $('#protect_level_1', mp).attr('checked') )
+		level = 1;
+	if ( $('#protect_level_2', mp).attr('checked') )
+		level = 2;
+	
+	var whitey = whiteOutMiniPrompt(mp);
+	$.post(stdAjaxPrefix + '&_mode=protect', { level: level, reason: reason }, function(response, statustext)
+		{
+			if ( response.success )
+			{
+				whiteOutReportSuccess(whitey);
+				// update protect button
+				var btn = $('#tb_ajax_protect_btn').get(0);
+				btn.level = level;
+				btn.setAttribute('onclick', null);
+				btn.onclick = null;
+				$(btn).click(function()
+					{
+						ajaxProtect(this.level);
+						return false;
+					});
+				var status = '';
+				switch(level)
+				{
+					case 1: status = $lang.get('onpage_btn_protect_on'); break;
+					case 0: status = $lang.get('onpage_btn_protect_off'); break;
+					case 2: status = $lang.get('onpage_btn_protect_semi'); break;
+				}
+				$('#tb_ajax_protect_status').text(status);
+			}
+			else
+			{
+				whiteOutReportFailure(whitey);
+				alert($lang.get('page_err_' + response.error));
+			}
+		}, 'json');
 }
 
 window.ajaxRename = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  
-  // updated - 1.1.4 to use miniPrompt
-  load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
-  miniPrompt(ajaxRenameConstructDialog);
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	
+	// updated - 1.1.4 to use miniPrompt
+	load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
+	miniPrompt(ajaxRenameConstructDialog);
 }
 
 var ajaxRenameConstructDialog = function(div)
 {
-  // title
-  var heading = document.createElement('h3');
-  heading.appendChild(document.createTextNode($lang.get('ajax_rename_prompt_short')));
-  div.appendChild(heading);
-  
-  // form
-  var form = document.createElement('form');
-  form.action = 'javascript:void(0);';
-  
-  // box
-  var box = document.createElement('input');
-  box.size = '43';
-  box.style.width = '100%';
-  form.appendChild(box);
-  div.appendChild(form);
-  
-  // notice
-  var notice = document.createElement('small');
-  notice.appendChild(document.createTextNode($lang.get('ajax_rename_notice')));
-  div.appendChild(notice);
-  
-  // button area
-  var btndiv = document.createElement('div');
-  btndiv.className = 'mp-buttons';
-  
-  // buttons
-  var btn_submit = document.createElement('a');
-  btn_submit.href = '#';
-  btn_submit.appendChild(document.createTextNode($lang.get('etc_go')));
-  btn_submit.className = 'abutton abutton_green';
-  
-  var btn_cancel = document.createElement('a');
-  btn_cancel.href = '#';
-  btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
-  btn_cancel.className = 'abutton';
-  
-  btndiv.appendChild(btn_submit);
-  btndiv.appendChild(document.createTextNode(' | '));
-  btndiv.appendChild(btn_cancel);
-  div.appendChild(btndiv);
-  
-  // events
-  btn_submit.onclick = function()
-  {
-    ajaxRenameSubmit(this);
-    return false;
-  }
-  btn_cancel.onclick = function()
-  {
-    miniPromptDestroy(this);
-    return false;
-  }
-  form.onsubmit = function()
-  {
-    ajaxRenameSubmit(this);
-    return false;
-  }
-  
-  setTimeout(function()
-    {
-      box.focus();
-    }, ( aclDisableTransitionFX ? 200 : 750 ));
+	// title
+	var heading = document.createElement('h3');
+	heading.appendChild(document.createTextNode($lang.get('ajax_rename_prompt_short')));
+	div.appendChild(heading);
+	
+	// form
+	var form = document.createElement('form');
+	form.action = 'javascript:void(0);';
+	
+	// box
+	var box = document.createElement('input');
+	box.size = '43';
+	box.style.width = '100%';
+	form.appendChild(box);
+	div.appendChild(form);
+	
+	// notice
+	var notice = document.createElement('small');
+	notice.appendChild(document.createTextNode($lang.get('ajax_rename_notice')));
+	div.appendChild(notice);
+	
+	// button area
+	var btndiv = document.createElement('div');
+	btndiv.className = 'mp-buttons';
+	
+	// buttons
+	var btn_submit = document.createElement('a');
+	btn_submit.href = '#';
+	btn_submit.appendChild(document.createTextNode($lang.get('etc_go')));
+	btn_submit.className = 'abutton abutton_green';
+	
+	var btn_cancel = document.createElement('a');
+	btn_cancel.href = '#';
+	btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
+	btn_cancel.className = 'abutton';
+	
+	btndiv.appendChild(btn_submit);
+	btndiv.appendChild(document.createTextNode(' | '));
+	btndiv.appendChild(btn_cancel);
+	div.appendChild(btndiv);
+	
+	// events
+	btn_submit.onclick = function()
+	{
+		ajaxRenameSubmit(this);
+		return false;
+	}
+	btn_cancel.onclick = function()
+	{
+		miniPromptDestroy(this);
+		return false;
+	}
+	form.onsubmit = function()
+	{
+		ajaxRenameSubmit(this);
+		return false;
+	}
+	
+	setTimeout(function()
+		{
+			box.focus();
+		}, ( aclDisableTransitionFX ? 200 : 750 ));
 }
 
 window.ajaxRenameSubmit = function(obj)
 {
-  var box = miniPromptGetParent(obj);
-  if ( !box )
-    return false;
-  
-  var input = box.getElementsByTagName('input')[0];
-  if ( !input )
-    return false;
-    
-  var newname = input.value;
-  newname = trim(newname);
-  
-  if ( newname.length < 1 )
-  {
-    alert($lang.get('ajax_rename_too_short'));
-    return false;
-  }
-  
-  if ( !newname )
-  {
-    return false;
-  }
-  
-  var innerBox = getElementsByClassName(box, 'div', 'mp-body')[0];
-  var whiteout = whiteOutElement(innerBox);
-  whiteout.style.width = ( $dynano(whiteout).Width() - 78 ) + 'px';
-  whiteout.style.left = ( $dynano(whiteout).Left() + 44 ) + 'px';
-  
-  ajaxPost(stdAjaxPrefix + '&_mode=rename', 'newtitle=' + ajaxEscape(newname), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        whiteout.parentNode.removeChild(whiteout);
-        var response = String(ajax.responseText);
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(response);
-          return false;
-        }
-        response = parseJSON(response);
-        if ( response.success )
-        {
-          miniPromptDestroy(box, true);
-          ajaxRenameDoClientTransform(newname);
-          new MessageBox( MB_OK|MB_ICONINFORMATION, $lang.get('ajax_rename_success_title'), $lang.get('ajax_rename_success_body', { page_name_new: newname }) );
-          mb_previously_had_darkener = false;
-        }
-        else
-        {
-          var errmsg = $lang.get('page_err_' + response.error);
-          alert(errmsg);
-        }
-      }
-    }, true);
+	var box = miniPromptGetParent(obj);
+	if ( !box )
+		return false;
+	
+	var input = box.getElementsByTagName('input')[0];
+	if ( !input )
+		return false;
+		
+	var newname = input.value;
+	newname = trim(newname);
+	
+	if ( newname.length < 1 )
+	{
+		alert($lang.get('ajax_rename_too_short'));
+		return false;
+	}
+	
+	if ( !newname )
+	{
+		return false;
+	}
+	
+	var innerBox = getElementsByClassName(box, 'div', 'mp-body')[0];
+	var whiteout = whiteOutElement(innerBox);
+	whiteout.style.width = ( $dynano(whiteout).Width() - 78 ) + 'px';
+	whiteout.style.left = ( $dynano(whiteout).Left() + 44 ) + 'px';
+	
+	ajaxPost(stdAjaxPrefix + '&_mode=rename', 'newtitle=' + ajaxEscape(newname), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				whiteout.parentNode.removeChild(whiteout);
+				var response = String(ajax.responseText);
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(response);
+					return false;
+				}
+				response = parseJSON(response);
+				if ( response.success )
+				{
+					miniPromptDestroy(box, true);
+					ajaxRenameDoClientTransform(newname);
+					new MessageBox( MB_OK|MB_ICONINFORMATION, $lang.get('ajax_rename_success_title'), $lang.get('ajax_rename_success_body', { page_name_new: newname }) );
+					mb_previously_had_darkener = false;
+				}
+				else
+				{
+					var errmsg = $lang.get('page_err_' + response.error);
+					alert(errmsg);
+				}
+			}
+		}, true);
 }
 
 window.ajaxRenameDoClientTransform = function(newname)
 {
-  var obj = document.getElementById('h2PageName');
-  if ( obj )
-  {
-    obj.firstChild.nodeValue = newname;
-  }
-  document.title = newname;
+	var obj = document.getElementById('h2PageName');
+	if ( obj )
+	{
+		obj.firstChild.nodeValue = newname;
+	}
+	document.title = newname;
 }
 
 window.ajaxDeletePage = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  
-  // require re-auth
-  if ( auth_level <= USER_LEVEL_MEMBER )
-  {
-    load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
-    ajaxDynamicReauth(function(key)
-      {
-        ajaxDeletePage();
-      }, user_level);
-    
-    return false;
-  }
-  
-  load_component(['l10n', 'messagebox', 'jquery', 'jquery-ui', 'fadefilter', 'flyin']);
-  
-  // stage 1: prompt for reason and confirmation
-  miniPrompt(function(parent)
-    {
-      // heading/title
-      var h3 = document.createElement('h3');
-      h3.appendChild(document.createTextNode($lang.get('ajax_delete_header')));
-      parent.appendChild(h3);
-      
-      // "please enter your reason"
-      var p1 = document.createElement('p');
-      p1.appendChild(document.createTextNode($lang.get('ajax_delete_prompt_reason')));
-      parent.appendChild(p1);
-      
-      // textbox + label thereof
-      var p2 = document.createElement('p');
-      var tb = document.createElement('input');
-      var dl = document.createElement('label');
-      
-      tb.type = 'text';
-      tb.size = '30';
-      tb.onkeyup = function(e)
-      {
-        if ( e )
-        if ( e.keyCode )
-        if ( e.keyCode == 13 )
-        {
-          if ( ajaxDeletePageSubmit(this) )
-          {
-            miniPromptDestroy(this);
-          }
-        }
-        else if ( e.keyCode == 27 )
-        {
-          miniPromptDestroy(this);
-        }
-      }
-      
-      dl.appendChild(document.createTextNode($lang.get('ajax_delete_lbl_reason') + ' '));
-      dl.appendChild(tb);
-      p2.appendChild(dl);
-      parent.appendChild(p2);
-      
-      // notice underneath
-      var p3 = document.createElement('p');
-      p3.style.fontSize = 'smaller';
-      p3.appendChild(document.createTextNode($lang.get('ajax_delete_msg_confirm')));
-      parent.appendChild(p3);
-      
-      // confirmation + submit/cancel (structure)
-      var divleft  = document.createElement('div');
-      var divright = document.createElement('div');
-      var divclear = document.createElement('div');
-      
-      divleft.style.cssFloat = 'left';
-      divleft.style.styleFloat = 'left';
-      
-      divright.style.cssFloat = 'right';
-      divright.style.styleFloat = 'right';
-      
-      divclear.style.clear = 'both';
-      
-      parent.appendChild(divleft);
-      parent.appendChild(divright);
-      parent.appendChild(divclear);
-      
-      // confirmation + submit/cancel (controls)
-      var cb = document.createElement('input');
-      var cl = document.createElement('label');
-      
-      cb.type = 'checkbox';
-      cb.checked = false;
-      
-      // a bit of a hack here, doesn't seem to work in fx3
-      cb.onblur = function(e)
-      {
-        var parent = this.parentNode.parentNode.parentNode;
-        var submitter = parent.getElementsByTagName('a')[0];
-        if ( submitter )
-          submitter.focus();
-      }
-      
-      cl.appendChild(cb);
-      cl.appendChild(document.createTextNode(' ' + $lang.get('ajax_delete_lbl_confirm')));
-      divleft.appendChild(cl);
-      
-      var btn_submit = document.createElement('a');
-      btn_submit.className = 'abutton abutton_red';
-      btn_submit.href = '#';
-      btn_submit.appendChild(document.createTextNode($lang.get('ajax_delete_btn_delete')));
-      btn_submit.onclick = function()
-      {
-        if ( ajaxDeletePageSubmit(this) )
-        {
-          miniPromptDestroy(this);
-        }
-        return false;
-      }
-      
-      var btn_cancel = document.createElement('a');
-      btn_cancel.className = 'abutton';
-      btn_cancel.href = '#';
-      btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
-      btn_cancel.onclick = function()
-      {
-        miniPromptDestroy(this);
-        return false;
-      }
-      
-      divright.appendChild(btn_submit);
-      divright.appendChild(document.createTextNode(' '));
-      divright.appendChild(btn_cancel);
-      
-      var timeout = ( aclDisableTransitionFX ) ? 10 : 1000;
-      setTimeout(function()
-        {
-          tb.focus();
-        }, timeout);
-    });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	
+	// require re-auth
+	if ( auth_level <= USER_LEVEL_MEMBER )
+	{
+		load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
+		ajaxDynamicReauth(function(key)
+			{
+				ajaxDeletePage();
+			}, user_level);
+		
+		return false;
+	}
+	
+	load_component(['l10n', 'messagebox', 'jquery', 'jquery-ui', 'fadefilter', 'flyin']);
+	
+	// stage 1: prompt for reason and confirmation
+	miniPrompt(function(parent)
+		{
+			// heading/title
+			var h3 = document.createElement('h3');
+			h3.appendChild(document.createTextNode($lang.get('ajax_delete_header')));
+			parent.appendChild(h3);
+			
+			// "please enter your reason"
+			var p1 = document.createElement('p');
+			p1.appendChild(document.createTextNode($lang.get('ajax_delete_prompt_reason')));
+			parent.appendChild(p1);
+			
+			// textbox + label thereof
+			var p2 = document.createElement('p');
+			var tb = document.createElement('input');
+			var dl = document.createElement('label');
+			
+			tb.type = 'text';
+			tb.size = '30';
+			tb.onkeyup = function(e)
+			{
+				if ( e )
+				if ( e.keyCode )
+				if ( e.keyCode == 13 )
+				{
+					if ( ajaxDeletePageSubmit(this) )
+					{
+						miniPromptDestroy(this);
+					}
+				}
+				else if ( e.keyCode == 27 )
+				{
+					miniPromptDestroy(this);
+				}
+			}
+			
+			dl.appendChild(document.createTextNode($lang.get('ajax_delete_lbl_reason') + ' '));
+			dl.appendChild(tb);
+			p2.appendChild(dl);
+			parent.appendChild(p2);
+			
+			// notice underneath
+			var p3 = document.createElement('p');
+			p3.style.fontSize = 'smaller';
+			p3.appendChild(document.createTextNode($lang.get('ajax_delete_msg_confirm')));
+			parent.appendChild(p3);
+			
+			// confirmation + submit/cancel (structure)
+			var divleft  = document.createElement('div');
+			var divright = document.createElement('div');
+			var divclear = document.createElement('div');
+			
+			divleft.style.cssFloat = 'left';
+			divleft.style.styleFloat = 'left';
+			
+			divright.style.cssFloat = 'right';
+			divright.style.styleFloat = 'right';
+			
+			divclear.style.clear = 'both';
+			
+			parent.appendChild(divleft);
+			parent.appendChild(divright);
+			parent.appendChild(divclear);
+			
+			// confirmation + submit/cancel (controls)
+			var cb = document.createElement('input');
+			var cl = document.createElement('label');
+			
+			cb.type = 'checkbox';
+			cb.checked = false;
+			
+			// a bit of a hack here, doesn't seem to work in fx3
+			cb.onblur = function(e)
+			{
+				var parent = this.parentNode.parentNode.parentNode;
+				var submitter = parent.getElementsByTagName('a')[0];
+				if ( submitter )
+					submitter.focus();
+			}
+			
+			cl.appendChild(cb);
+			cl.appendChild(document.createTextNode(' ' + $lang.get('ajax_delete_lbl_confirm')));
+			divleft.appendChild(cl);
+			
+			var btn_submit = document.createElement('a');
+			btn_submit.className = 'abutton abutton_red';
+			btn_submit.href = '#';
+			btn_submit.appendChild(document.createTextNode($lang.get('ajax_delete_btn_delete')));
+			btn_submit.onclick = function()
+			{
+				if ( ajaxDeletePageSubmit(this) )
+				{
+					miniPromptDestroy(this);
+				}
+				return false;
+			}
+			
+			var btn_cancel = document.createElement('a');
+			btn_cancel.className = 'abutton';
+			btn_cancel.href = '#';
+			btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
+			btn_cancel.onclick = function()
+			{
+				miniPromptDestroy(this);
+				return false;
+			}
+			
+			divright.appendChild(btn_submit);
+			divright.appendChild(document.createTextNode(' '));
+			divright.appendChild(btn_cancel);
+			
+			var timeout = ( aclDisableTransitionFX ) ? 10 : 1000;
+			setTimeout(function()
+				{
+					tb.focus();
+				}, timeout);
+		});
 }
 
 window.ajaxDeletePageSubmit = function(prompt_obj)
 {
-  prompt_obj = miniPromptGetParent(prompt_obj).childNodes[1];
-  var inputs = prompt_obj.getElementsByTagName('input');
-  var reason = inputs[0];
-  var confirm = inputs[1];
-  
-  if ( trim(reason.value) == '' )
-  {
-    // flash the background of the reason entry
-    $(reason.parentNode).effect("highlight", {}, 1000);
-    return false;
-  }
-  
-  if ( !confirm.checked )
-  {
-    // flash the background of the confirm checkbox
-    $(confirm.parentNode).effect("highlight", {}, 1000);
-    return false;
-  }
-  
-  prompt_obj.innerHTML = '<img alt="loading" style="display: block; margin: 0 auto;" src="' + cdnPath + '/images/loading-big.gif" />';
-  
-  // tenemos la confirmación y la razón - borre la página.
-  setAjaxLoading();
-  ajaxPost(stdAjaxPrefix + '&_mode=deletepage', 'reason=' + ajaxEscape(trim(reason.value)), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        unsetAjaxLoading();
-        
-        // show the response in the same prompt window
-        prompt_obj.style.textAlign = 'center';
-        prompt_obj.innerHTML = '';
-        
-        var p1 = document.createElement('div');
-        p1.style.marginBottom = '15px';
-        p1.appendChild(document.createTextNode(ajax.responseText));
-        prompt_obj.appendChild(p1);
-        
-        var p2 = document.createElement('p');
-        var a = document.createElement('a');
-        a.className = 'abutton';
-        a.href = '#';
-        a.appendChild(document.createTextNode($lang.get('etc_close')));
-        a.onclick = function()
-        {
-          miniPromptDestroy(this);
-          window.location.reload();
-          return false;
-        }
-        p2.appendChild(a);
-        prompt_obj.appendChild(a);
-        
-        a.focus();
-      }
-    });
-  
-  return true;
+	prompt_obj = miniPromptGetParent(prompt_obj).childNodes[1];
+	var inputs = prompt_obj.getElementsByTagName('input');
+	var reason = inputs[0];
+	var confirm = inputs[1];
+	
+	if ( trim(reason.value) == '' )
+	{
+		// flash the background of the reason entry
+		$(reason.parentNode).effect("highlight", {}, 1000);
+		return false;
+	}
+	
+	if ( !confirm.checked )
+	{
+		// flash the background of the confirm checkbox
+		$(confirm.parentNode).effect("highlight", {}, 1000);
+		return false;
+	}
+	
+	prompt_obj.innerHTML = '<img alt="loading" style="display: block; margin: 0 auto;" src="' + cdnPath + '/images/loading-big.gif" />';
+	
+	// tenemos la confirmación y la razón - borre la página.
+	setAjaxLoading();
+	ajaxPost(stdAjaxPrefix + '&_mode=deletepage', 'reason=' + ajaxEscape(trim(reason.value)), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				unsetAjaxLoading();
+				
+				// show the response in the same prompt window
+				prompt_obj.style.textAlign = 'center';
+				prompt_obj.innerHTML = '';
+				
+				var p1 = document.createElement('div');
+				p1.style.marginBottom = '15px';
+				p1.appendChild(document.createTextNode(ajax.responseText));
+				prompt_obj.appendChild(p1);
+				
+				var p2 = document.createElement('p');
+				var a = document.createElement('a');
+				a.className = 'abutton';
+				a.href = '#';
+				a.appendChild(document.createTextNode($lang.get('etc_close')));
+				a.onclick = function()
+				{
+					miniPromptDestroy(this);
+					window.location.reload();
+					return false;
+				}
+				p2.appendChild(a);
+				prompt_obj.appendChild(a);
+				
+				a.focus();
+			}
+		});
+	
+	return true;
 }
 
 window.ajaxDelVote = function()
 {
-  load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
-  
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  miniPromptMessage({
-      title: $lang.get('ajax_delvote_confirm_title'),
-      message: $lang.get('ajax_delvote_confirm_body'),
-      buttons: [
-        {
-          text: $lang.get('ajax_delvote_btn_submit'),
-          color: 'red',
-          style: {
-            fontWeight: 'bold'
-          },
-          onclick: function(e)
-          {
-            miniPromptDestroy(this);
-            setAjaxLoading();
-            ajaxGet(stdAjaxPrefix+'&_mode=delvote', function(ajax) {
-              if ( ajax.readyState == 4 && ajax.status == 200 ) {
-                unsetAjaxLoading();
-                alert(ajax.responseText);
-              }
-            }, true);
-          }
-        },
-        {
-          text: $lang.get('etc_cancel'),
-          onclick: function(e)
-          {
-            miniPromptDestroy(this);
-          }
-        }
-      ]
-    });
+	load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
+	
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	miniPromptMessage({
+			title: $lang.get('ajax_delvote_confirm_title'),
+			message: $lang.get('ajax_delvote_confirm_body'),
+			buttons: [
+				{
+					text: $lang.get('ajax_delvote_btn_submit'),
+					color: 'red',
+					style: {
+						fontWeight: 'bold'
+					},
+					onclick: function(e)
+					{
+						miniPromptDestroy(this);
+						setAjaxLoading();
+						ajaxGet(stdAjaxPrefix+'&_mode=delvote', function(ajax) {
+							if ( ajax.readyState == 4 && ajax.status == 200 ) {
+								unsetAjaxLoading();
+								alert(ajax.responseText);
+							}
+						}, true);
+					}
+				},
+				{
+					text: $lang.get('etc_cancel'),
+					onclick: function(e)
+					{
+						miniPromptDestroy(this);
+					}
+				}
+			]
+		});
 }
 
 window.ajaxResetDelVotes = function()
 {
-  load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
-  
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  miniPromptMessage({
-      title: $lang.get('ajax_delvote_reset_confirm_title'),
-      message: $lang.get('ajax_delvote_reset_confirm_body'),
-      buttons: [
-        {
-          text: $lang.get('ajax_delvote_reset_btn_submit'),
-          color: 'red',
-          style: {
-            fontWeight: 'bold'
-          },
-          onclick: function(e)
-          {
-            var box = miniPromptGetParent(this);
-            var whitey = whiteOutMiniPrompt(box);
-            ajaxGet(stdAjaxPrefix+'&_mode=resetdelvotes', function(ajax) {
-              if ( ajax.readyState == 4 && ajax.status == 200 ) {
-                whiteOutReportSuccess(whitey);
-                
-                item = document.getElementById('mdgDeleteVoteNoticeBox');
-                if(item)
-                {
-                  opacity('mdgDeleteVoteNoticeBox', 100, 0, 1000);
-                  setTimeout("document.getElementById('mdgDeleteVoteNoticeBox').style.display = 'none';", 1000);
-                }
-              }
-            }, true);
-          }
-        },
-        {
-          text: $lang.get('etc_cancel'),
-          onclick: function(e)
-          {
-            miniPromptDestroy(this);
-          }
-        }
-      ]
-    });
+	load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
+	
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	miniPromptMessage({
+			title: $lang.get('ajax_delvote_reset_confirm_title'),
+			message: $lang.get('ajax_delvote_reset_confirm_body'),
+			buttons: [
+				{
+					text: $lang.get('ajax_delvote_reset_btn_submit'),
+					color: 'red',
+					style: {
+						fontWeight: 'bold'
+					},
+					onclick: function(e)
+					{
+						var box = miniPromptGetParent(this);
+						var whitey = whiteOutMiniPrompt(box);
+						ajaxGet(stdAjaxPrefix+'&_mode=resetdelvotes', function(ajax) {
+							if ( ajax.readyState == 4 && ajax.status == 200 ) {
+								whiteOutReportSuccess(whitey);
+								
+								item = document.getElementById('mdgDeleteVoteNoticeBox');
+								if(item)
+								{
+									opacity('mdgDeleteVoteNoticeBox', 100, 0, 1000);
+									setTimeout("document.getElementById('mdgDeleteVoteNoticeBox').style.display = 'none';", 1000);
+								}
+							}
+						}, true);
+					}
+				},
+				{
+					text: $lang.get('etc_cancel'),
+					onclick: function(e)
+					{
+						miniPromptDestroy(this);
+					}
+				}
+			]
+		});
 }
 
 // Editing/saving category information
@@ -608,1181 +608,1181 @@
 
 window.ajaxCatEdit = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  setAjaxLoading();
-  ajaxGet(stdAjaxPrefix+'&_mode=catedit', function(ajax) {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      edit_open = false;
-      eval(ajax.responseText);
-    }
-  });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	setAjaxLoading();
+	ajaxGet(stdAjaxPrefix+'&_mode=catedit', function(ajax) {
+		if ( ajax.readyState == 4 && ajax.status == 200 ) {
+			unsetAjaxLoading();
+			edit_open = false;
+			eval(ajax.responseText);
+		}
+	});
 }
 
 window.ajaxCatSave = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  if(!catlist)
-  {
-    alert('Var catlist has no properties');
-    return;
-  }
-  query='';
-  for(i=0;i<catlist.length;i++)
-  {
-    var s = ( document.forms.mdgCatForm['mdgCat_' + catlist[i]]['checked'] ) ? true : false;
-    if(s) query = query + '&' + catlist[i] + '=true';
-  }
-  setAjaxLoading();
-  query = query.substring(1, query.length);
-  ajaxPost(stdAjaxPrefix+'&_mode=catsave', query, function(ajax) {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      edit_open = false;
-      if(ajax.responseText != 'GOOD') alert(ajax.responseText);
-      ajaxReset();
-    }
-  });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	if(!catlist)
+	{
+		alert('Var catlist has no properties');
+		return;
+	}
+	query='';
+	for(i=0;i<catlist.length;i++)
+	{
+		var s = ( document.forms.mdgCatForm['mdgCat_' + catlist[i]]['checked'] ) ? true : false;
+		if(s) query = query + '&' + catlist[i] + '=true';
+	}
+	setAjaxLoading();
+	query = query.substring(1, query.length);
+	ajaxPost(stdAjaxPrefix+'&_mode=catsave', query, function(ajax) {
+		if ( ajax.readyState == 4 && ajax.status == 200 ) {
+			unsetAjaxLoading();
+			edit_open = false;
+			if(ajax.responseText != 'GOOD') alert(ajax.responseText);
+			ajaxReset();
+		}
+	});
 }
 
 // History stuff
 
 window.ajaxHistory = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  setAjaxLoading();
-  ajaxGet(stdAjaxPrefix+'&_mode=histlist', function(ajax) {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      edit_open = false;
-      selectButtonMajor('article');
-      selectButtonMinor('history');
-      document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
-      buildDiffList();
-    }
-  });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	setAjaxLoading();
+	ajaxGet(stdAjaxPrefix+'&_mode=histlist', function(ajax) {
+		if ( ajax.readyState == 4 && ajax.status == 200 ) {
+			unsetAjaxLoading();
+			edit_open = false;
+			selectButtonMajor('article');
+			selectButtonMinor('history');
+			document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
+			buildDiffList();
+		}
+	});
 }
 
 window.ajaxHistView = function(oldid, ttl) {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  if(!ttl) ttl=title;
-  setAjaxLoading();
-  ajaxGet(append_sid(scriptPath+'/ajax.php?title='+ttl+'&_mode=getpage&oldid='+oldid), function(ajax) {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      edit_open = false;
-      document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
-    }
-  });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	if(!ttl) ttl=title;
+	setAjaxLoading();
+	ajaxGet(append_sid(scriptPath+'/ajax.php?title='+ttl+'&_mode=getpage&oldid='+oldid), function(ajax) {
+		if ( ajax.readyState == 4 && ajax.status == 200 ) {
+			unsetAjaxLoading();
+			edit_open = false;
+			document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
+		}
+	});
 }
 
 window.ajaxRollback = function(id) {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  setAjaxLoading();
-  ajaxGet(stdAjaxPrefix+'&_mode=rollback&id='+id, function(ajax) {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      
-      var response = String(ajax.responseText + '');
-      if ( !check_json_response(response) )
-      {
-        handle_invalid_json(response);
-        return false;
-      }
-      
-      response = parseJSON(response);
-      if ( response.success )
-      {
-        alert( $lang.get('page_msg_rb_success_' + response.action, { dateline: response.dateline }) )
-      }
-      else
-      {
-        if ( response.action )
-        {
-          alert( $lang.get('page_err_' + response.error, { action: response.action }) );
-        }
-        else
-        {
-          alert( $lang.get('page_err_' + response.error) );
-        }
-      }
-    }
-  });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	setAjaxLoading();
+	ajaxGet(stdAjaxPrefix+'&_mode=rollback&id='+id, function(ajax) {
+		if ( ajax.readyState == 4 && ajax.status == 200 ) {
+			unsetAjaxLoading();
+			
+			var response = String(ajax.responseText + '');
+			if ( !check_json_response(response) )
+			{
+				handle_invalid_json(response);
+				return false;
+			}
+			
+			response = parseJSON(response);
+			if ( response.success )
+			{
+				alert( $lang.get('page_msg_rb_success_' + response.action, { dateline: response.dateline }) )
+			}
+			else
+			{
+				if ( response.action )
+				{
+					alert( $lang.get('page_err_' + response.error, { action: response.action }) );
+				}
+				else
+				{
+					alert( $lang.get('page_err_' + response.error) );
+				}
+			}
+		}
+	});
 }
 
 window.ajaxClearLogs = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  
-  // require re-auth
-  if ( auth_level <= USER_LEVEL_MEMBER )
-  {
-    load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
-    ajaxDynamicReauth(function(key)
-      {
-        ajaxClearLogs();
-      }, user_level);
-    
-    return false;
-  }
-  
-  load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
-  
-  miniPromptMessage({
-      title: $lang.get('ajax_clearlogs_confirm_title'),
-      message: $lang.get('ajax_clearlogs_confirm_body'),
-      buttons: [
-        {
-          text: $lang.get('ajax_clearlogs_btn_submit'),
-          color: 'red',
-          style: {
-            fontWeight: 'bold'
-          },
-          onclick: function(e)
-          {
-            miniPromptDestroy(this);
-            setAjaxLoading();
-            ajaxGet(stdAjaxPrefix+'&_mode=flushlogs', function(ajax) {
-              if ( ajax.readyState == 4 && ajax.status == 200 ) {
-                unsetAjaxLoading();
-                alert(ajax.responseText);
-                window.location.reload();
-              }
-            });
-          }
-        },
-        {
-          text: $lang.get('etc_cancel'),
-          onclick: function(e)
-          {
-            miniPromptDestroy(this);
-          }
-        }
-      ]
-    });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	
+	// require re-auth
+	if ( auth_level <= USER_LEVEL_MEMBER )
+	{
+		load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
+		ajaxDynamicReauth(function(key)
+			{
+				ajaxClearLogs();
+			}, user_level);
+		
+		return false;
+	}
+	
+	load_component(['l10n', 'messagebox', 'flyin', 'fadefilter']);
+	
+	miniPromptMessage({
+			title: $lang.get('ajax_clearlogs_confirm_title'),
+			message: $lang.get('ajax_clearlogs_confirm_body'),
+			buttons: [
+				{
+					text: $lang.get('ajax_clearlogs_btn_submit'),
+					color: 'red',
+					style: {
+						fontWeight: 'bold'
+					},
+					onclick: function(e)
+					{
+						miniPromptDestroy(this);
+						setAjaxLoading();
+						ajaxGet(stdAjaxPrefix+'&_mode=flushlogs', function(ajax) {
+							if ( ajax.readyState == 4 && ajax.status == 200 ) {
+								unsetAjaxLoading();
+								alert(ajax.responseText);
+								window.location.reload();
+							}
+						});
+					}
+				},
+				{
+					text: $lang.get('etc_cancel'),
+					onclick: function(e)
+					{
+						miniPromptDestroy(this);
+					}
+				}
+			]
+		});
 }
 
 window.buildDiffList = function()
 {
-  arrDiff1Buttons = getElementsByClassName(document, 'input', 'clsDiff1Radio');
-  arrDiff2Buttons = getElementsByClassName(document, 'input', 'clsDiff2Radio');
-  var len = arrDiff1Buttons.length;
-  if ( len < 1 )
-    return false;
-  timelist = new Array();
-  for ( var i = 0; i < len; i++ )
-  {
-    timelist.push( arrDiff2Buttons[i].id.substr(6) );
-  }
-  timelist.push( arrDiff1Buttons[len-1].id.substr(6) );
-  delete(timelist.toJSONString);
-  for ( var i = 1; i < timelist.length-1; i++ )
-  {
-    if ( i >= timelist.length ) break;
-    arrDiff2Buttons[i].style.display = 'none';
-  }
+	arrDiff1Buttons = getElementsByClassName(document, 'input', 'clsDiff1Radio');
+	arrDiff2Buttons = getElementsByClassName(document, 'input', 'clsDiff2Radio');
+	var len = arrDiff1Buttons.length;
+	if ( len < 1 )
+		return false;
+	timelist = new Array();
+	for ( var i = 0; i < len; i++ )
+	{
+		timelist.push( arrDiff2Buttons[i].id.substr(6) );
+	}
+	timelist.push( arrDiff1Buttons[len-1].id.substr(6) );
+	delete(timelist.toJSONString);
+	for ( var i = 1; i < timelist.length-1; i++ )
+	{
+		if ( i >= timelist.length ) break;
+		arrDiff2Buttons[i].style.display = 'none';
+	}
 }
 
 window.selectDiff1Button = function(obj)
 {
-  var this_time = obj.id.substr(6);
-  var index = parseInt(in_array(this_time, timelist));
-  for ( var i = 0; i < timelist.length - 1; i++ )
-  {
-    if ( i < timelist.length - 1 )
-    {
-      var state = ( i < index ) ? 'inline' : 'none';
-      var id = 'diff2_' + timelist[i];
-      document.getElementById(id).style.display = state;
-      
-      // alert("Debug:\nIndex: "+index+"\nState: "+state+"\ni: "+i);
-    }
-  }
+	var this_time = obj.id.substr(6);
+	var index = parseInt(in_array(this_time, timelist));
+	for ( var i = 0; i < timelist.length - 1; i++ )
+	{
+		if ( i < timelist.length - 1 )
+		{
+			var state = ( i < index ) ? 'inline' : 'none';
+			var id = 'diff2_' + timelist[i];
+			document.getElementById(id).style.display = state;
+			
+			// alert("Debug:\nIndex: "+index+"\nState: "+state+"\ni: "+i);
+		}
+	}
 }
 
 window.selectDiff2Button = function(obj)
 {
-  var this_time = obj.id.substr(6);
-  var index = parseInt(in_array(this_time, timelist));
-  for ( var i = 1; i < timelist.length; i++ )
-  {
-    if ( i < timelist.length - 1 )
-    {
-      var state = ( i > index ) ? 'inline' : 'none';
-      var id = 'diff1_' + timelist[i];
-      document.getElementById(id).style.display = state;
-      
-      // alert("Debug:\nIndex: "+index+"\nState: "+state+"\ni: "+i);
-    }
-  }
+	var this_time = obj.id.substr(6);
+	var index = parseInt(in_array(this_time, timelist));
+	for ( var i = 1; i < timelist.length; i++ )
+	{
+		if ( i < timelist.length - 1 )
+		{
+			var state = ( i > index ) ? 'inline' : 'none';
+			var id = 'diff1_' + timelist[i];
+			document.getElementById(id).style.display = state;
+			
+			// alert("Debug:\nIndex: "+index+"\nState: "+state+"\ni: "+i);
+		}
+	}
 }
 
 window.ajaxHistDiff = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  var id1=false;
-  var id2=false;
-  for ( i = 0; i < arrDiff1Buttons.length; i++ )
-  {
-    k = i + '';
-    kpp = i + 1;
-    kpp = kpp + '';
-    if(arrDiff1Buttons[k].checked) id1 = arrDiff1Buttons[k].id.substr(6);
-    if(arrDiff2Buttons[k].checked) id2 = arrDiff2Buttons[k].id.substr(6);
-  }
-  if(!id1 || !id2) { alert('BUG: Couldn\'t get checked radiobutton state'); return; }
-  setAjaxLoading();
-  ajaxGet(stdAjaxPrefix+'&_mode=pagediff&diff1='+id1+'&diff2='+id2, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        unsetAjaxLoading();
-        document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
-      }
-    });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	var id1=false;
+	var id2=false;
+	for ( i = 0; i < arrDiff1Buttons.length; i++ )
+	{
+		k = i + '';
+		kpp = i + 1;
+		kpp = kpp + '';
+		if(arrDiff1Buttons[k].checked) id1 = arrDiff1Buttons[k].id.substr(6);
+		if(arrDiff2Buttons[k].checked) id2 = arrDiff2Buttons[k].id.substr(6);
+	}
+	if(!id1 || !id2) { alert('BUG: Couldn\'t get checked radiobutton state'); return; }
+	setAjaxLoading();
+	ajaxGet(stdAjaxPrefix+'&_mode=pagediff&diff1='+id1+'&diff2='+id2, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				unsetAjaxLoading();
+				document.getElementById('ajaxEditContainer').innerHTML = ajax.responseText;
+			}
+		});
 }
 
 // Change the user's preferred style/theme
 
 window.ajaxChangeStyle = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  load_component(['l10n', 'fadefilter', 'jquery', 'jquery-ui']);
-  
-  // force string fetch
-  $lang.get('etc_cancel');
-  
-  // preload some images
-  var i1 = new Image();
-  i1.src = cdnPath + '/images/loading-big.gif';
-  var i2 = new Image();
-  i2.src = cdnPath + '/images/check-large.png';
-  
-  darken(true, 70, 'theme-selector-shade');
-  
-  $('body').append('<div id="theme-selector-wrapper"><div id="theme-selector-body"><div id="theme-selector-inner"><div class="theme-selector-spinner"></div></div></div></div>');
-  $('#theme-selector-wrapper')
-    .css('top', String(getScrollOffset()) + 'px')
-    .css('left', 0)
-    .css('z-index', String( getHighestZ() + 20 ));
-  
-  $.get(stdAjaxPrefix + '&_mode=theme_list', {}, function(data, status)
-    {
-      $('#theme-selector-inner .theme-selector-spinner').fadeOut(650);
-      $('#theme-selector-body').animate({ width: 728 }, 600, function()
-        {
-          // avoiding jQuery's fade functions because they insist on toggling display as well
-          if ( !aclDisableTransitionFX )
-            changeOpac(0, 'theme-selector-inner');
-          $('#theme-selector-inner').html('<h3></h3>');
-          $('#theme-selector-inner > h3').text($lang.get('ajax_thmsel_lbl_choosetheme'));
-          $('#theme-selector-inner').append('<ul></ul>');
-          for ( var i = 0; i < data.length; i++ )
-          {
-            var bgi = data[i].have_thumb ? cdnPath + '/themes/' + data[i].theme_id + '/preview.png' : cdnPath + '/images/themepreview.png';
-            var maxheight = getHeight() - 325;
-            $('#theme-selector-inner > ul')
-              .css('clip', 'rect(0px, auto, auto, 0px)')
-              .css('overflow', 'auto')
-              .css('max-height', maxheight)
-              .append('<li id="theme_' + i + '"><a href="#"><span></span></a></li>');
-            $('#theme-selector-inner li#theme_' + i + ' > a')
-              .css('background-image', 'url(' + bgi + ')')
-              .attr('enano:theme_id', data[i].theme_id);
-            $('#theme-selector-inner li#theme_' + i + ' > a > span')
-              .text(data[i].theme_name);
-          }
-          $('#theme-selector-inner').append('<span class="menuclear"></span>');
-          $('#theme-selector-inner').append('<div style="padding-top: 40px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('etc_cancel') + '</a></div>');
-          
-          $('#theme-selector-body').animate({ height: $('#theme-selector-inner').height() + 30 }, 600, function()
-            {
-              if ( !aclDisableTransitionFX )
-                opacity('theme-selector-inner', 0, 100, 750);
-            });
-          
-          $('#theme-selector-inner li a').click(function()
-            {
-              var theme_id = $(this).attr('enano:theme_id');
-              $('span', this).html('&nbsp;').addClass('loading').fadeTo('fast', 0.6)
-              $.get(stdAjaxPrefix + '&_mode=get_styles', { theme_id: theme_id }, function(data, status)
-                {
-                  if ( data.length > 1 )
-                  {
-                    $('#theme-selector-inner').css('height', $('#theme-selector-inner').height()).fadeOut(600, function()
-                    {
-                      var div = document.createElement('div');
-                      if ( !aclDisableTransitionFX )
-                        domObjChangeOpac(0, div);
-                      
-                      $(div).attr('id', 'theme-selector-style-list').append('<h3></h3>');
-                      $('h3', div).text($lang.get('ajax_thmsel_lbl_choosestyle'));
-                      
-                      for ( var i = 0; i < data.length; i++ )
-                      {
-                        $(div).append('<a class="abutton block stylebtn" id="stylebtn_' + i + '" enano:style_id="' + data[i] + '">' + themeid_to_title(data[i]) + '</a>');
-                      }
-                      
-                      $(div).append('<div style="padding-top: 40px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('etc_cancel') + '</a></div>');
-                      
-                      if ( !aclDisableTransitionFX )
-                        changeOpac(0, 'theme-selector-style-list');
-                      $(this).html(div).show();
-                      
-                      $('#theme-selector-body').animate({width: 300, height: $('#theme-selector-style-list').height() + 30}, 300, function()
-                        {
-                          if ( !aclDisableTransitionFX )
-                            opacity('theme-selector-style-list', 0, 100, 500);
-                        });
-                      
-                      $('.stylebtn').click(function()
-                        {
-                          ajaxChangeThemeSetLoading();
-                          $.post(stdAjaxPrefix + '&_mode=change_theme', { theme_id: theme_id, style_id: $(this).attr('enano:style_id') }, function(data, status)
-                            {
-                              if ( data.error )
-                              {
-                                alert(data.error);
-                                ajaxChangeStyleClose();
-                                return false;
-                              }
-                              ajaxChangeThemeShowSuccess();
-                            }, 2000);
-                          
-                          return false;
-                        });
-                    });
-                  }
-                  else
-                  {
-                    if ( !data[0] )
-                    {
-                      alert('Didn\'t find any CSS files. :-/');
-                      ajaxChangeStyleClose();
-                    }
-                    
-                    $.post(stdAjaxPrefix + '&_mode=change_theme', { theme_id: theme_id, style_id: data[0] }, function(data, status)
-                      {
-                        if ( data.error )
-                        {
-                          alert(data.error);
-                          ajaxChangeStyleClose();
-                          return false;
-                        }
-                        ajaxChangeThemeShowSuccess();
-                      }, 'json');
-                  }
-                }, 'json');
-              return false;
-            }); // click function
-        }); // animate
-    }, 'json'); // get
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	load_component(['l10n', 'fadefilter', 'jquery', 'jquery-ui']);
+	
+	// force string fetch
+	$lang.get('etc_cancel');
+	
+	// preload some images
+	var i1 = new Image();
+	i1.src = cdnPath + '/images/loading-big.gif';
+	var i2 = new Image();
+	i2.src = cdnPath + '/images/check-large.png';
+	
+	darken(true, 70, 'theme-selector-shade');
+	
+	$('body').append('<div id="theme-selector-wrapper"><div id="theme-selector-body"><div id="theme-selector-inner"><div class="theme-selector-spinner"></div></div></div></div>');
+	$('#theme-selector-wrapper')
+		.css('top', String(getScrollOffset()) + 'px')
+		.css('left', 0)
+		.css('z-index', String( getHighestZ() + 20 ));
+	
+	$.get(stdAjaxPrefix + '&_mode=theme_list', {}, function(data, status)
+		{
+			$('#theme-selector-inner .theme-selector-spinner').fadeOut(650);
+			$('#theme-selector-body').animate({ width: 728 }, 600, function()
+				{
+					// avoiding jQuery's fade functions because they insist on toggling display as well
+					if ( !aclDisableTransitionFX )
+						changeOpac(0, 'theme-selector-inner');
+					$('#theme-selector-inner').html('<h3></h3>');
+					$('#theme-selector-inner > h3').text($lang.get('ajax_thmsel_lbl_choosetheme'));
+					$('#theme-selector-inner').append('<ul></ul>');
+					for ( var i = 0; i < data.length; i++ )
+					{
+						var bgi = data[i].have_thumb ? cdnPath + '/themes/' + data[i].theme_id + '/preview.png' : cdnPath + '/images/themepreview.png';
+						var maxheight = getHeight() - 325;
+						$('#theme-selector-inner > ul')
+							.css('clip', 'rect(0px, auto, auto, 0px)')
+							.css('overflow', 'auto')
+							.css('max-height', maxheight)
+							.append('<li id="theme_' + i + '"><a href="#"><span></span></a></li>');
+						$('#theme-selector-inner li#theme_' + i + ' > a')
+							.css('background-image', 'url(' + bgi + ')')
+							.attr('enano:theme_id', data[i].theme_id);
+						$('#theme-selector-inner li#theme_' + i + ' > a > span')
+							.text(data[i].theme_name);
+					}
+					$('#theme-selector-inner').append('<span class="menuclear"></span>');
+					$('#theme-selector-inner').append('<div style="padding-top: 40px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('etc_cancel') + '</a></div>');
+					
+					$('#theme-selector-body').animate({ height: $('#theme-selector-inner').height() + 30 }, 600, function()
+						{
+							if ( !aclDisableTransitionFX )
+								opacity('theme-selector-inner', 0, 100, 750);
+						});
+					
+					$('#theme-selector-inner li a').click(function()
+						{
+							var theme_id = $(this).attr('enano:theme_id');
+							$('span', this).html('&nbsp;').addClass('loading').fadeTo('fast', 0.6)
+							$.get(stdAjaxPrefix + '&_mode=get_styles', { theme_id: theme_id }, function(data, status)
+								{
+									if ( data.length > 1 )
+									{
+										$('#theme-selector-inner').css('height', $('#theme-selector-inner').height()).fadeOut(600, function()
+										{
+											var div = document.createElement('div');
+											if ( !aclDisableTransitionFX )
+												domObjChangeOpac(0, div);
+											
+											$(div).attr('id', 'theme-selector-style-list').append('<h3></h3>');
+											$('h3', div).text($lang.get('ajax_thmsel_lbl_choosestyle'));
+											
+											for ( var i = 0; i < data.length; i++ )
+											{
+												$(div).append('<a class="abutton block stylebtn" id="stylebtn_' + i + '" enano:style_id="' + data[i] + '">' + themeid_to_title(data[i]) + '</a>');
+											}
+											
+											$(div).append('<div style="padding-top: 40px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('etc_cancel') + '</a></div>');
+											
+											if ( !aclDisableTransitionFX )
+												changeOpac(0, 'theme-selector-style-list');
+											$(this).html(div).show();
+											
+											$('#theme-selector-body').animate({width: 300, height: $('#theme-selector-style-list').height() + 30}, 300, function()
+												{
+													if ( !aclDisableTransitionFX )
+														opacity('theme-selector-style-list', 0, 100, 500);
+												});
+											
+											$('.stylebtn').click(function()
+												{
+													ajaxChangeThemeSetLoading();
+													$.post(stdAjaxPrefix + '&_mode=change_theme', { theme_id: theme_id, style_id: $(this).attr('enano:style_id') }, function(data, status)
+														{
+															if ( data.error )
+															{
+																alert(data.error);
+																ajaxChangeStyleClose();
+																return false;
+															}
+															ajaxChangeThemeShowSuccess();
+														}, 2000);
+													
+													return false;
+												});
+										});
+									}
+									else
+									{
+										if ( !data[0] )
+										{
+											alert('Didn\'t find any CSS files. :-/');
+											ajaxChangeStyleClose();
+										}
+										
+										$.post(stdAjaxPrefix + '&_mode=change_theme', { theme_id: theme_id, style_id: data[0] }, function(data, status)
+											{
+												if ( data.error )
+												{
+													alert(data.error);
+													ajaxChangeStyleClose();
+													return false;
+												}
+												ajaxChangeThemeShowSuccess();
+											}, 'json');
+									}
+								}, 'json');
+							return false;
+						}); // click function
+				}); // animate
+		}, 'json'); // get
 }
 
 window.ajaxChangeThemeSetLoading = function()
 {
-  $('#theme-selector-body').animate({width: 130, height: 130});
-  $('#theme-selector-inner').empty().html('<div class="theme-selector-spinner"></div>');
+	$('#theme-selector-body').animate({width: 130, height: 130});
+	$('#theme-selector-inner').empty().html('<div class="theme-selector-spinner"></div>');
 }
 
 window.ajaxChangeThemeShowSuccess = function()
 {
-  if ( aclDisableTransitionFX )
-  {
-    $('#theme-selector-inner').empty();
-  }
-  else
-  {
-    setTimeout(function()
-      {
-        $('#theme-selector-inner').empty();
-      }, 10);
-  }
-  
-  $('#theme-selector-body').animate({width: 400, height: 300 }, 600, function()
-      {
-        $('#theme-selector-inner').append('<img src="' + cdnPath + '/images/check-large.png" alt=" " style="display: block; margin: 15px auto;" />');
-        $('#theme-selector-inner').append('<h3>' + $lang.get('ajax_thmsel_msg_success') + '</h3>');
-        $('#theme-selector-inner').append('<div style="padding-top: 20px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="window.location.reload(); return false;">' + $lang.get('ajax_thmsel_btn_reload') + '</a></div>');
-        $('#theme-selector-inner').append('<div style="padding-top: 25px;"><a href="#" style="font-size: smaller;" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('ajax_thmsel_btn_close') + '</a><br /><small>' + $lang.get('ajax_thmsel_btn_close_hint') + '</small></div>');
-        $('#theme-selector-inner').fadeIn();
-      });
+	if ( aclDisableTransitionFX )
+	{
+		$('#theme-selector-inner').empty();
+	}
+	else
+	{
+		setTimeout(function()
+			{
+				$('#theme-selector-inner').empty();
+			}, 10);
+	}
+	
+	$('#theme-selector-body').animate({width: 400, height: 300 }, 600, function()
+			{
+				$('#theme-selector-inner').append('<img src="' + cdnPath + '/images/check-large.png" alt=" " style="display: block; margin: 15px auto;" />');
+				$('#theme-selector-inner').append('<h3>' + $lang.get('ajax_thmsel_msg_success') + '</h3>');
+				$('#theme-selector-inner').append('<div style="padding-top: 20px;"><a class="abutton abutton_green" style="font-size: larger;" href="#" onclick="window.location.reload(); return false;">' + $lang.get('ajax_thmsel_btn_reload') + '</a></div>');
+				$('#theme-selector-inner').append('<div style="padding-top: 25px;"><a href="#" style="font-size: smaller;" onclick="ajaxChangeStyleClose(); return false;">' + $lang.get('ajax_thmsel_btn_close') + '</a><br /><small>' + $lang.get('ajax_thmsel_btn_close_hint') + '</small></div>');
+				$('#theme-selector-inner').fadeIn();
+			});
 }
 
 window.ajaxChangeStyleClose = function()
 {
-  setTimeout(function()
-    {
-      enlighten(false, 'theme-selector-shade');
-      $('#theme-selector-wrapper').fadeOut(500, function()
-        {
-          $(this).remove();
-        });
-    }, ( aclDisableTransitionFX ? 0 : 300));
-  if ( !aclDisableTransitionFX )
-    opacity('theme-selector-inner', 100, 0, 250);
+	setTimeout(function()
+		{
+			enlighten(false, 'theme-selector-shade');
+			$('#theme-selector-wrapper').fadeOut(500, function()
+				{
+					$(this).remove();
+				});
+		}, ( aclDisableTransitionFX ? 0 : 300));
+	if ( !aclDisableTransitionFX )
+		opacity('theme-selector-inner', 100, 0, 250);
 }
 
 function themeid_to_title(id)
 {
-  if ( typeof(id) != 'string' )
-    return false;
-  id = id.substr(0, 1).toUpperCase() + id.substr(1);
-  id = id.replace(/_/g, ' ');
-  id = id.replace(/-/g, ' ');
-  return id;
+	if ( typeof(id) != 'string' )
+		return false;
+	id = id.substr(0, 1).toUpperCase() + id.substr(1);
+	id = id.replace(/_/g, ' ');
+	id = id.replace(/-/g, ' ');
+	return id;
 }
 
 window.ajaxSetPassword = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  load_component('crypto');
-  pass = hex_sha1(document.getElementById('mdgPassSetField').value);
-  setAjaxLoading();
-  ajaxPost(stdAjaxPrefix+'&_mode=setpass', 'password='+pass, function(ajax)
-    {
-      unsetAjaxLoading();
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        alert(ajax.responseText);
-      }
-    }, true);
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	load_component('crypto');
+	pass = hex_sha1(document.getElementById('mdgPassSetField').value);
+	setAjaxLoading();
+	ajaxPost(stdAjaxPrefix+'&_mode=setpass', 'password='+pass, function(ajax)
+		{
+			unsetAjaxLoading();
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				alert(ajax.responseText);
+			}
+		}, true);
 }
 
 window.ajaxDisableEmbeddedPHP = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  if ( !confirm($lang.get('ajax_killphp_confirm')) )
-    return false;
-  var $killdiv = $dynano('php_killer');
-  if ( !$killdiv.object )
-  {
-    alert('Can\'t get kill div object');
-    return false;
-  }
-  $killdiv.object.innerHTML = '<img alt="Loading..." src="' + scriptPath + '/images/loading-big.gif" /><br />Making request...';
-  var url = makeUrlNS('Admin', 'Home', 'src=ajax');
-  ajaxPost(url, 'act=kill_php', function(ajax) {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        if ( ajax.responseText == '1' )
-        {
-          var $killdiv = $dynano('php_killer');
-          //$killdiv.object.innerHTML = '<img alt="Success" src="' + scriptPath + '/images/error.png" /><br />Embedded PHP in pages has been disabled.';
-          $killdiv.object.parentNode.removeChild($killdiv.object);
-          var newdiv = document.createElement('div');
-          // newdiv.style = $killdiv.object.style;
-          newdiv.className = $killdiv.object.className;
-          newdiv.innerHTML = '<img alt="Success" src="' + scriptPath + '/images/error.png" /><br />' + $lang.get('ajax_killphp_success');
-          $killdiv.object.parentNode.appendChild(newdiv);
-          $killdiv.object.parentNode.removeChild($killdiv.object);
-        }
-        else
-        {
-          var $killdiv = $dynano('php_killer');
-          $killdiv.object.innerHTML = ajax.responseText;
-        }
-      }
-    });
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	if ( !confirm($lang.get('ajax_killphp_confirm')) )
+		return false;
+	var $killdiv = $dynano('php_killer');
+	if ( !$killdiv.object )
+	{
+		alert('Can\'t get kill div object');
+		return false;
+	}
+	$killdiv.object.innerHTML = '<img alt="Loading..." src="' + scriptPath + '/images/loading-big.gif" /><br />Making request...';
+	var url = makeUrlNS('Admin', 'Home', 'src=ajax');
+	ajaxPost(url, 'act=kill_php', function(ajax) {
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				if ( ajax.responseText == '1' )
+				{
+					var $killdiv = $dynano('php_killer');
+					//$killdiv.object.innerHTML = '<img alt="Success" src="' + scriptPath + '/images/error.png" /><br />Embedded PHP in pages has been disabled.';
+					$killdiv.object.parentNode.removeChild($killdiv.object);
+					var newdiv = document.createElement('div');
+					// newdiv.style = $killdiv.object.style;
+					newdiv.className = $killdiv.object.className;
+					newdiv.innerHTML = '<img alt="Success" src="' + scriptPath + '/images/error.png" /><br />' + $lang.get('ajax_killphp_success');
+					$killdiv.object.parentNode.appendChild(newdiv);
+					$killdiv.object.parentNode.removeChild($killdiv.object);
+				}
+				else
+				{
+					var $killdiv = $dynano('php_killer');
+					$killdiv.object.innerHTML = ajax.responseText;
+				}
+			}
+		});
 }
 
 var catHTMLBuf = false;
 
 window.ajaxCatToTag = function()
 {
-  if ( KILL_SWITCH )
-    return false;
-  setAjaxLoading();
-  ajaxGet(stdAjaxPrefix + '&_mode=get_tags', function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        unsetAjaxLoading();
-        var resptext = String(ajax.responseText + ' ');
-        resptext = resptext.substr(0, resptext.length-1);
-        if ( resptext.substr(0, 1) != '{' )
-        {
-          handle_invalid_json(resptext);
-          return false;
-        }
-        var json = parseJSON(resptext);
-        var catbox = document.getElementById('mdgCatBox');
-        if ( !catbox )
-          return false;
-        var linkbox = catbox.parentNode.firstChild.firstChild.nextSibling;
-        linkbox.firstChild.nodeValue = $lang.get('catedit_catbox_link_showcategorization');
-        linkbox.onclick = function() { ajaxTagToCat(); return false; };
-        catHTMLBuf = catbox.innerHTML;
-        catbox.innerHTML = '';
-        catbox.appendChild(document.createTextNode($lang.get('tags_lbl_page_tags')+' '));
-        if ( json.tags.length < 1 )
-        {
-          catbox.appendChild(document.createTextNode($lang.get('tags_lbl_no_tags')));
-        }
-        for ( var i = 0; i < json.tags.length; i++ )
-        {
-          catbox.appendChild(document.createTextNode(json.tags[i].name));
-          if ( json.tags[i].can_del )
-          {
-            catbox.appendChild(document.createTextNode(' '));
-            var a = document.createElement('a');
-            a.appendChild(document.createTextNode('[X]'));
-            a.href = '#';
-            a._js_tag_id = json.tags[i].id;
-            a.onclick = function() { ajaxDeleteTag(this, this._js_tag_id); return false; }
-            catbox.appendChild(a);
-          }
-          if ( ( i + 1 ) < json.tags.length )
-            catbox.appendChild(document.createTextNode(', '));
-        }
-        if ( json.can_add )
-        {
-          catbox.appendChild(document.createTextNode(' '));
-          var addlink = document.createElement('a');
-          addlink.href = '#';
-          addlink.onclick = function() { try { ajaxAddTagStage1(); } catch(e) { }; return false; };
-          addlink.appendChild(document.createTextNode($lang.get('tags_btn_add_tag')));
-          catbox.appendChild(addlink);
-        }
-      }
-    });
+	if ( KILL_SWITCH )
+		return false;
+	setAjaxLoading();
+	ajaxGet(stdAjaxPrefix + '&_mode=get_tags', function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				unsetAjaxLoading();
+				var resptext = String(ajax.responseText + ' ');
+				resptext = resptext.substr(0, resptext.length-1);
+				if ( resptext.substr(0, 1) != '{' )
+				{
+					handle_invalid_json(resptext);
+					return false;
+				}
+				var json = parseJSON(resptext);
+				var catbox = document.getElementById('mdgCatBox');
+				if ( !catbox )
+					return false;
+				var linkbox = catbox.parentNode.firstChild.firstChild.nextSibling;
+				linkbox.firstChild.nodeValue = $lang.get('catedit_catbox_link_showcategorization');
+				linkbox.onclick = function() { ajaxTagToCat(); return false; };
+				catHTMLBuf = catbox.innerHTML;
+				catbox.innerHTML = '';
+				catbox.appendChild(document.createTextNode($lang.get('tags_lbl_page_tags')+' '));
+				if ( json.tags.length < 1 )
+				{
+					catbox.appendChild(document.createTextNode($lang.get('tags_lbl_no_tags')));
+				}
+				for ( var i = 0; i < json.tags.length; i++ )
+				{
+					catbox.appendChild(document.createTextNode(json.tags[i].name));
+					if ( json.tags[i].can_del )
+					{
+						catbox.appendChild(document.createTextNode(' '));
+						var a = document.createElement('a');
+						a.appendChild(document.createTextNode('[X]'));
+						a.href = '#';
+						a._js_tag_id = json.tags[i].id;
+						a.onclick = function() { ajaxDeleteTag(this, this._js_tag_id); return false; }
+						catbox.appendChild(a);
+					}
+					if ( ( i + 1 ) < json.tags.length )
+						catbox.appendChild(document.createTextNode(', '));
+				}
+				if ( json.can_add )
+				{
+					catbox.appendChild(document.createTextNode(' '));
+					var addlink = document.createElement('a');
+					addlink.href = '#';
+					addlink.onclick = function() { try { ajaxAddTagStage1(); } catch(e) { }; return false; };
+					addlink.appendChild(document.createTextNode($lang.get('tags_btn_add_tag')));
+					catbox.appendChild(addlink);
+				}
+			}
+		});
 }
 
 var addtag_open = false;
 
 window.ajaxAddTagStage1 = function()
 {
-  if ( addtag_open )
-    return false;
-  var catbox = document.getElementById('mdgCatBox');
-  var adddiv = document.createElement('div');
-  var text = document.createElement('input');
-  var addlink = document.createElement('a');
-  addlink.href = '#';
-  addlink.onclick = function() { ajaxAddTagStage2(this.parentNode.firstChild.nextSibling.value, this.parentNode); return false; };
-  addlink.appendChild(document.createTextNode($lang.get('tags_btn_add')));
-  text.type = 'text';
-  text.size = '15';
-  text.onkeyup = function(e)
-  {
-    if ( e.keyCode == 13 )
-    {
-      ajaxAddTagStage2(this.value, this.parentNode);
-    }
-  }
-  
-  adddiv.style.margin = '5px 0 0 0';
-  adddiv.appendChild(document.createTextNode($lang.get('tags_lbl_add_tag')+' '));
-  adddiv.appendChild(text);
-  adddiv.appendChild(document.createTextNode(' '));
-  adddiv.appendChild(addlink);
-  catbox.appendChild(adddiv);
-  addtag_open = true;
+	if ( addtag_open )
+		return false;
+	var catbox = document.getElementById('mdgCatBox');
+	var adddiv = document.createElement('div');
+	var text = document.createElement('input');
+	var addlink = document.createElement('a');
+	addlink.href = '#';
+	addlink.onclick = function() { ajaxAddTagStage2(this.parentNode.firstChild.nextSibling.value, this.parentNode); return false; };
+	addlink.appendChild(document.createTextNode($lang.get('tags_btn_add')));
+	text.type = 'text';
+	text.size = '15';
+	text.onkeyup = function(e)
+	{
+		if ( e.keyCode == 13 )
+		{
+			ajaxAddTagStage2(this.value, this.parentNode);
+		}
+	}
+	
+	adddiv.style.margin = '5px 0 0 0';
+	adddiv.appendChild(document.createTextNode($lang.get('tags_lbl_add_tag')+' '));
+	adddiv.appendChild(text);
+	adddiv.appendChild(document.createTextNode(' '));
+	adddiv.appendChild(addlink);
+	catbox.appendChild(adddiv);
+	addtag_open = true;
 }
 
 var addtag_nukeme = false;
 
 window.ajaxAddTagStage2 = function(tag, nukeme)
 {
-  if ( !addtag_open )
-    return false;
-  if ( addtag_nukeme )
-    return false;
-  addtag_nukeme = nukeme;
-  tag = ajaxEscape(tag);
-  setAjaxLoading();
-  ajaxPost(stdAjaxPrefix + '&_mode=addtag', 'tag=' + tag, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        unsetAjaxLoading();
-        var nukeme = addtag_nukeme;
-        addtag_nukeme = false;
-        var resptext = String(ajax.responseText + ' ');
-        resptext = resptext.substr(0, resptext.length-1);
-        if ( resptext.substr(0, 1) != '{' )
-        {
-          handle_invalid_json(resptext);
-          return false;
-        }
-        var json = parseJSON(resptext);
-        var parent = nukeme.parentNode;
-        parent.removeChild(nukeme);
-        addtag_open = false;
-        if ( json.success )
-        {
-          var node = parent.childNodes[1];
-          var insertafter = false;
-          var nukeafter = false;
-          if ( node.nodeValue == $lang.get('tags_lbl_no_tags') )
-          {
-            nukeafter = true;
-          }
-          insertafter = parent.childNodes[ parent.childNodes.length - 3 ];
-          // these need to be inserted in reverse order
-          if ( json.can_del )
-          {
-            var a = document.createElement('a');
-            a.appendChild(document.createTextNode('[X]'));
-            a.href = '#';
-            a._js_tag_id = json.tag_id;
-            a.onclick = function() { ajaxDeleteTag(this, this._js_tag_id); return false; }
-            insertAfter(parent, a, insertafter);
-            insertAfter(parent, document.createTextNode(' '), insertafter);
-          }
-          insertAfter(parent, document.createTextNode(json.tag), insertafter);
-          if ( !nukeafter )
-          {
-            insertAfter(parent, document.createTextNode(', '), insertafter);
-          }
-          if ( nukeafter )
-          {
-            parent.removeChild(insertafter);
-          }
-        }
-        else
-        {
-          alert(json.error);
-        }
-      }
-    });
+	if ( !addtag_open )
+		return false;
+	if ( addtag_nukeme )
+		return false;
+	addtag_nukeme = nukeme;
+	tag = ajaxEscape(tag);
+	setAjaxLoading();
+	ajaxPost(stdAjaxPrefix + '&_mode=addtag', 'tag=' + tag, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				unsetAjaxLoading();
+				var nukeme = addtag_nukeme;
+				addtag_nukeme = false;
+				var resptext = String(ajax.responseText + ' ');
+				resptext = resptext.substr(0, resptext.length-1);
+				if ( resptext.substr(0, 1) != '{' )
+				{
+					handle_invalid_json(resptext);
+					return false;
+				}
+				var json = parseJSON(resptext);
+				var parent = nukeme.parentNode;
+				parent.removeChild(nukeme);
+				addtag_open = false;
+				if ( json.success )
+				{
+					var node = parent.childNodes[1];
+					var insertafter = false;
+					var nukeafter = false;
+					if ( node.nodeValue == $lang.get('tags_lbl_no_tags') )
+					{
+						nukeafter = true;
+					}
+					insertafter = parent.childNodes[ parent.childNodes.length - 3 ];
+					// these need to be inserted in reverse order
+					if ( json.can_del )
+					{
+						var a = document.createElement('a');
+						a.appendChild(document.createTextNode('[X]'));
+						a.href = '#';
+						a._js_tag_id = json.tag_id;
+						a.onclick = function() { ajaxDeleteTag(this, this._js_tag_id); return false; }
+						insertAfter(parent, a, insertafter);
+						insertAfter(parent, document.createTextNode(' '), insertafter);
+					}
+					insertAfter(parent, document.createTextNode(json.tag), insertafter);
+					if ( !nukeafter )
+					{
+						insertAfter(parent, document.createTextNode(', '), insertafter);
+					}
+					if ( nukeafter )
+					{
+						parent.removeChild(insertafter);
+					}
+				}
+				else
+				{
+					alert(json.error);
+				}
+			}
+		});
 }
 
 window.ajaxDeleteTag = function(parentobj, tag_id)
 {
-  var arrDelete = [ parentobj, parentobj.previousSibling, parentobj.previousSibling.previousSibling ];
-  var parent = parentobj.parentNode;
-  var writeNoTags = false;
-  if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == ', ' )
-    arrDelete.push(parentobj.previousSibling.previousSibling.previousSibling);
-  else if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == $lang.get('tags_lbl_page_tags') + ' ' )
-    arrDelete.push(parentobj.nextSibling);
-  
-  if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == $lang.get('tags_lbl_page_tags') + ' ' &&
-       parentobj.nextSibling.nextSibling.firstChild )
-    if ( parentobj.nextSibling.nextSibling.firstChild.nodeValue == $lang.get('tags_btn_add_tag'))
-      writeNoTags = true;
-    
-  ajaxPost(stdAjaxPrefix + '&_mode=deltag', 'tag_id=' + String(tag_id), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        if ( ajax.responseText == 'success' )
-        {
-          for ( var i = 0; i < arrDelete.length; i++ )
-          {
-            try
-            {
-              parent.removeChild(arrDelete[i]);
-            } catch(e) {}
-          }
-          if ( writeNoTags )
-          {
-            var node1 = document.createTextNode($lang.get('tags_lbl_no_tags'));
-            var node2 = document.createTextNode(' ');
-            insertAfter(parent, node1, parent.firstChild);
-            insertAfter(parent, node2, node1);
-          }
-        }
-        else
-        {
-          alert(ajax.responseText);
-        }
-      }
-    });
+	var arrDelete = [ parentobj, parentobj.previousSibling, parentobj.previousSibling.previousSibling ];
+	var parent = parentobj.parentNode;
+	var writeNoTags = false;
+	if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == ', ' )
+		arrDelete.push(parentobj.previousSibling.previousSibling.previousSibling);
+	else if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == $lang.get('tags_lbl_page_tags') + ' ' )
+		arrDelete.push(parentobj.nextSibling);
+	
+	if ( parentobj.previousSibling.previousSibling.previousSibling.nodeValue == $lang.get('tags_lbl_page_tags') + ' ' &&
+ 			parentobj.nextSibling.nextSibling.firstChild )
+		if ( parentobj.nextSibling.nextSibling.firstChild.nodeValue == $lang.get('tags_btn_add_tag'))
+			writeNoTags = true;
+		
+	ajaxPost(stdAjaxPrefix + '&_mode=deltag', 'tag_id=' + String(tag_id), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				if ( ajax.responseText == 'success' )
+				{
+					for ( var i = 0; i < arrDelete.length; i++ )
+					{
+						try
+						{
+							parent.removeChild(arrDelete[i]);
+						} catch(e) {}
+					}
+					if ( writeNoTags )
+					{
+						var node1 = document.createTextNode($lang.get('tags_lbl_no_tags'));
+						var node2 = document.createTextNode(' ');
+						insertAfter(parent, node1, parent.firstChild);
+						insertAfter(parent, node2, node1);
+					}
+				}
+				else
+				{
+					alert(ajax.responseText);
+				}
+			}
+		});
 }
 
 window.ajaxTagToCat = function()
 {
-  if ( !catHTMLBuf )
-    return false;
-  var catbox = document.getElementById('mdgCatBox');
-  if ( !catbox )
-    return false;
-  addtag_open = false;
-  var linkbox = catbox.parentNode.firstChild.firstChild.nextSibling;
-  linkbox.firstChild.nodeValue = $lang.get('tags_catbox_link');
-  linkbox.onclick = function() { ajaxCatToTag(); return false; };
-  catbox.innerHTML = catHTMLBuf;
-  catHTMLBuf = false;
+	if ( !catHTMLBuf )
+		return false;
+	var catbox = document.getElementById('mdgCatBox');
+	if ( !catbox )
+		return false;
+	addtag_open = false;
+	var linkbox = catbox.parentNode.firstChild.firstChild.nextSibling;
+	linkbox.firstChild.nodeValue = $lang.get('tags_catbox_link');
+	linkbox.onclick = function() { ajaxCatToTag(); return false; };
+	catbox.innerHTML = catHTMLBuf;
+	catHTMLBuf = false;
 }
 
 var keepalive_interval = false;
 
 window.ajaxPingServer = function()
 {
-  ajaxGet(stdAjaxPrefix + '&_mode=ping', function(ajax)
-    {
-    });
+	ajaxGet(stdAjaxPrefix + '&_mode=ping', function(ajax)
+		{
+		});
 }
 
 window.ajaxToggleKeepalive = function()
 {
-  if ( readCookie('admin_keepalive') == '1' )
-  {
-    createCookie('admin_keepalive', '0', 3650);
-    if ( keepalive_interval )
-      clearInterval(keepalive_interval);
-    var span = document.getElementById('keepalivestat');
-    span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_off');
-  }
-  else
-  {
-    createCookie('admin_keepalive', '1', 3650);
-    if ( !keepalive_interval )
-      keepalive_interval = setInterval('ajaxPingServer();', 600000);
-    var span = document.getElementById('keepalivestat');
-    span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_on');
-    ajaxPingServer();
-  }
+	if ( readCookie('admin_keepalive') == '1' )
+	{
+		createCookie('admin_keepalive', '0', 3650);
+		if ( keepalive_interval )
+			clearInterval(keepalive_interval);
+		var span = document.getElementById('keepalivestat');
+		span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_off');
+	}
+	else
+	{
+		createCookie('admin_keepalive', '1', 3650);
+		if ( !keepalive_interval )
+			keepalive_interval = setInterval('ajaxPingServer();', 600000);
+		var span = document.getElementById('keepalivestat');
+		span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_on');
+		ajaxPingServer();
+	}
 }
 
 var keepalive_onload = function()
 {
-  if ( readCookie('admin_keepalive') == '1' )
-  {
-    if ( !keepalive_interval )
-      keepalive_interval = setInterval('ajaxPingServer();', 600000);
-    var span = document.getElementById('keepalivestat');
-    span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_on');
-  }
-  else
-  {
-    if ( keepalive_interval )
-      clearInterval(keepalive_interval);
-    var span = document.getElementById('keepalivestat');
-    span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_off');
-  }
+	if ( readCookie('admin_keepalive') == '1' )
+	{
+		if ( !keepalive_interval )
+			keepalive_interval = setInterval('ajaxPingServer();', 600000);
+		var span = document.getElementById('keepalivestat');
+		span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_on');
+	}
+	else
+	{
+		if ( keepalive_interval )
+			clearInterval(keepalive_interval);
+		var span = document.getElementById('keepalivestat');
+		span.firstChild.nodeValue = $lang.get('adm_btn_keepalive_off');
+	}
 };
 
 window.aboutKeepAlive = function()
 {
-  load_component(['messagebox', 'flyin', 'fadefilter']);
-  new MessageBox(MB_OK|MB_ICONINFORMATION, $lang.get('user_keepalive_info_title'), $lang.get('user_keepalive_info_body'));
+	load_component(['messagebox', 'flyin', 'fadefilter']);
+	new MessageBox(MB_OK|MB_ICONINFORMATION, $lang.get('user_keepalive_info_title'), $lang.get('user_keepalive_info_body'));
 }
 
 window.ajaxUpdateCheck = function(targetelement)
 {
-  if ( !document.getElementById(targetelement) )
-  {
-    return false;
-  }
-  var target = document.getElementById(targetelement);
-  target.innerHTML = '';
-  var img = document.createElement('img');
-  img.src = cdnPath + '/images/loading.gif';
-  img.alt = 'Loading...';
-  target.appendChild(img);
-  ajaxGet(makeUrlNS('Admin', 'Home/updates.xml'), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var releases = new Array();
-        var update_available = false;
-        if ( ajax.responseXML == null )
-        {
-          alert("Error fetching updates list:\n" + ajax.responseText);
-          return false;
-        }
-        if ( ajax.responseXML.firstChild.tagName == 'enano' )
-        {
-          var enanotag = ajax.responseXML.firstChild;
-          for ( var i = 0; i < enanotag.childNodes.length; i++ )
-          {
-            if ( enanotag.childNodes[i].tagName == 'error' )
-            {
-              alert(enanotag.childNodes[i].firstChild.nodeValue);
-            }
-            else if ( enanotag.childNodes[i].tagName == 'latest' )
-            {
-              // got <latest>
-              var latesttag = enanotag.childNodes[i];
-              for ( var j = 0; j < latesttag.childNodes.length; j++ )
-              {
-                var node = latesttag.childNodes[j];
-                if ( node.tagName == 'release' )
-                {
-                  var releasedata = new Object();
-                  for ( var k = 0; k < node.attributes.length; k++ )
-                  {
-                    releasedata[node.attributes[k].nodeName] = node.attributes[k].nodeValue;
-                  }
-                  releases.push(releasedata);
-                }
-                else if ( node.tagName == 'haveupdates' )
-                {
-                  update_available = true;
-                }
-              }
-              break;
-            }
-          }
-        }
-        else
-        {
-          return false;
-        }
-        var thediv = document.getElementById(targetelement);
-        thediv.innerHTML = '';
-        if ( !thediv )
-        {
-          return false;
-        }
-        if ( releases.length > 0 )
-        {
-          thediv.className = 'tblholder';
-          // FIXME: l10n
-          if ( update_available )
-          {
-            var infobox = document.createElement('div');
-            infobox.className = 'info-box-mini';
-            infobox.appendChild(document.createTextNode('An update for Enano is available. The newest release is highlighted below.'));
-            infobox.style.borderWidth = '0';
-            infobox.style.margin = '0 0 0 0';
-            thediv.appendChild(infobox);
-          }
-          else
-          {
-            var infobox = document.createElement('div');
-            infobox.className = 'info-box-mini';
-            infobox.appendChild(document.createTextNode('No new updates are available. The latest available releases are shown below.'));
-            infobox.style.borderWidth = '0';
-            infobox.style.margin = '0 0 0 0';
-            thediv.appendChild(infobox);
-          }
-          var table = document.createElement('table');
-          table.setAttribute('border', '0');
-          table.setAttribute('cellspacing', '1');
-          table.setAttribute('cellpadding', '4');
-          
-          var tr = document.createElement('tr');
-          
-          var td1 = document.createElement('th');
-          var td2 = document.createElement('th');
-          var td3 = document.createElement('th');
-          var td4 = document.createElement('th');
-          
-          // FIXME: l10n
-          td1.appendChild( document.createTextNode('Release type') );
-          td2.appendChild( document.createTextNode('Version') );
-          td3.appendChild( document.createTextNode('Code name') );
-          td4.appendChild( document.createTextNode('Release notes') );
-          
-          tr.appendChild(td1);
-          tr.appendChild(td2);
-          tr.appendChild(td3);
-          tr.appendChild(td4);
-            
-          table.appendChild(tr);
-          
-          var cls = 'row2';
-          
-          var j = 0;
-          for ( var i in releases )
-          {
-            j++;
-            if ( j > 5 )
-              break;
-            if ( update_available && j == 1 )
-              cls = 'row1_green';
-            else
-              cls = ( cls == 'row1' ) ? 'row2' : 'row1';
-            var release = releases[i];
-            var tr = document.createElement('tr');
-            
-            var td1 = document.createElement('td');
-            var td2 = document.createElement('td');
-            var td3 = document.createElement('td');
-            var td4 = document.createElement('td');
-            
-            td1.className = cls;
-            td2.className = cls;
-            td3.className = cls;
-            td4.className = cls;
-            
-            if ( release.tag )
-              td1.appendChild( document.createTextNode(release.tag) );
-            
-            if ( release.version )
-              td2.appendChild( document.createTextNode(release.version) );
-            
-            if ( release.codename )
-              td3.appendChild( document.createTextNode(release.codename) );
-            
-            if ( release.relnotes )
-            {
-              var a = document.createElement('a');
-              a.href = release.relnotes;
-              a.appendChild(document.createTextNode('View'));
-              td4.appendChild( a );
-            }
-            
-            tr.appendChild(td1);
-            tr.appendChild(td2);
-            tr.appendChild(td3);
-            tr.appendChild(td4);
-            
-            table.appendChild(tr);
-          }
-          thediv.appendChild(table);
-        }
-        else
-        {
-          thediv.appendChild(document.createTextNode('No releases available.'));
-        }
-      }
-    });
+	if ( !document.getElementById(targetelement) )
+	{
+		return false;
+	}
+	var target = document.getElementById(targetelement);
+	target.innerHTML = '';
+	var img = document.createElement('img');
+	img.src = cdnPath + '/images/loading.gif';
+	img.alt = 'Loading...';
+	target.appendChild(img);
+	ajaxGet(makeUrlNS('Admin', 'Home/updates.xml'), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var releases = new Array();
+				var update_available = false;
+				if ( ajax.responseXML == null )
+				{
+					alert("Error fetching updates list:\n" + ajax.responseText);
+					return false;
+				}
+				if ( ajax.responseXML.firstChild.tagName == 'enano' )
+				{
+					var enanotag = ajax.responseXML.firstChild;
+					for ( var i = 0; i < enanotag.childNodes.length; i++ )
+					{
+						if ( enanotag.childNodes[i].tagName == 'error' )
+						{
+							alert(enanotag.childNodes[i].firstChild.nodeValue);
+						}
+						else if ( enanotag.childNodes[i].tagName == 'latest' )
+						{
+							// got <latest>
+							var latesttag = enanotag.childNodes[i];
+							for ( var j = 0; j < latesttag.childNodes.length; j++ )
+							{
+								var node = latesttag.childNodes[j];
+								if ( node.tagName == 'release' )
+								{
+									var releasedata = new Object();
+									for ( var k = 0; k < node.attributes.length; k++ )
+									{
+										releasedata[node.attributes[k].nodeName] = node.attributes[k].nodeValue;
+									}
+									releases.push(releasedata);
+								}
+								else if ( node.tagName == 'haveupdates' )
+								{
+									update_available = true;
+								}
+							}
+							break;
+						}
+					}
+				}
+				else
+				{
+					return false;
+				}
+				var thediv = document.getElementById(targetelement);
+				thediv.innerHTML = '';
+				if ( !thediv )
+				{
+					return false;
+				}
+				if ( releases.length > 0 )
+				{
+					thediv.className = 'tblholder';
+					// FIXME: l10n
+					if ( update_available )
+					{
+						var infobox = document.createElement('div');
+						infobox.className = 'info-box-mini';
+						infobox.appendChild(document.createTextNode('An update for Enano is available. The newest release is highlighted below.'));
+						infobox.style.borderWidth = '0';
+						infobox.style.margin = '0 0 0 0';
+						thediv.appendChild(infobox);
+					}
+					else
+					{
+						var infobox = document.createElement('div');
+						infobox.className = 'info-box-mini';
+						infobox.appendChild(document.createTextNode('No new updates are available. The latest available releases are shown below.'));
+						infobox.style.borderWidth = '0';
+						infobox.style.margin = '0 0 0 0';
+						thediv.appendChild(infobox);
+					}
+					var table = document.createElement('table');
+					table.setAttribute('border', '0');
+					table.setAttribute('cellspacing', '1');
+					table.setAttribute('cellpadding', '4');
+					
+					var tr = document.createElement('tr');
+					
+					var td1 = document.createElement('th');
+					var td2 = document.createElement('th');
+					var td3 = document.createElement('th');
+					var td4 = document.createElement('th');
+					
+					// FIXME: l10n
+					td1.appendChild( document.createTextNode('Release type') );
+					td2.appendChild( document.createTextNode('Version') );
+					td3.appendChild( document.createTextNode('Code name') );
+					td4.appendChild( document.createTextNode('Release notes') );
+					
+					tr.appendChild(td1);
+					tr.appendChild(td2);
+					tr.appendChild(td3);
+					tr.appendChild(td4);
+						
+					table.appendChild(tr);
+					
+					var cls = 'row2';
+					
+					var j = 0;
+					for ( var i in releases )
+					{
+						j++;
+						if ( j > 5 )
+							break;
+						if ( update_available && j == 1 )
+							cls = 'row1_green';
+						else
+							cls = ( cls == 'row1' ) ? 'row2' : 'row1';
+						var release = releases[i];
+						var tr = document.createElement('tr');
+						
+						var td1 = document.createElement('td');
+						var td2 = document.createElement('td');
+						var td3 = document.createElement('td');
+						var td4 = document.createElement('td');
+						
+						td1.className = cls;
+						td2.className = cls;
+						td3.className = cls;
+						td4.className = cls;
+						
+						if ( release.tag )
+							td1.appendChild( document.createTextNode(release.tag) );
+						
+						if ( release.version )
+							td2.appendChild( document.createTextNode(release.version) );
+						
+						if ( release.codename )
+							td3.appendChild( document.createTextNode(release.codename) );
+						
+						if ( release.relnotes )
+						{
+							var a = document.createElement('a');
+							a.href = release.relnotes;
+							a.appendChild(document.createTextNode('View'));
+							td4.appendChild( a );
+						}
+						
+						tr.appendChild(td1);
+						tr.appendChild(td2);
+						tr.appendChild(td3);
+						tr.appendChild(td4);
+						
+						table.appendChild(tr);
+					}
+					thediv.appendChild(table);
+				}
+				else
+				{
+					thediv.appendChild(document.createTextNode('No releases available.'));
+				}
+			}
+		});
 }
 
 window.ajaxPluginAction = function(action, plugin_filename, btnobj, send_confirm)
 {
-  // if installing, uninstalling, or re-importing, confirm
-  if ( action == 'install' || action == 'uninstall' || action == 'reimport' )
-  {
-    var prompt = miniPrompt(function(div)
-      {
-        var txtholder = document.createElement('div');
-        txtholder.style.textAlign = 'center';
-        txtholder.appendChild(document.createTextNode($lang.get('acppl_msg_confirm_' + action)));
-        txtholder.appendChild(document.createElement('br'));
-        txtholder.appendChild(document.createElement('br'));
-        
-        // create buttons
-        var btn_go = document.createElement('a');
-        btn_go.className = 'abutton abutton_red';
-        btn_go.href = '#';
-        btn_go._action = action;
-        btn_go._filename = plugin_filename;
-        btn_go._button = btnobj;
-        btn_go.appendChild(document.createTextNode($lang.get('acppl_btn_' + action)));
-        btn_go.style.fontWeight = 'bold';
-        txtholder.appendChild(btn_go);
-        
-        // space
-        txtholder.appendChild(document.createTextNode(' '));
-        
-        // cancel
-        var btn_cancel = document.createElement('a');
-        btn_cancel.className = 'abutton abutton_blue';
-        btn_cancel.href = '#';
-        btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
-        
-        txtholder.appendChild(btn_cancel);
-        div.appendChild(txtholder);
-        
-        btn_go.onclick = function()
-        {
-          ajaxPluginAction(this._action + '_confirm', this._filename, this._button);
-          miniPromptDestroy(this);
-          return false;
-        };
-        btn_cancel.onclick = function()
-        {
-          miniPromptDestroy(this);
-          return false;
-        };
-      });
-    return true;
-  }
-  action = action.replace(/_confirm$/, '');
-  // white-out the plugin info box
-  if ( btnobj )
-  {
-    var td = btnobj.parentNode.parentNode.parentNode.parentNode;
-    var blackbox = whiteOutElement(td);
-  }
-  var request = {
-      mode: action,
-      plugin: plugin_filename
-    };
-  if ( send_confirm )
-  {
-    request.install_confirmed = true;
-  }
-  request = toJSONString(request);
-  ajaxPost(makeUrlNS('Admin', 'PluginManager/action.json'), 'r=' + ajaxEscape(request), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(response);
-          return false;
-        }
-        response = parseJSON(response);
-        if ( blackbox )
-        {
-          blackbox.parentNode.removeChild(blackbox);
-        }
-        if ( response.success )
-        {
-          ajaxPage( namespace_list['Admin'] + 'PluginManager' );
-          return true;
-        }
-        if ( response.need_confirm )
-        {
-          miniPromptMessage({
-              title: $lang.get(response.confirm_title),
-              message: $lang.get(response.confirm_body),
-              buttons: [
-                {
-                  text: $lang.get('acppl_btn_install'),
-                  color: 'red',
-                  style: {
-                    fontWeight: 'bold'
-                  },
-                  onclick: function() {
-                    ajaxPluginAction(action + '_confirm', plugin_filename, btnobj, true);
-                    miniPromptDestroy(this);
-                  }
-                },
-                {
-                  text: $lang.get('etc_cancel'),
-                  color: 'blue',
-                  onclick: function() {
-                    miniPromptDestroy(this);
-                  }
-                }
-              ]
-            });
-          return true;
-        }
-        // wait for fade effect to finish its run
-        setTimeout(function()
-          {
-            miniPrompt(function(div)
-              {
-                if ( blackbox )
-                {
-                  blackbox.parentNode.removeChild(blackbox);
-                }
-                var txtholder = document.createElement('div');
-                txtholder.style.textAlign = 'center';
-                txtholder.appendChild(document.createTextNode(response.error));
-                txtholder.appendChild(document.createElement('br'));
-                txtholder.appendChild(document.createElement('br'));
-                
-                // close button
-                var btn_cancel = document.createElement('a');
-                btn_cancel.className = 'abutton abutton_red';
-                btn_cancel.href = '#';
-                btn_cancel.appendChild(document.createTextNode($lang.get('etc_ok')));
-                
-                txtholder.appendChild(btn_cancel);
-                div.appendChild(txtholder);
-                
-                btn_cancel.onclick = function()
-                {
-                  miniPromptDestroy(this);
-                  return false;
-                }
-              });
-          }, 750);
-      }
-    });
+	// if installing, uninstalling, or re-importing, confirm
+	if ( action == 'install' || action == 'uninstall' || action == 'reimport' )
+	{
+		var prompt = miniPrompt(function(div)
+			{
+				var txtholder = document.createElement('div');
+				txtholder.style.textAlign = 'center';
+				txtholder.appendChild(document.createTextNode($lang.get('acppl_msg_confirm_' + action)));
+				txtholder.appendChild(document.createElement('br'));
+				txtholder.appendChild(document.createElement('br'));
+				
+				// create buttons
+				var btn_go = document.createElement('a');
+				btn_go.className = 'abutton abutton_red';
+				btn_go.href = '#';
+				btn_go._action = action;
+				btn_go._filename = plugin_filename;
+				btn_go._button = btnobj;
+				btn_go.appendChild(document.createTextNode($lang.get('acppl_btn_' + action)));
+				btn_go.style.fontWeight = 'bold';
+				txtholder.appendChild(btn_go);
+				
+				// space
+				txtholder.appendChild(document.createTextNode(' '));
+				
+				// cancel
+				var btn_cancel = document.createElement('a');
+				btn_cancel.className = 'abutton abutton_blue';
+				btn_cancel.href = '#';
+				btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
+				
+				txtholder.appendChild(btn_cancel);
+				div.appendChild(txtholder);
+				
+				btn_go.onclick = function()
+				{
+					ajaxPluginAction(this._action + '_confirm', this._filename, this._button);
+					miniPromptDestroy(this);
+					return false;
+				};
+				btn_cancel.onclick = function()
+				{
+					miniPromptDestroy(this);
+					return false;
+				};
+			});
+		return true;
+	}
+	action = action.replace(/_confirm$/, '');
+	// white-out the plugin info box
+	if ( btnobj )
+	{
+		var td = btnobj.parentNode.parentNode.parentNode.parentNode;
+		var blackbox = whiteOutElement(td);
+	}
+	var request = {
+			mode: action,
+			plugin: plugin_filename
+		};
+	if ( send_confirm )
+	{
+		request.install_confirmed = true;
+	}
+	request = toJSONString(request);
+	ajaxPost(makeUrlNS('Admin', 'PluginManager/action.json'), 'r=' + ajaxEscape(request), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(response);
+					return false;
+				}
+				response = parseJSON(response);
+				if ( blackbox )
+				{
+					blackbox.parentNode.removeChild(blackbox);
+				}
+				if ( response.success )
+				{
+					ajaxPage( namespace_list['Admin'] + 'PluginManager' );
+					return true;
+				}
+				if ( response.need_confirm )
+				{
+					miniPromptMessage({
+							title: $lang.get(response.confirm_title),
+							message: $lang.get(response.confirm_body),
+							buttons: [
+								{
+									text: $lang.get('acppl_btn_install'),
+									color: 'red',
+									style: {
+										fontWeight: 'bold'
+									},
+									onclick: function() {
+										ajaxPluginAction(action + '_confirm', plugin_filename, btnobj, true);
+										miniPromptDestroy(this);
+									}
+								},
+								{
+									text: $lang.get('etc_cancel'),
+									color: 'blue',
+									onclick: function() {
+										miniPromptDestroy(this);
+									}
+								}
+							]
+						});
+					return true;
+				}
+				// wait for fade effect to finish its run
+				setTimeout(function()
+					{
+						miniPrompt(function(div)
+							{
+								if ( blackbox )
+								{
+									blackbox.parentNode.removeChild(blackbox);
+								}
+								var txtholder = document.createElement('div');
+								txtholder.style.textAlign = 'center';
+								txtholder.appendChild(document.createTextNode(response.error));
+								txtholder.appendChild(document.createElement('br'));
+								txtholder.appendChild(document.createElement('br'));
+								
+								// close button
+								var btn_cancel = document.createElement('a');
+								btn_cancel.className = 'abutton abutton_red';
+								btn_cancel.href = '#';
+								btn_cancel.appendChild(document.createTextNode($lang.get('etc_ok')));
+								
+								txtholder.appendChild(btn_cancel);
+								div.appendChild(txtholder);
+								
+								btn_cancel.onclick = function()
+								{
+									miniPromptDestroy(this);
+									return false;
+								}
+							});
+					}, 750);
+			}
+		});
 }
 
 window.ajaxReverseDNS = function(o, text)
 {
-  if(text) var ipaddr = text;
-  else var ipaddr = o.innerHTML;
-  rDnsObj = o;
-  rDnsBannerObj = bannerOn('Retrieving reverse DNS info...');
-  ajaxGet(stdAjaxPrefix+'&_mode=rdns&ip='+ipaddr, function(ajax) {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        off = fetch_offset(rDnsObj);
-        dim = fetch_dimensions(rDnsObj);
-        right = off['left'] + dim['w'];
-        top = off['top'] + dim['h'];
-        var thediv = document.createElement('div');
-        thediv.className = 'info-box';
-        thediv.style.margin = '0';
-        thediv.style.position = 'absolute';
-        thediv.style.top  = top  + 'px';
-        thediv.style.display = 'none';
-        thediv.style.zIndex = getHighestZ() + 2;
-        thediv.id = 'mdgDynamic_rDnsInfoDiv_'+Math.floor(Math.random() * 1000000);
-        // FIXME: l10n
-        thediv.innerHTML = '<b>Reverse DNS:</b><br />'+ajax.responseText+' <a href="#" onclick="elem = document.getElementById(\''+thediv.id+'\'); elem.innerHTML = \'\'; elem.style.display = \'none\';return false;">Close</a>';
-        var body = document.getElementsByTagName('body');
-        body = body[0];
-        bannerOff(rDnsBannerObj);
-        body.appendChild(thediv);
-        thediv.style.display = 'block';
-        left = fetch_dimensions(thediv);
-        thediv.style.display = 'none';
-        left = right - left['w'];
-        thediv.style.left = left + 'px';
-        thediv.style.display = 'block';
-        fadeInfoBoxes();
-      }
-    });
+	if(text) var ipaddr = text;
+	else var ipaddr = o.innerHTML;
+	rDnsObj = o;
+	rDnsBannerObj = bannerOn('Retrieving reverse DNS info...');
+	ajaxGet(stdAjaxPrefix+'&_mode=rdns&ip='+ipaddr, function(ajax) {
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				off = fetch_offset(rDnsObj);
+				dim = fetch_dimensions(rDnsObj);
+				right = off['left'] + dim['w'];
+				top = off['top'] + dim['h'];
+				var thediv = document.createElement('div');
+				thediv.className = 'info-box';
+				thediv.style.margin = '0';
+				thediv.style.position = 'absolute';
+				thediv.style.top  = top  + 'px';
+				thediv.style.display = 'none';
+				thediv.style.zIndex = getHighestZ() + 2;
+				thediv.id = 'mdgDynamic_rDnsInfoDiv_'+Math.floor(Math.random() * 1000000);
+				// FIXME: l10n
+				thediv.innerHTML = '<b>Reverse DNS:</b><br />'+ajax.responseText+' <a href="#" onclick="elem = document.getElementById(\''+thediv.id+'\'); elem.innerHTML = \'\'; elem.style.display = \'none\';return false;">Close</a>';
+				var body = document.getElementsByTagName('body');
+				body = body[0];
+				bannerOff(rDnsBannerObj);
+				body.appendChild(thediv);
+				thediv.style.display = 'block';
+				left = fetch_dimensions(thediv);
+				thediv.style.display = 'none';
+				left = right - left['w'];
+				thediv.style.left = left + 'px';
+				thediv.style.display = 'block';
+				fadeInfoBoxes();
+			}
+		});
 }
 
 window.ajaxGzipCheck = function()
 {
-  var resultdiv = document.getElementById('gzip_check_result');
-  if ( !resultdiv )
-    return false;
-  
-  resultdiv.innerHTML = '<img alt="Loading..." src="' + cdnPath + '/images/loading.gif" />';
-  ajaxPost(makeUrlNS('Admin', 'GeneralConfig'), 'act=gzip_check', function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        resultdiv.innerHTML = '';
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(response);
-          return false;
-        }
-        response = parseJSON(response);
-        if ( response.error )
-        {
-          resultdiv.innerHTML = '<div class="error-box-mini">' + response.error + '</div>';
-        }
-        else
-        {
-          // probably success.
-          resultdiv.innerHTML += response.server_does_it ?
-            '<div class="error-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_server_does_it') + '</div>' :
-            '<div class="info-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_server_good') + '</div>';
-          resultdiv.innerHTML += response.php_supports_gzip ?
-            '<div class="info-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_php_good') + '</div>' :
-            '<div class="error-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_php_bad') + '</div>';
-            
-          if ( response.php_supports_gzip && !response.server_does_it )
-          {
-            resultdiv.innerHTML += '<div class="success-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_success') + '</div>';
-          }
-          else
-          {
-            resultdiv.innerHTML += '<div class="error-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_failure') + '</div>';
-          }
-        }
-      }
-    });
+	var resultdiv = document.getElementById('gzip_check_result');
+	if ( !resultdiv )
+		return false;
+	
+	resultdiv.innerHTML = '<img alt="Loading..." src="' + cdnPath + '/images/loading.gif" />';
+	ajaxPost(makeUrlNS('Admin', 'GeneralConfig'), 'act=gzip_check', function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				resultdiv.innerHTML = '';
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(response);
+					return false;
+				}
+				response = parseJSON(response);
+				if ( response.error )
+				{
+					resultdiv.innerHTML = '<div class="error-box-mini">' + response.error + '</div>';
+				}
+				else
+				{
+					// probably success.
+					resultdiv.innerHTML += response.server_does_it ?
+						'<div class="error-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_server_does_it') + '</div>' :
+						'<div class="info-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_server_good') + '</div>';
+					resultdiv.innerHTML += response.php_supports_gzip ?
+						'<div class="info-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_php_good') + '</div>' :
+						'<div class="error-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_php_bad') + '</div>';
+						
+					if ( response.php_supports_gzip && !response.server_does_it )
+					{
+						resultdiv.innerHTML += '<div class="success-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_success') + '</div>';
+					}
+					else
+					{
+						resultdiv.innerHTML += '<div class="error-box-mini">' + $lang.get('acpgc_field_gzip_check_msg_failure') + '</div>';
+					}
+				}
+			}
+		});
 }
--- a/includes/clientside/static/autofill.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/autofill.js	Sun Mar 28 23:10:46 2010 -0400
@@ -11,12 +11,12 @@
  */
 
 autofill_schemas.generic = {
-  init: function(element, fillclass, params)
-  {
-    $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
-        minChars: 3
-    });
-  }
+	init: function(element, fillclass, params)
+	{
+		$(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
+				minChars: 3
+		});
+	}
 }
 
 /**
@@ -24,738 +24,738 @@
  */
 
 autofill_schemas.username = {
-  init: function(element, fillclass, params)
-  {
-    params = params || {};
-    var allow_anon = params.allow_anon ? '1' : '0';
-    $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon=' + allow_anon) + '&userinput=', {
-        minChars: 3,
-        formatItem: function(row, _, __)
-        {
-          var html = row.name_highlight + ' &ndash; ';
-          html += '<span style="' + row.rank_style + '">' + row.rank_title + '</span>';
-          return html;
-        },
-        tableHeader: '<tr><th>' + $lang.get('user_autofill_heading_suggestions') + '</th></tr>',
-        showWhenNoResults: true,
-        noResultsHTML: '<tr><td class="row1" style="font-size: smaller;">' + $lang.get('user_autofill_msg_no_suggestions') + '</td></tr>'
-    });
-  }
+	init: function(element, fillclass, params)
+	{
+		params = params || {};
+		var allow_anon = params.allow_anon ? '1' : '0';
+		$(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon=' + allow_anon) + '&userinput=', {
+				minChars: 3,
+				formatItem: function(row, _, __)
+				{
+					var html = row.name_highlight + ' &ndash; ';
+					html += '<span style="' + row.rank_style + '">' + row.rank_title + '</span>';
+					return html;
+				},
+				tableHeader: '<tr><th>' + $lang.get('user_autofill_heading_suggestions') + '</th></tr>',
+				showWhenNoResults: true,
+				noResultsHTML: '<tr><td class="row1" style="font-size: smaller;">' + $lang.get('user_autofill_msg_no_suggestions') + '</td></tr>'
+		});
+	}
 }
 
 autofill_schemas.page = {
-  init: function(element, fillclass, params)
-  {
-    $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
-        minChars: 3,
-        formatItem: function(row, _, __)
-        {
-          var html = '<u>' + row.name_highlight + '</u>';
-          html += ' &ndash; ' + row.pid_highlight;
-          return html;
-        },
-        showWhenNoResults: true,
-        noResultsHTML: '<tr><td class="row1" style="font-size: smaller;">' + $lang.get('user_autofill_msg_no_suggestions') + '</td></tr>'
-    });
-  }
+	init: function(element, fillclass, params)
+	{
+		$(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
+				minChars: 3,
+				formatItem: function(row, _, __)
+				{
+					var html = '<u>' + row.name_highlight + '</u>';
+					html += ' &ndash; ' + row.pid_highlight;
+					return html;
+				},
+				showWhenNoResults: true,
+				noResultsHTML: '<tr><td class="row1" style="font-size: smaller;">' + $lang.get('user_autofill_msg_no_suggestions') + '</td></tr>'
+		});
+	}
 }
 
 window.autofill_init_element = function(element, params)
 {
-  if ( element.af_initted )
-    return false;
-  
-  params = params || {};
-  // assign an ID if it doesn't have one yet
-  if ( !element.id )
-  {
-    element.id = 'autofill_' + Math.floor(Math.random() * 100000);
-  }
-  var id = element.id;
-  
-  // get the fill type
-  var fillclass = element.className;
-  fillclass = fillclass.split(' ');
-  fillclass = fillclass[1];
-  
-  var schema = ( autofill_schemas[fillclass] ) ? autofill_schemas[fillclass] : autofill_schemas['generic'];
-  if ( typeof(schema.init) != 'function' )
-  {
-    schema.init = autofill_schemas.generic.init;
-  }
-  schema.init(element, fillclass, params);
-  
-  element.af_initted = true;
+	if ( element.af_initted )
+		return false;
+	
+	params = params || {};
+	// assign an ID if it doesn't have one yet
+	if ( !element.id )
+	{
+		element.id = 'autofill_' + Math.floor(Math.random() * 100000);
+	}
+	var id = element.id;
+	
+	// get the fill type
+	var fillclass = element.className;
+	fillclass = fillclass.split(' ');
+	fillclass = fillclass[1];
+	
+	var schema = ( autofill_schemas[fillclass] ) ? autofill_schemas[fillclass] : autofill_schemas['generic'];
+	if ( typeof(schema.init) != 'function' )
+	{
+		schema.init = autofill_schemas.generic.init;
+	}
+	schema.init(element, fillclass, params);
+	
+	element.af_initted = true;
 }
 
 window.AutofillUsername = function(el, allow_anon)
 {
-  el.onkeyup = null;
-  el.className = 'autofill username';
-  autofill_init_element(el, { allow_anon: allow_anon });
+	el.onkeyup = null;
+	el.className = 'autofill username';
+	autofill_init_element(el, { allow_anon: allow_anon });
 }
 
 window.AutofillPage = function(el)
 {
-  el.onkeyup = null;
-  el.className = 'autofill page';
-  autofill_init_element(el, {});
+	el.onkeyup = null;
+	el.className = 'autofill page';
+	autofill_init_element(el, {});
 }
 
 // note: init, then onload (the latter is called automatically)
 
 window.autofill_onload = function()
 {
-  if ( this.loaded )
-  {
-    return true;
-  }
-  
-  var inputs = document.getElementsByClassName('input', 'autofill');
-  
-  if ( inputs.length > 0 )
-  {
-    // we have at least one input that needs to be made an autofill element.
-    // is spry data loaded?
-    load_component('l10n');
-  }
-  
-  this.loaded = true;
-  
-  for ( var i = 0; i < inputs.length; i++ )
-  {
-    autofill_init_element(inputs[i]);
-  }
+	if ( this.loaded )
+	{
+		return true;
+	}
+	
+	var inputs = document.getElementsByClassName('input', 'autofill');
+	
+	if ( inputs.length > 0 )
+	{
+		// we have at least one input that needs to be made an autofill element.
+		// is spry data loaded?
+		load_component('l10n');
+	}
+	
+	this.loaded = true;
+	
+	for ( var i = 0; i < inputs.length; i++ )
+	{
+		autofill_init_element(inputs[i]);
+	}
 }
 
 window.autofill_init = function()
 {
-  load_component(['l10n', 'jquery', 'jquery-ui']);
-  
-  if ( !window.jQuery )
-  {
-    throw('jQuery didn\'t load properly. Aborting auto-complete init.');
-  }
-  
-  jQuery.autocomplete = function(input, options) {
-    // Create a link to self
-    var me = this;
-  
-    // Create jQuery object for input element
-    var $input = $(input).attr("autocomplete", "off");
-  
-    // Apply inputClass if necessary
-    if (options.inputClass) {
-      $input.addClass(options.inputClass);
-    }
-  
-    // Create results
-    var results = document.createElement("div");
-    $(results).addClass('tblholder').css('z-index', getHighestZ() + 1).css('margin-top', 0);
-    $(results).css('clip', 'rect(0px,auto,auto,0px)').css('overflow', 'auto').css('max-height', '300px');
-  
-    // Create jQuery object for results
-    // var $results = $(results);
-    var $results = $(results).hide().addClass(options.resultsClass).css("position", "absolute");
-    if( options.width > 0 ) {
-      $results.css("width", options.width);
-    }
-  
-    // Add to body element
-    $("body").append(results);
-  
-    input.autocompleter = me;
-  
-    var timeout = null;
-    var prev = "";
-    var active = -1;
-    var cache = {};
-    var keyb = false;
-    // hasFocus was false by default, see if making it true helps
-    var hasFocus = true;
-    var hasNoResults = false;
-    var lastKeyPressCode = null;
-    var mouseDownOnSelect = false;
-    var hidingResults = false;
-  
-    // flush cache
-    function flushCache(){
-      cache = {};
-      cache.data = {};
-      cache.length = 0;
-    };
-  
-    // flush cache
-    flushCache();
-  
-    // if there is a data array supplied
-    if( options.data != null ){
-      var sFirstChar = "", stMatchSets = {}, row = [];
-  
-      // no url was specified, we need to adjust the cache length to make sure it fits the local data store
-      if( typeof options.url != "string" ) {
-        options.cacheLength = 1;
-      }
-  
-      // loop through the array and create a lookup structure
-      for( var i=0; i < options.data.length; i++ ){
-        // if row is a string, make an array otherwise just reference the array
-        row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);
-  
-        // if the length is zero, don't add to list
-        if( row[0].length > 0 ){
-          // get the first character
-          sFirstChar = row[0].substring(0, 1).toLowerCase();
-          // if no lookup array for this character exists, look it up now
-          if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
-          // if the match is a string
-          stMatchSets[sFirstChar].push(row);
-        }
-      }
-  
-      // add the data items to the cache
-      if ( options.cacheLength )
-      {
-        for( var k in stMatchSets ) {
-          // increase the cache size
-          options.cacheLength++;
-          // add to the cache
-          addToCache(k, stMatchSets[k]);
-        }
-      }
-    }
-  
-    $input
-    .keydown(function(e) {
-      // track last key pressed
-      lastKeyPressCode = e.keyCode;
-      switch(e.keyCode) {
-        case 38: // up
-          e.preventDefault();
-          moveSelect(-1);
-          break;
-        case 40: // down
-          e.preventDefault();
-          moveSelect(1);
-          break;
-        case 9:  // tab
-        case 13: // return
-          if( selectCurrent() ){
-            // make sure to blur off the current field
-            // (Enano edit - why do we want this, again?)
-            // $input.get(0).blur();
-            e.preventDefault();
-          }
-          break;
-        default:
-          active = -1;
-          if (timeout) clearTimeout(timeout);
-          timeout = setTimeout(function(){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 = true;
-    })
-    .blur(function() {
-      // track whether the field has focus
-      hasFocus = false;
-      if (!mouseDownOnSelect) {
-        hideResults();
-      }
-    });
-  
-    hideResultsNow();
-  
-    function onChange() {
-      // ignore if the following keys are pressed: [del] [shift] [capslock]
-      if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
-      var v = $input.val();
-      if (v == prev) return;
-      prev = v;
-      if (v.length >= options.minChars) {
-        $input.addClass(options.loadingClass);
-        requestData(v);
-      } else {
-        $input.removeClass(options.loadingClass);
-        $results.hide();
-      }
-    };
-  
-    function moveSelect(step) {
-  
-      var lis = $("td", results);
-      if (!lis || hasNoResults) return;
-  
-      active += step;
-  
-      if (active < 0) {
-        active = 0;
-      } else if (active >= lis.size()) {
-        active = lis.size() - 1;
-      }
-  
-      lis.removeClass("row2");
-  
-      $(lis[active]).addClass("row2");
-      
-      // scroll the results div
-      // are we going up or down?
-      var td_top = $dynano(lis[active]).Top() - $dynano(results).Top();
-      var td_height = $dynano(lis[active]).Height();
-      var td_bottom = td_top + td_height;
-      var visibleTopBoundary = getScrollOffset(results);
-      var results_height = $dynano(results).Height();
-      var visibleBottomBoundary = visibleTopBoundary + results_height;
-      var scrollTo = false;
-      if ( td_top < visibleTopBoundary && step < 0 )
-      {
-        // going up: scroll the results div to just higher than the result we're trying to see
-        scrollTo = td_top - 7;
-      }
-      else if ( td_bottom > visibleBottomBoundary && step > 0 )
-      {
-        // going down is a little harder, we want the result to be at the bottom
-        scrollTo = td_top - results_height + td_height + 7;
-      }
-      if ( scrollTo )
-      {
-        results.scrollTop = scrollTo;
-      }
-  
-      // Weird behaviour in IE
-      // if (lis[active] && lis[active].scrollIntoView) {
-      // 	lis[active].scrollIntoView(false);
-      // }
-  
-    };
-  
-    function selectCurrent() {
-      var li = $("td.row2", results)[0];
-      if (!li) {
-        var $li = $("td", results);
-        if (options.selectOnly) {
-          if ($li.length == 1) li = $li[0];
-        } else if (options.selectFirst) {
-          li = $li[0];
-        }
-      }
-      if (li) {
-        selectItem(li);
-        return true;
-      } else {
-        return false;
-      }
-    };
-  
-    function selectItem(li) {
-      if (!li) {
-        li = document.createElement("li");
-        li.extra = [];
-        li.selectValue = "";
-      }
-      var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
-      input.lastSelected = v;
-      prev = v;
-      $results.html("");
-      $input.val(v);
-      hideResultsNow();
-      if (options.onItemSelect) {
-        setTimeout(function() { options.onItemSelect(li) }, 1);
-      }
-    };
-  
-    // selects a portion of the input string
-    function createSelection(start, end){
-      // get a reference to the input element
-      var field = $input.get(0);
-      if( field.createTextRange ){
-        var selRange = field.createTextRange();
-        selRange.collapse(true);
-        selRange.moveStart("character", start);
-        selRange.moveEnd("character", end);
-        selRange.select();
-      } else if( field.setSelectionRange ){
-        field.setSelectionRange(start, end);
-      } else {
-        if( field.selectionStart ){
-          field.selectionStart = start;
-          field.selectionEnd = end;
-        }
-      }
-      field.focus();
-    };
-  
-    // fills in the input box w/the first match (assumed to be the best match)
-    function autoFill(sValue){
-      // if the last user key pressed was backspace, don't autofill
-      if( lastKeyPressCode != 8 ){
-        // fill in the value (keep the case the user has typed)
-        $input.val($input.val() + sValue.substring(prev.length));
-        // select the portion of the value not typed by the user (so the next character will erase)
-        createSelection(prev.length, sValue.length);
-      }
-    };
-  
-    function showResults() {
-      // get the position of the input field right now (in case the DOM is shifted)
-      var pos = findPos(input);
-      // either use the specified width, or autocalculate based on form element
-      var iWidth = (options.width > 0) ? options.width : $input.width();
-      // reposition
-      $results.css({
-        width: parseInt(iWidth) + "px",
-        top: (pos.y + input.offsetHeight) + "px",
-        left: pos.x + "px"
-      });
-      if ( !$results.is(":visible") )
-      {
-        $results.show("blind", {}, 200);
-      }
-      else
-      {
-        $results.show();
-      }
-    };
-  
-    function hideResults() {
-      if (timeout) clearTimeout(timeout);
-      timeout = setTimeout(hideResultsNow, 200);
-    };
-  
-    function hideResultsNow() {
-      if (hidingResults) {
-        return;
-      }
-      hidingResults = true;
-    
-      if (timeout) {
-        clearTimeout(timeout);
-      }
-      
-      var v = $input.removeClass(options.loadingClass).val();
-      
-      if ($results.is(":visible")) {
-        $results.hide();
-      }
-      
-      if (options.mustMatch) {
-        if (!input.lastSelected || input.lastSelected != v) {
-          selectItem(null);
-        }
-      }
-  
-      hidingResults = false;
-    };
-  
-    function receiveData(q, data) {
-      if (data) {
-        $input.removeClass(options.loadingClass);
-        results.innerHTML = "";
-  
-        // if the field no longer has focus or if there are no matches, do not display the drop down
-        if( !hasFocus )
-        {
-          return hideResultsNow();
-        }
-        if ( data.length == 0 && !options.showWhenNoResults )
-        {
-          return hideResultsNow();
-        }
-        hasNoResults = false;
-  
-        if ($.browser.msie) {
-          // we put a styled iframe behind the calendar so HTML SELECT elements don't show through
-          $results.append(document.createElement('iframe'));
-        }
-        results.appendChild(dataToDom(data));
-        // autofill in the complete box w/the first match as long as the user hasn't entered in more data
-        if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
-        showResults();
-      } else {
-        hideResultsNow();
-      }
-    };
-  
-    function parseData(data) {
-      if (!data) return null;
-      var parsed = parseJSON(data);
-      return parsed;
-    };
-  
-    function dataToDom(data) {
-      var ul = document.createElement("table");
-      $(ul).attr("border", "0").attr("cellspacing", "1").attr("cellpadding", "3");
-      var num = data.length;
-      
-      if ( options.tableHeader )
-      {
-        // fails in IE6
-        try
-        {
-          ul.innerHTML = options.tableHeader;
-        }
-        catch ( e ) {};
-      }
-      
-      if ( num == 0 )
-      {
-        // not showing any results
-        if ( options.noResultsHTML )
-          ul.innerHTML += options.noResultsHTML;
-        
-        hasNoResults = true;
-        return ul;
-      }
-      
-      // limited results to a max number
-      if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;
-      
-      for (var i=0; i < num; i++) {
-        var row = data[i];
-        if (!row) continue;
-        
-        if ( typeof(row[0]) != 'string' )
-        {
-          // last ditch resort if it's a 1.1.4 autocomplete plugin that doesn't provide an automatic result.
-          // hopefully this doesn't slow it down a lot.
-          for ( var i in row )
-          {
-            if ( i == "0" || i == 0 )
-              break;
-            row[0] = row[i];
-            break;
-          }
-        }
-        
-        var li = document.createElement("tr");
-        var td = document.createElement("td");
-        td.selectValue = row[0];
-        $(td).addClass('row1');
-        $(td).css("font-size", "smaller");
-        
-        if ( options.formatItem )
-        {
-          td.innerHTML = options.formatItem(row, i, num);
-        }
-        else
-        {
-          td.innerHTML = row[0];
-        }
-        li.appendChild(td);
-        ul.appendChild(li);
-        
-        $(td).hover(
-          function() { $("tr", ul).removeClass("row2"); $(this).addClass("row2"); active = $("tr", ul).indexOf($(this).get(0)); },
-          function() { $(this).removeClass("row2"); }
-        ).click(function(e) { 
-          e.preventDefault();
-          e.stopPropagation();
-          selectItem(this)
-        });
-      }
-      
-      $(ul).mousedown(function() {
-        mouseDownOnSelect = true;
-      }).mouseup(function() {
-        mouseDownOnSelect = false;
-      });
-      return ul;
-    };
-  
-    function requestData(q) {
-      if (!options.matchCase) q = q.toLowerCase();
-      var data = options.cacheLength ? loadFromCache(q) : null;
-      // recieve the cached data
-      if (data) {
-        receiveData(q, data);
-      // if an AJAX url has been supplied, try loading the data now
-      } else if( (typeof options.url == "string") && (options.url.length > 0) ){
-        $.get(makeUrl(q), function(data) {
-          data = parseData(data);
-          addToCache(q, data);
-          receiveData(q, data);
-        });
-      // if there's been no data found, remove the loading class
-      } else {
-        $input.removeClass(options.loadingClass);
-      }
-    };
-  
-    function makeUrl(q) {
-      var sep = options.url.indexOf('?') == -1 ? '?' : '&'; 
-      var url = options.url + encodeURI(q);
-      for (var i in options.extraParams) {
-        url += "&" + i + "=" + encodeURI(options.extraParams[i]);
-      }
-      return url;
-    };
-  
-    function loadFromCache(q) {
-      if (!q) return null;
-      if (cache.data[q]) return cache.data[q];
-      if (options.matchSubset) {
-        for (var i = q.length - 1; i >= options.minChars; i--) {
-          var qs = q.substr(0, i);
-          var c = cache.data[qs];
-          if (c) {
-            var csub = [];
-            for (var j = 0; j < c.length; j++) {
-              var x = c[j];
-              var x0 = x[0];
-              if (matchSubset(x0, q)) {
-                csub[csub.length] = x;
-              }
-            }
-            return csub;
-          }
-        }
-      }
-      return null;
-    };
-  
-    function matchSubset(s, sub) {
-      if (!options.matchCase) s = s.toLowerCase();
-      var i = s.indexOf(sub);
-      if (i == -1) return false;
-      return i == 0 || options.matchContains;
-    };
-  
-    this.flushCache = function() {
-      flushCache();
-    };
-  
-    this.setExtraParams = function(p) {
-      options.extraParams = p;
-    };
-  
-    this.findValue = function(){
-      var q = $input.val();
-  
-      if (!options.matchCase) q = q.toLowerCase();
-      var data = options.cacheLength ? loadFromCache(q) : null;
-      if (data) {
-        findValueCallback(q, data);
-      } else if( (typeof options.url == "string") && (options.url.length > 0) ){
-        $.get(makeUrl(q), function(data) {
-          data = parseData(data)
-          addToCache(q, data);
-          findValueCallback(q, data);
-        });
-      } else {
-        // no matches
-        findValueCallback(q, null);
-      }
-    }
-  
-    function findValueCallback(q, data){
-      if (data) $input.removeClass(options.loadingClass);
-  
-      var num = (data) ? data.length : 0;
-      var li = null;
-  
-      for (var i=0; i < num; i++) {
-        var row = data[i];
-  
-        if( row[0].toLowerCase() == q.toLowerCase() ){
-          li = document.createElement("li");
-          if (options.formatItem) {
-            li.innerHTML = options.formatItem(row, i, num);
-            li.selectValue = row[0];
-          } else {
-            li.innerHTML = row[0];
-            li.selectValue = row[0];
-          }
-          var extra = null;
-          if( row.length > 1 ){
-            extra = [];
-            for (var j=1; j < row.length; j++) {
-              extra[extra.length] = row[j];
-            }
-          }
-          li.extra = extra;
-        }
-      }
-  
-      if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
-    }
-  
-    function addToCache(q, data) {
-      if (!data || !q || !options.cacheLength) return;
-      if (!cache.length || cache.length > options.cacheLength) {
-        flushCache();
-        cache.length++;
-      } else if (!cache[q]) {
-        cache.length++;
-      }
-      cache.data[q] = data;
-    };
-  
-    function findPos(obj) {
-      var curleft = obj.offsetLeft || 0;
-      var curtop = obj.offsetTop || 0;
-      while (obj = obj.offsetParent) {
-        curleft += obj.offsetLeft
-        curtop += obj.offsetTop
-      }
-      return {x:curleft,y:curtop};
-    }
-  }
-  
-  jQuery.fn.autocomplete = function(url, options, data) {
-    // Make sure options exists
-    options = options || {};
-    // Set url as option
-    options.url = url;
-    // set some bulk local data
-    options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;
-  
-    // Set default values for required options
-    options = $.extend({
-      inputClass: "ac_input",
-      resultsClass: "ac_results",
-      lineSeparator: "\n",
-      cellSeparator: "|",
-      minChars: 1,
-      delay: 400,
-      matchCase: 0,
-      matchSubset: 1,
-      matchContains: 0,
-      cacheLength: false,
-      mustMatch: 0,
-      extraParams: {},
-      loadingClass: "ac_loading",
-      selectFirst: false,
-      selectOnly: false,
-      maxItemsToShow: -1,
-      autoFill: false,
-      showWhenNoResults: false,
-      width: 0
-    }, options);
-    options.width = parseInt(options.width, 10);
-  
-    this.each(function() {
-      var input = this;
-      new jQuery.autocomplete(input, options);
-    });
-  
-    // Don't break the chain
-    return this;
-  }
-  
-  jQuery.fn.autocompleteArray = function(data, options) {
-    return this.autocomplete(null, options, data);
-  }
-  
-  jQuery.fn.indexOf = function(e){
-    for( var i=0; i<this.length; i++ ){
-      if( this[i] == e ) return i;
-    }
-    return -1;
-  };
-  
-  autofill_onload();
+	load_component(['l10n', 'jquery', 'jquery-ui']);
+	
+	if ( !window.jQuery )
+	{
+		throw('jQuery didn\'t load properly. Aborting auto-complete init.');
+	}
+	
+	jQuery.autocomplete = function(input, options) {
+		// Create a link to self
+		var me = this;
+	
+		// Create jQuery object for input element
+		var $input = $(input).attr("autocomplete", "off");
+	
+		// Apply inputClass if necessary
+		if (options.inputClass) {
+			$input.addClass(options.inputClass);
+		}
+	
+		// Create results
+		var results = document.createElement("div");
+		$(results).addClass('tblholder').css('z-index', getHighestZ() + 1).css('margin-top', 0);
+		$(results).css('clip', 'rect(0px,auto,auto,0px)').css('overflow', 'auto').css('max-height', '300px');
+	
+		// Create jQuery object for results
+		// var $results = $(results);
+		var $results = $(results).hide().addClass(options.resultsClass).css("position", "absolute");
+		if( options.width > 0 ) {
+			$results.css("width", options.width);
+		}
+	
+		// Add to body element
+		$("body").append(results);
+	
+		input.autocompleter = me;
+	
+		var timeout = null;
+		var prev = "";
+		var active = -1;
+		var cache = {};
+		var keyb = false;
+		// hasFocus was false by default, see if making it true helps
+		var hasFocus = true;
+		var hasNoResults = false;
+		var lastKeyPressCode = null;
+		var mouseDownOnSelect = false;
+		var hidingResults = false;
+	
+		// flush cache
+		function flushCache(){
+			cache = {};
+			cache.data = {};
+			cache.length = 0;
+		};
+	
+		// flush cache
+		flushCache();
+	
+		// if there is a data array supplied
+		if( options.data != null ){
+			var sFirstChar = "", stMatchSets = {}, row = [];
+	
+			// no url was specified, we need to adjust the cache length to make sure it fits the local data store
+			if( typeof options.url != "string" ) {
+				options.cacheLength = 1;
+			}
+	
+			// loop through the array and create a lookup structure
+			for( var i=0; i < options.data.length; i++ ){
+				// if row is a string, make an array otherwise just reference the array
+				row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);
+	
+				// if the length is zero, don't add to list
+				if( row[0].length > 0 ){
+					// get the first character
+					sFirstChar = row[0].substring(0, 1).toLowerCase();
+					// if no lookup array for this character exists, look it up now
+					if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
+					// if the match is a string
+					stMatchSets[sFirstChar].push(row);
+				}
+			}
+	
+			// add the data items to the cache
+			if ( options.cacheLength )
+			{
+				for( var k in stMatchSets ) {
+					// increase the cache size
+					options.cacheLength++;
+					// add to the cache
+					addToCache(k, stMatchSets[k]);
+				}
+			}
+		}
+	
+		$input
+		.keydown(function(e) {
+			// track last key pressed
+			lastKeyPressCode = e.keyCode;
+			switch(e.keyCode) {
+				case 38: // up
+					e.preventDefault();
+					moveSelect(-1);
+					break;
+				case 40: // down
+					e.preventDefault();
+					moveSelect(1);
+					break;
+				case 9:  // tab
+				case 13: // return
+					if( selectCurrent() ){
+						// make sure to blur off the current field
+						// (Enano edit - why do we want this, again?)
+						// $input.get(0).blur();
+						e.preventDefault();
+					}
+					break;
+				default:
+					active = -1;
+					if (timeout) clearTimeout(timeout);
+					timeout = setTimeout(function(){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 = true;
+		})
+		.blur(function() {
+			// track whether the field has focus
+			hasFocus = false;
+			if (!mouseDownOnSelect) {
+				hideResults();
+			}
+		});
+	
+		hideResultsNow();
+	
+		function onChange() {
+			// ignore if the following keys are pressed: [del] [shift] [capslock]
+			if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
+			var v = $input.val();
+			if (v == prev) return;
+			prev = v;
+			if (v.length >= options.minChars) {
+				$input.addClass(options.loadingClass);
+				requestData(v);
+			} else {
+				$input.removeClass(options.loadingClass);
+				$results.hide();
+			}
+		};
+	
+		function moveSelect(step) {
+	
+			var lis = $("td", results);
+			if (!lis || hasNoResults) return;
+	
+			active += step;
+	
+			if (active < 0) {
+				active = 0;
+			} else if (active >= lis.size()) {
+				active = lis.size() - 1;
+			}
+	
+			lis.removeClass("row2");
+	
+			$(lis[active]).addClass("row2");
+			
+			// scroll the results div
+			// are we going up or down?
+			var td_top = $dynano(lis[active]).Top() - $dynano(results).Top();
+			var td_height = $dynano(lis[active]).Height();
+			var td_bottom = td_top + td_height;
+			var visibleTopBoundary = getScrollOffset(results);
+			var results_height = $dynano(results).Height();
+			var visibleBottomBoundary = visibleTopBoundary + results_height;
+			var scrollTo = false;
+			if ( td_top < visibleTopBoundary && step < 0 )
+			{
+				// going up: scroll the results div to just higher than the result we're trying to see
+				scrollTo = td_top - 7;
+			}
+			else if ( td_bottom > visibleBottomBoundary && step > 0 )
+			{
+				// going down is a little harder, we want the result to be at the bottom
+				scrollTo = td_top - results_height + td_height + 7;
+			}
+			if ( scrollTo )
+			{
+				results.scrollTop = scrollTo;
+			}
+	
+			// Weird behaviour in IE
+			// if (lis[active] && lis[active].scrollIntoView) {
+			// 	lis[active].scrollIntoView(false);
+			// }
+	
+		};
+	
+		function selectCurrent() {
+			var li = $("td.row2", results)[0];
+			if (!li) {
+				var $li = $("td", results);
+				if (options.selectOnly) {
+					if ($li.length == 1) li = $li[0];
+				} else if (options.selectFirst) {
+					li = $li[0];
+				}
+			}
+			if (li) {
+				selectItem(li);
+				return true;
+			} else {
+				return false;
+			}
+		};
+	
+		function selectItem(li) {
+			if (!li) {
+				li = document.createElement("li");
+				li.extra = [];
+				li.selectValue = "";
+			}
+			var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
+			input.lastSelected = v;
+			prev = v;
+			$results.html("");
+			$input.val(v);
+			hideResultsNow();
+			if (options.onItemSelect) {
+				setTimeout(function() { options.onItemSelect(li) }, 1);
+			}
+		};
+	
+		// selects a portion of the input string
+		function createSelection(start, end){
+			// get a reference to the input element
+			var field = $input.get(0);
+			if( field.createTextRange ){
+				var selRange = field.createTextRange();
+				selRange.collapse(true);
+				selRange.moveStart("character", start);
+				selRange.moveEnd("character", end);
+				selRange.select();
+			} else if( field.setSelectionRange ){
+				field.setSelectionRange(start, end);
+			} else {
+				if( field.selectionStart ){
+					field.selectionStart = start;
+					field.selectionEnd = end;
+				}
+			}
+			field.focus();
+		};
+	
+		// fills in the input box w/the first match (assumed to be the best match)
+		function autoFill(sValue){
+			// if the last user key pressed was backspace, don't autofill
+			if( lastKeyPressCode != 8 ){
+				// fill in the value (keep the case the user has typed)
+				$input.val($input.val() + sValue.substring(prev.length));
+				// select the portion of the value not typed by the user (so the next character will erase)
+				createSelection(prev.length, sValue.length);
+			}
+		};
+	
+		function showResults() {
+			// get the position of the input field right now (in case the DOM is shifted)
+			var pos = findPos(input);
+			// either use the specified width, or autocalculate based on form element
+			var iWidth = (options.width > 0) ? options.width : $input.width();
+			// reposition
+			$results.css({
+				width: parseInt(iWidth) + "px",
+				top: (pos.y + input.offsetHeight) + "px",
+				left: pos.x + "px"
+			});
+			if ( !$results.is(":visible") )
+			{
+				$results.show("blind", {}, 200);
+			}
+			else
+			{
+				$results.show();
+			}
+		};
+	
+		function hideResults() {
+			if (timeout) clearTimeout(timeout);
+			timeout = setTimeout(hideResultsNow, 200);
+		};
+	
+		function hideResultsNow() {
+			if (hidingResults) {
+				return;
+			}
+			hidingResults = true;
+		
+			if (timeout) {
+				clearTimeout(timeout);
+			}
+			
+			var v = $input.removeClass(options.loadingClass).val();
+			
+			if ($results.is(":visible")) {
+				$results.hide();
+			}
+			
+			if (options.mustMatch) {
+				if (!input.lastSelected || input.lastSelected != v) {
+					selectItem(null);
+				}
+			}
+	
+			hidingResults = false;
+		};
+	
+		function receiveData(q, data) {
+			if (data) {
+				$input.removeClass(options.loadingClass);
+				results.innerHTML = "";
+	
+				// if the field no longer has focus or if there are no matches, do not display the drop down
+				if( !hasFocus )
+				{
+					return hideResultsNow();
+				}
+				if ( data.length == 0 && !options.showWhenNoResults )
+				{
+					return hideResultsNow();
+				}
+				hasNoResults = false;
+	
+				if ($.browser.msie) {
+					// we put a styled iframe behind the calendar so HTML SELECT elements don't show through
+					$results.append(document.createElement('iframe'));
+				}
+				results.appendChild(dataToDom(data));
+				// autofill in the complete box w/the first match as long as the user hasn't entered in more data
+				if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
+				showResults();
+			} else {
+				hideResultsNow();
+			}
+		};
+	
+		function parseData(data) {
+			if (!data) return null;
+			var parsed = parseJSON(data);
+			return parsed;
+		};
+	
+		function dataToDom(data) {
+			var ul = document.createElement("table");
+			$(ul).attr("border", "0").attr("cellspacing", "1").attr("cellpadding", "3");
+			var num = data.length;
+			
+			if ( options.tableHeader )
+			{
+				// fails in IE6
+				try
+				{
+					ul.innerHTML = options.tableHeader;
+				}
+				catch ( e ) {};
+			}
+			
+			if ( num == 0 )
+			{
+				// not showing any results
+				if ( options.noResultsHTML )
+					ul.innerHTML += options.noResultsHTML;
+				
+				hasNoResults = true;
+				return ul;
+			}
+			
+			// limited results to a max number
+			if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;
+			
+			for (var i=0; i < num; i++) {
+				var row = data[i];
+				if (!row) continue;
+				
+				if ( typeof(row[0]) != 'string' )
+				{
+					// last ditch resort if it's a 1.1.4 autocomplete plugin that doesn't provide an automatic result.
+					// hopefully this doesn't slow it down a lot.
+					for ( var i in row )
+					{
+						if ( i == "0" || i == 0 )
+							break;
+						row[0] = row[i];
+						break;
+					}
+				}
+				
+				var li = document.createElement("tr");
+				var td = document.createElement("td");
+				td.selectValue = row[0];
+				$(td).addClass('row1');
+				$(td).css("font-size", "smaller");
+				
+				if ( options.formatItem )
+				{
+					td.innerHTML = options.formatItem(row, i, num);
+				}
+				else
+				{
+					td.innerHTML = row[0];
+				}
+				li.appendChild(td);
+				ul.appendChild(li);
+				
+				$(td).hover(
+					function() { $("tr", ul).removeClass("row2"); $(this).addClass("row2"); active = $("tr", ul).indexOf($(this).get(0)); },
+					function() { $(this).removeClass("row2"); }
+				).click(function(e) { 
+					e.preventDefault();
+					e.stopPropagation();
+					selectItem(this)
+				});
+			}
+			
+			$(ul).mousedown(function() {
+				mouseDownOnSelect = true;
+			}).mouseup(function() {
+				mouseDownOnSelect = false;
+			});
+			return ul;
+		};
+	
+		function requestData(q) {
+			if (!options.matchCase) q = q.toLowerCase();
+			var data = options.cacheLength ? loadFromCache(q) : null;
+			// recieve the cached data
+			if (data) {
+				receiveData(q, data);
+			// if an AJAX url has been supplied, try loading the data now
+			} else if( (typeof options.url == "string") && (options.url.length > 0) ){
+				$.get(makeUrl(q), function(data) {
+					data = parseData(data);
+					addToCache(q, data);
+					receiveData(q, data);
+				});
+			// if there's been no data found, remove the loading class
+			} else {
+				$input.removeClass(options.loadingClass);
+			}
+		};
+	
+		function makeUrl(q) {
+			var sep = options.url.indexOf('?') == -1 ? '?' : '&'; 
+			var url = options.url + encodeURI(q);
+			for (var i in options.extraParams) {
+				url += "&" + i + "=" + encodeURI(options.extraParams[i]);
+			}
+			return url;
+		};
+	
+		function loadFromCache(q) {
+			if (!q) return null;
+			if (cache.data[q]) return cache.data[q];
+			if (options.matchSubset) {
+				for (var i = q.length - 1; i >= options.minChars; i--) {
+					var qs = q.substr(0, i);
+					var c = cache.data[qs];
+					if (c) {
+						var csub = [];
+						for (var j = 0; j < c.length; j++) {
+							var x = c[j];
+							var x0 = x[0];
+							if (matchSubset(x0, q)) {
+								csub[csub.length] = x;
+							}
+						}
+						return csub;
+					}
+				}
+			}
+			return null;
+		};
+	
+		function matchSubset(s, sub) {
+			if (!options.matchCase) s = s.toLowerCase();
+			var i = s.indexOf(sub);
+			if (i == -1) return false;
+			return i == 0 || options.matchContains;
+		};
+	
+		this.flushCache = function() {
+			flushCache();
+		};
+	
+		this.setExtraParams = function(p) {
+			options.extraParams = p;
+		};
+	
+		this.findValue = function(){
+			var q = $input.val();
+	
+			if (!options.matchCase) q = q.toLowerCase();
+			var data = options.cacheLength ? loadFromCache(q) : null;
+			if (data) {
+				findValueCallback(q, data);
+			} else if( (typeof options.url == "string") && (options.url.length > 0) ){
+				$.get(makeUrl(q), function(data) {
+					data = parseData(data)
+					addToCache(q, data);
+					findValueCallback(q, data);
+				});
+			} else {
+				// no matches
+				findValueCallback(q, null);
+			}
+		}
+	
+		function findValueCallback(q, data){
+			if (data) $input.removeClass(options.loadingClass);
+	
+			var num = (data) ? data.length : 0;
+			var li = null;
+	
+			for (var i=0; i < num; i++) {
+				var row = data[i];
+	
+				if( row[0].toLowerCase() == q.toLowerCase() ){
+					li = document.createElement("li");
+					if (options.formatItem) {
+						li.innerHTML = options.formatItem(row, i, num);
+						li.selectValue = row[0];
+					} else {
+						li.innerHTML = row[0];
+						li.selectValue = row[0];
+					}
+					var extra = null;
+					if( row.length > 1 ){
+						extra = [];
+						for (var j=1; j < row.length; j++) {
+							extra[extra.length] = row[j];
+						}
+					}
+					li.extra = extra;
+				}
+			}
+	
+			if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
+		}
+	
+		function addToCache(q, data) {
+			if (!data || !q || !options.cacheLength) return;
+			if (!cache.length || cache.length > options.cacheLength) {
+				flushCache();
+				cache.length++;
+			} else if (!cache[q]) {
+				cache.length++;
+			}
+			cache.data[q] = data;
+		};
+	
+		function findPos(obj) {
+			var curleft = obj.offsetLeft || 0;
+			var curtop = obj.offsetTop || 0;
+			while (obj = obj.offsetParent) {
+				curleft += obj.offsetLeft
+				curtop += obj.offsetTop
+			}
+			return {x:curleft,y:curtop};
+		}
+	}
+	
+	jQuery.fn.autocomplete = function(url, options, data) {
+		// Make sure options exists
+		options = options || {};
+		// Set url as option
+		options.url = url;
+		// set some bulk local data
+		options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;
+	
+		// Set default values for required options
+		options = $.extend({
+			inputClass: "ac_input",
+			resultsClass: "ac_results",
+			lineSeparator: "\n",
+			cellSeparator: "|",
+			minChars: 1,
+			delay: 400,
+			matchCase: 0,
+			matchSubset: 1,
+			matchContains: 0,
+			cacheLength: false,
+			mustMatch: 0,
+			extraParams: {},
+			loadingClass: "ac_loading",
+			selectFirst: false,
+			selectOnly: false,
+			maxItemsToShow: -1,
+			autoFill: false,
+			showWhenNoResults: false,
+			width: 0
+		}, options);
+		options.width = parseInt(options.width, 10);
+	
+		this.each(function() {
+			var input = this;
+			new jQuery.autocomplete(input, options);
+		});
+	
+		// Don't break the chain
+		return this;
+	}
+	
+	jQuery.fn.autocompleteArray = function(data, options) {
+		return this.autocomplete(null, options, data);
+	}
+	
+	jQuery.fn.indexOf = function(e){
+		for( var i=0; i<this.length; i++ ){
+			if( this[i] == e ) return i;
+		}
+		return -1;
+	};
+	
+	autofill_onload();
 };
 
 addOnloadHook(autofill_init);
--- a/includes/clientside/static/comments.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/comments.js	Sun Mar 28 23:10:46 2010 -0400
@@ -5,687 +5,687 @@
 
 window.ajaxComments = function(parms)
 {
-  load_component(['l10n', 'paginate', 'template-compiler', 'toolbar', 'flyin', 'jquery', 'jquery-ui']);
-  setAjaxLoading();
-  var pid = strToPageID(title);
-  if(!parms)
-  {
-    var parms = {
-      mode: 'fetch',
-      pagenum: 0
-    };
-  }
-  parms.page_id = pid[0];
-  parms.namespace = pid[1];
-  if(comment_template)
-    parms.have_template = true;
-  parms = ajaxEscape(toJSONString(parms));
-  ajaxPost(stdAjaxPrefix+'&_mode=comments', 'data=' + parms, function(ajax) {
-    if ( ajax.readyState == 4 && ajax.status == 200 ) {
-      unsetAjaxLoading();
-      selectButtonMajor('discussion');
-      unselectAllButtonsMinor();
-      // IE compatibility - doing ajax.responseText.substr() doesn't work
-      var rsptxt = ajax.responseText + '';
-      if ( rsptxt.substr(0, 1) != '{' )
-      {
-        document.getElementById('ajaxEditContainer').innerHTML = '<p>Comment system Javascript runtime: invalid JSON response from server, response text:</p><pre>' + ajax.responseText + '</pre>';
-        return false;
-      }
-      var response = parseJSON(ajax.responseText);
-      switch(response.mode)
-      {
-        case 'fetch':
-          if(response.template)
-            comment_template = response.template;
-          setAjaxLoading();
-          renderComments(response);
-          unsetAjaxLoading();
-          break;
-        case 'refetch':
-          var html = '';
-          for ( var i = 0; i < response.comments.length; i++ )
-          {
-            html += window._render_comment(response.comments[i], response);
-          }
-          $('#' + response.passback.paginator_id + '_0')
-            .css('height', 'auto')
-            .css('background-color', 'transparent')
-            .css('background-image', 'none')
-            .fadeTo('fast', 100)
-            .html(html);
-          break;
-        case 'redraw':
-          redrawComment(response);
-          break;
-        case 'annihilate':
-          annihiliateComment(response.id);
-          break;
-        case 'materialize':
-          alert($lang.get('comment_msg_comment_posted'));
-          hideCommentForm();
-          materializeComment(response);
-          break;
-        case 'error':
-          load_component(['messagebox', 'fadefilter', 'flyin']);
-          new MessageBox(MB_OK|MB_ICONSTOP, ( response.title ? response.title : $lang.get('comment_ajax_err_generic_title') ), response.error);
-          break;
-        default:
-          alert(ajax.responseText);
-          break;
-      }
-    }
-  });
+	load_component(['l10n', 'paginate', 'template-compiler', 'toolbar', 'flyin', 'jquery', 'jquery-ui']);
+	setAjaxLoading();
+	var pid = strToPageID(title);
+	if(!parms)
+	{
+		var parms = {
+			mode: 'fetch',
+			pagenum: 0
+		};
+	}
+	parms.page_id = pid[0];
+	parms.namespace = pid[1];
+	if(comment_template)
+		parms.have_template = true;
+	parms = ajaxEscape(toJSONString(parms));
+	ajaxPost(stdAjaxPrefix+'&_mode=comments', 'data=' + parms, function(ajax) {
+		if ( ajax.readyState == 4 && ajax.status == 200 ) {
+			unsetAjaxLoading();
+			selectButtonMajor('discussion');
+			unselectAllButtonsMinor();
+			// IE compatibility - doing ajax.responseText.substr() doesn't work
+			var rsptxt = ajax.responseText + '';
+			if ( rsptxt.substr(0, 1) != '{' )
+			{
+				document.getElementById('ajaxEditContainer').innerHTML = '<p>Comment system Javascript runtime: invalid JSON response from server, response text:</p><pre>' + ajax.responseText + '</pre>';
+				return false;
+			}
+			var response = parseJSON(ajax.responseText);
+			switch(response.mode)
+			{
+				case 'fetch':
+					if(response.template)
+						comment_template = response.template;
+					setAjaxLoading();
+					renderComments(response);
+					unsetAjaxLoading();
+					break;
+				case 'refetch':
+					var html = '';
+					for ( var i = 0; i < response.comments.length; i++ )
+					{
+						html += window._render_comment(response.comments[i], response);
+					}
+					$('#' + response.passback.paginator_id + '_0')
+						.css('height', 'auto')
+						.css('background-color', 'transparent')
+						.css('background-image', 'none')
+						.fadeTo('fast', 100)
+						.html(html);
+					break;
+				case 'redraw':
+					redrawComment(response);
+					break;
+				case 'annihilate':
+					annihiliateComment(response.id);
+					break;
+				case 'materialize':
+					alert($lang.get('comment_msg_comment_posted'));
+					hideCommentForm();
+					materializeComment(response);
+					break;
+				case 'error':
+					load_component(['messagebox', 'fadefilter', 'flyin']);
+					new MessageBox(MB_OK|MB_ICONSTOP, ( response.title ? response.title : $lang.get('comment_ajax_err_generic_title') ), response.error);
+					break;
+				default:
+					alert(ajax.responseText);
+					break;
+			}
+		}
+	});
 }
 
 window.renderComments = function(data)
 {
-  
-  var html = '';
-  
-  // Header
-  
-    html += '<h3>' + $lang.get('comment_heading') + '</h3>';
-    
-    var ns = ENANO_PAGE_TYPE;
-  
-  // Counters
-    if ( data.auth_mod_comments )
-    {
-      var cnt = ( data.auth_mod_comments ) ? data.count_total : data.count_appr;
-      
-      var subst = {
-        num_comments: cnt,
-        page_type: ns
-      }
-      var count_msg = ( cnt == 0 ) ? $lang.get('comment_msg_count_zero', subst) : ( ( cnt == 1 ) ? $lang.get('comment_msg_count_one', subst) : $lang.get('comment_msg_count_plural', subst) );
-      
-      html += "<p id=\"comment_status\"><span>" + count_msg + '</span>';
-      if ( data.count_unappr > 0 )
-      {
-        html += ' <span style="color: #D84308" id="comment_status_unapp">' + $lang.get('comment_msg_count_unapp_mod', { num_unapp: data.count_unappr }) + '</span>';
-      }
-      html += '</p>';
-    }
-    else
-    {
-      var cnt = data.count_appr;
-      
-      var subst = {
-        num_comments: cnt,
-        page_type: ns
-      }
-      var count_msg = ( cnt == 0 ) ? $lang.get('comment_msg_count_zero', subst) : ( ( cnt == 1 ) ? $lang.get('comment_msg_count_one', subst) : $lang.get('comment_msg_count_plural', subst) );
-      
-      html += "<p id=\"comment_status\">" + count_msg;
-      if ( data.count_unappr > 0 )
-      {
-        var unappr_msg  = ( data.count_unappr == 1 ) ? $lang.get('comment_msg_count_unapp_one') : $lang.get('comment_msg_count_unapp_plural', { num_unapp: data.count_unappr });
-        html += ' ' + unappr_msg;
-      }
-      html += '</p>';
-    }
-    
-  // Comment display
-  
-  if ( data.count_total > 0 )
-  {
-    comment_render_track = 0;
-    var commentpages = new paginator(data.comments, _render_comment, 0, data.per_page, data, Math.ceil(data.count_total / data.per_page), window._comment_page_flip);
-    html += commentpages.html;
-  }
-  
-  if ( data.auth_post_comments )
-  {
-    // Posting form
-  
-    html += '<h3>' + $lang.get('comment_postform_title') + '</h3>';
-    html += '<p>' + $lang.get('comment_postform_blurb');
-    if ( data.approval_needed )
-      html+=' ' + $lang.get('comment_postform_blurb_unapp');
-    html += ' <a id="leave_comment_button" href="#" onclick="displayCommentForm(); return false;">' + $lang.get('comment_postform_blurb_link') + '</a></p>';
-    html += '<div id="comment_form" style="display: none;">';
-    html += '  <table border="0" style="width: 100%;">';
-    html += '    <tr><td>' + $lang.get('comment_postform_field_name') + '</td><td>';
-    if ( data.user_id > 1 ) html += data.username + '<input id="commentform_name" type="hidden" value="'+data.username+'" size="40" />';
-    else html += '<input id="commentform_name" type="text" size="40" style="width: 100%;" />';
-    html += '    </td></tr>';
-    html += '    <tr><td>' + $lang.get('comment_postform_field_subject') + '</td><td><input id="commentform_subject" type="text" size="40" style="width: 100%;" /></td></tr>';
-    html += '    <tr><td>' + $lang.get('comment_postform_field_comment') + '</td><td><textarea id="commentform_message" rows="15" cols="50" style="width: 100%;"></textarea></td></tr>';
-    if ( !data.logged_in && data.guest_posting == '1' )
-    {
-      html += '  <tr><td>' + $lang.get('comment_postform_field_captcha_title') + '<br /><small>' + $lang.get('comment_postform_field_captcha_blurb') + '</small></td><td>';
-      html += '  <img alt="CAPTCHA image" src="'+makeUrlNS('Special', 'Captcha/' + data.captcha)+'" onclick="this.src=\''+makeUrlNS('Special', 'Captcha/' + data.captcha)+'/\'+Math.floor(Math.random()*10000000);" style="cursor: pointer;" /><br />';
-      html += '  ' + $lang.get('comment_postform_field_captcha_label') + ' <input type="text" size="8" id="commentform_captcha" />';
-      html += '  <!-- This input is used to track the ID of the CAPTCHA image --> <input type="hidden" id="commentform_captcha_id" value="'+data.captcha+'" />';
-      html += '  </td></tr>';
-    }
-    html += '    <tr><td colspan="2" style="text-align: center;"><input type="button" onclick="submitComment();" value="' + $lang.get('comment_postform_btn_submit') + '" /></td></tr>';
-    html += '  </table>';
-    html += '</div>';
-  }
-    
-  document.getElementById('ajaxEditContainer').innerHTML = html;
-  if ( document.getElementById('commentform_message') )
-  {
-    document.getElementById('commentform_message').allow_wysiwyg = data.auth_edit_wysiwyg
-  }
-  
-  for ( i = 0; i < data.comments.length; i++ )
-  {
-    document.getElementById('comment_source_'+i).value = data.comments[i].comment_source;
-  }
-  
+	
+	var html = '';
+	
+	// Header
+	
+		html += '<h3>' + $lang.get('comment_heading') + '</h3>';
+		
+		var ns = ENANO_PAGE_TYPE;
+	
+	// Counters
+		if ( data.auth_mod_comments )
+		{
+			var cnt = ( data.auth_mod_comments ) ? data.count_total : data.count_appr;
+			
+			var subst = {
+				num_comments: cnt,
+				page_type: ns
+			}
+			var count_msg = ( cnt == 0 ) ? $lang.get('comment_msg_count_zero', subst) : ( ( cnt == 1 ) ? $lang.get('comment_msg_count_one', subst) : $lang.get('comment_msg_count_plural', subst) );
+			
+			html += "<p id=\"comment_status\"><span>" + count_msg + '</span>';
+			if ( data.count_unappr > 0 )
+			{
+				html += ' <span style="color: #D84308" id="comment_status_unapp">' + $lang.get('comment_msg_count_unapp_mod', { num_unapp: data.count_unappr }) + '</span>';
+			}
+			html += '</p>';
+		}
+		else
+		{
+			var cnt = data.count_appr;
+			
+			var subst = {
+				num_comments: cnt,
+				page_type: ns
+			}
+			var count_msg = ( cnt == 0 ) ? $lang.get('comment_msg_count_zero', subst) : ( ( cnt == 1 ) ? $lang.get('comment_msg_count_one', subst) : $lang.get('comment_msg_count_plural', subst) );
+			
+			html += "<p id=\"comment_status\">" + count_msg;
+			if ( data.count_unappr > 0 )
+			{
+				var unappr_msg  = ( data.count_unappr == 1 ) ? $lang.get('comment_msg_count_unapp_one') : $lang.get('comment_msg_count_unapp_plural', { num_unapp: data.count_unappr });
+				html += ' ' + unappr_msg;
+			}
+			html += '</p>';
+		}
+		
+	// Comment display
+	
+	if ( data.count_total > 0 )
+	{
+		comment_render_track = 0;
+		var commentpages = new paginator(data.comments, _render_comment, 0, data.per_page, data, Math.ceil(data.count_total / data.per_page), window._comment_page_flip);
+		html += commentpages.html;
+	}
+	
+	if ( data.auth_post_comments )
+	{
+		// Posting form
+	
+		html += '<h3>' + $lang.get('comment_postform_title') + '</h3>';
+		html += '<p>' + $lang.get('comment_postform_blurb');
+		if ( data.approval_needed )
+			html+=' ' + $lang.get('comment_postform_blurb_unapp');
+		html += ' <a id="leave_comment_button" href="#" onclick="displayCommentForm(); return false;">' + $lang.get('comment_postform_blurb_link') + '</a></p>';
+		html += '<div id="comment_form" style="display: none;">';
+		html += '  <table border="0" style="width: 100%;">';
+		html += '    <tr><td>' + $lang.get('comment_postform_field_name') + '</td><td>';
+		if ( data.user_id > 1 ) html += data.username + '<input id="commentform_name" type="hidden" value="'+data.username+'" size="40" />';
+		else html += '<input id="commentform_name" type="text" size="40" style="width: 100%;" />';
+		html += '    </td></tr>';
+		html += '    <tr><td>' + $lang.get('comment_postform_field_subject') + '</td><td><input id="commentform_subject" type="text" size="40" style="width: 100%;" /></td></tr>';
+		html += '    <tr><td>' + $lang.get('comment_postform_field_comment') + '</td><td><textarea id="commentform_message" rows="15" cols="50" style="width: 100%;"></textarea></td></tr>';
+		if ( !data.logged_in && data.guest_posting == '1' )
+		{
+			html += '  <tr><td>' + $lang.get('comment_postform_field_captcha_title') + '<br /><small>' + $lang.get('comment_postform_field_captcha_blurb') + '</small></td><td>';
+			html += '  <img alt="CAPTCHA image" src="'+makeUrlNS('Special', 'Captcha/' + data.captcha)+'" onclick="this.src=\''+makeUrlNS('Special', 'Captcha/' + data.captcha)+'/\'+Math.floor(Math.random()*10000000);" style="cursor: pointer;" /><br />';
+			html += '  ' + $lang.get('comment_postform_field_captcha_label') + ' <input type="text" size="8" id="commentform_captcha" />';
+			html += '  <!-- This input is used to track the ID of the CAPTCHA image --> <input type="hidden" id="commentform_captcha_id" value="'+data.captcha+'" />';
+			html += '  </td></tr>';
+		}
+		html += '    <tr><td colspan="2" style="text-align: center;"><input type="button" onclick="submitComment();" value="' + $lang.get('comment_postform_btn_submit') + '" /></td></tr>';
+		html += '  </table>';
+		html += '</div>';
+	}
+		
+	document.getElementById('ajaxEditContainer').innerHTML = html;
+	if ( document.getElementById('commentform_message') )
+	{
+		document.getElementById('commentform_message').allow_wysiwyg = data.auth_edit_wysiwyg
+	}
+	
+	for ( i = 0; i < data.comments.length; i++ )
+	{
+		document.getElementById('comment_source_'+i).value = data.comments[i].comment_source;
+	}
+	
 }
 
 var _render_comment = function(this_comment, data)
 {
-  var i = comment_render_track;
-  comment_render_track++;
-  var parser = new templateParser(comment_template);
-  var tplvars = new Object();
-  
-  if ( this_comment.approved != '1' && !data.auth_mod_comments )
-    return '';
-  
-  tplvars.ID = i;
-  tplvars.DATETIME = this_comment.time;
-  tplvars.SUBJECT = this_comment.subject;
-  tplvars.DATA = this_comment.comment_data;
-  tplvars.SIGNATURE = this_comment.signature;
-  
-  if ( this_comment.approved == '0' )
-    tplvars.SUBJECT += ' <span style="color: #D84308">' + $lang.get('comment_msg_note_unapp') + '</span>';
-  else if ( this_comment.approved == '2' )
-    tplvars.SUBJECT += ' <span style="color: #D84308">' + $lang.get('comment_msg_note_spam') + '</span>';
-  
-  // Name
-  tplvars.NAME = this_comment.name;
-  if ( this_comment.user_id > 1 )
-    tplvars.NAME = '<a href="' + makeUrlNS('User', this_comment.name) + '" style="' + this_comment.rank_style + '">' + this_comment.name + '</a>';
-  
-  // Avatar
-  if ( this_comment.user_has_avatar == '1' )
-  {
-    tplvars.AVATAR_URL = this_comment.avatar_path;
-    tplvars.USERPAGE_LINK = makeUrlNS('User', this_comment.name);
-    tplvars.AVATAR_ALT = $lang.get('usercp_avatar_image_alt', { username: this_comment.name });
-  }
-  
-  // User level
-  tplvars.USER_LEVEL = '';
-  if ( this_comment.user_title )
-    tplvars.USER_LEVEL += this_comment.user_title;
-  if ( this_comment.rank_title && this_comment.user_title )
-    tplvars.USER_LEVEL += '<br />';
-  if ( this_comment.rank_title )
-    tplvars.USER_LEVEL += $lang.get(this_comment.rank_title);
-  
-  // Send PM link
-  tplvars.SEND_PM_LINK=(this_comment.user_id>1)?'<a class="abutton icon abutton_blue" style="background-image: url(' + cdnPath + '/images/icons/send_pm.png);" onclick="window.open(this.href); return false;" href="'+ makeUrlNS('Special', 'PrivateMessages/Compose/To/' + ( this_comment.name.replace(/ /g, '_') )) +'">' + $lang.get('comment_btn_send_privmsg') + '</a><br /><br />':'';
-  
-  // Add buddy link
-  tplvars.ADD_BUDDY_LINK=(this_comment.user_id>1)?'<a class="abutton icon abutton_green" style="background-image: url(' + cdnPath + '/images/icons/add_buddy.png);" onclick="window.open(this.href); return false;" href="'+ makeUrlNS('Special', 'PrivateMessages/FriendList/Add/' + ( this_comment.name.replace(/ /g, '_') )) +'">' + $lang.get('comment_btn_add_buddy') + '</a><br />':'';
-  
-  // Edit link
-  tplvars.EDIT_LINK='<a href="#edit_'+i+'" onclick="editComment(\''+i+'\', this); return false;" id="cmteditlink_'+i+'">' + $lang.get('comment_btn_edit') + '</a>';
-  
-  // Delete link
-  tplvars.DELETE_LINK='<a href="#delete_'+i+'" onclick="deleteComment(\''+i+'\'); return false;">' + $lang.get('comment_btn_delete') + '</a>';
-  
-  // Moderation: (Un)approve link
-  var appr = ( this_comment.approved == 1 ) ? $lang.get('comment_btn_mod_unapprove') : $lang.get('comment_btn_mod_approve');
-  tplvars.MOD_APPROVE_LINK='<a href="#approve_'+i+'" id="comment_approve_'+i+'" onclick="approveComment(\''+i+'\'); return false;">'+appr+'</a>';
-  
-  // Moderation: Delete post link
-  tplvars.MOD_DELETE_LINK='<a href="#mod_del_'+i+'" onclick="deleteComment(\''+i+'\'); return false;">' + $lang.get('comment_btn_mod_delete') + '</a>';
-  
-  // Moderation: IP address link
-  if ( this_comment.have_ip )
-  {
-    tplvars.MOD_IP_LINK = '<span id="comment_ip_' + i + '"><a href="#mod_ip_' + i + '" onclick="viewCommentIP(' + this_comment.comment_id + ', ' + i + '); return false;">' + $lang.get('comment_btn_mod_ip_logged') + '</a></span>';
-  }
-  else
-  {
-    tplvars.MOD_IP_LINK = $lang.get('comment_btn_mod_ip_missing');
-  }
-  
-  var tplbool = new Object();
-  
-  tplbool.signature = ( this_comment.signature == '' ) ? false : true;
-  tplbool.can_edit = ( data.auth_edit_comments && ( ( this_comment.user_id == data.user_id && data.logged_in ) || data.auth_mod_comments ) );
-  tplbool.auth_mod = data.auth_mod_comments;
-  tplbool.is_friend = ( this_comment.is_buddy == 1 && this_comment.is_friend == 1 );
-  tplbool.is_foe = ( this_comment.is_buddy == 1 && this_comment.is_friend == 0 );
-  tplbool.user_has_avatar = ( this_comment.user_has_avatar == '1' );
-  
-  if ( tplbool.is_friend )
-    tplvars.USER_LEVEL += '<br /><b>' + $lang.get('comment_on_friend_list') + '</b>';
-  else if ( tplbool.is_foe )
-    tplvars.USER_LEVEL += '<br /><b>' + $lang.get('comment_on_foe_list') + '</b>';
-  
-  parser.assign_vars(tplvars);
-  parser.assign_bool(tplbool);
-  
-  var ret = '<div id="comment_holder_' + i + '">';
-  ret += '<input type="hidden" value="'+this_comment.comment_id+'" />';
-  ret += '<input type="hidden" id="comment_source_'+i+'" />';
-  ret += parser.run();
-  ret += '</div>';
-  return ret;
+	var i = comment_render_track;
+	comment_render_track++;
+	var parser = new templateParser(comment_template);
+	var tplvars = new Object();
+	
+	if ( this_comment.approved != '1' && !data.auth_mod_comments )
+		return '';
+	
+	tplvars.ID = i;
+	tplvars.DATETIME = this_comment.time;
+	tplvars.SUBJECT = this_comment.subject;
+	tplvars.DATA = this_comment.comment_data;
+	tplvars.SIGNATURE = this_comment.signature;
+	
+	if ( this_comment.approved == '0' )
+		tplvars.SUBJECT += ' <span style="color: #D84308">' + $lang.get('comment_msg_note_unapp') + '</span>';
+	else if ( this_comment.approved == '2' )
+		tplvars.SUBJECT += ' <span style="color: #D84308">' + $lang.get('comment_msg_note_spam') + '</span>';
+	
+	// Name
+	tplvars.NAME = this_comment.name;
+	if ( this_comment.user_id > 1 )
+		tplvars.NAME = '<a href="' + makeUrlNS('User', this_comment.name) + '" style="' + this_comment.rank_style + '">' + this_comment.name + '</a>';
+	
+	// Avatar
+	if ( this_comment.user_has_avatar == '1' )
+	{
+		tplvars.AVATAR_URL = this_comment.avatar_path;
+		tplvars.USERPAGE_LINK = makeUrlNS('User', this_comment.name);
+		tplvars.AVATAR_ALT = $lang.get('usercp_avatar_image_alt', { username: this_comment.name });
+	}
+	
+	// User level
+	tplvars.USER_LEVEL = '';
+	if ( this_comment.user_title )
+		tplvars.USER_LEVEL += this_comment.user_title;
+	if ( this_comment.rank_title && this_comment.user_title )
+		tplvars.USER_LEVEL += '<br />';
+	if ( this_comment.rank_title )
+		tplvars.USER_LEVEL += $lang.get(this_comment.rank_title);
+	
+	// Send PM link
+	tplvars.SEND_PM_LINK=(this_comment.user_id>1)?'<a class="abutton icon abutton_blue" style="background-image: url(' + cdnPath + '/images/icons/send_pm.png);" onclick="window.open(this.href); return false;" href="'+ makeUrlNS('Special', 'PrivateMessages/Compose/To/' + ( this_comment.name.replace(/ /g, '_') )) +'">' + $lang.get('comment_btn_send_privmsg') + '</a><br /><br />':'';
+	
+	// Add buddy link
+	tplvars.ADD_BUDDY_LINK=(this_comment.user_id>1)?'<a class="abutton icon abutton_green" style="background-image: url(' + cdnPath + '/images/icons/add_buddy.png);" onclick="window.open(this.href); return false;" href="'+ makeUrlNS('Special', 'PrivateMessages/FriendList/Add/' + ( this_comment.name.replace(/ /g, '_') )) +'">' + $lang.get('comment_btn_add_buddy') + '</a><br />':'';
+	
+	// Edit link
+	tplvars.EDIT_LINK='<a href="#edit_'+i+'" onclick="editComment(\''+i+'\', this); return false;" id="cmteditlink_'+i+'">' + $lang.get('comment_btn_edit') + '</a>';
+	
+	// Delete link
+	tplvars.DELETE_LINK='<a href="#delete_'+i+'" onclick="deleteComment(\''+i+'\'); return false;">' + $lang.get('comment_btn_delete') + '</a>';
+	
+	// Moderation: (Un)approve link
+	var appr = ( this_comment.approved == 1 ) ? $lang.get('comment_btn_mod_unapprove') : $lang.get('comment_btn_mod_approve');
+	tplvars.MOD_APPROVE_LINK='<a href="#approve_'+i+'" id="comment_approve_'+i+'" onclick="approveComment(\''+i+'\'); return false;">'+appr+'</a>';
+	
+	// Moderation: Delete post link
+	tplvars.MOD_DELETE_LINK='<a href="#mod_del_'+i+'" onclick="deleteComment(\''+i+'\'); return false;">' + $lang.get('comment_btn_mod_delete') + '</a>';
+	
+	// Moderation: IP address link
+	if ( this_comment.have_ip )
+	{
+		tplvars.MOD_IP_LINK = '<span id="comment_ip_' + i + '"><a href="#mod_ip_' + i + '" onclick="viewCommentIP(' + this_comment.comment_id + ', ' + i + '); return false;">' + $lang.get('comment_btn_mod_ip_logged') + '</a></span>';
+	}
+	else
+	{
+		tplvars.MOD_IP_LINK = $lang.get('comment_btn_mod_ip_missing');
+	}
+	
+	var tplbool = new Object();
+	
+	tplbool.signature = ( this_comment.signature == '' ) ? false : true;
+	tplbool.can_edit = ( data.auth_edit_comments && ( ( this_comment.user_id == data.user_id && data.logged_in ) || data.auth_mod_comments ) );
+	tplbool.auth_mod = data.auth_mod_comments;
+	tplbool.is_friend = ( this_comment.is_buddy == 1 && this_comment.is_friend == 1 );
+	tplbool.is_foe = ( this_comment.is_buddy == 1 && this_comment.is_friend == 0 );
+	tplbool.user_has_avatar = ( this_comment.user_has_avatar == '1' );
+	
+	if ( tplbool.is_friend )
+		tplvars.USER_LEVEL += '<br /><b>' + $lang.get('comment_on_friend_list') + '</b>';
+	else if ( tplbool.is_foe )
+		tplvars.USER_LEVEL += '<br /><b>' + $lang.get('comment_on_foe_list') + '</b>';
+	
+	parser.assign_vars(tplvars);
+	parser.assign_bool(tplbool);
+	
+	var ret = '<div id="comment_holder_' + i + '">';
+	ret += '<input type="hidden" value="'+this_comment.comment_id+'" />';
+	ret += '<input type="hidden" id="comment_source_'+i+'" />';
+	ret += parser.run();
+	ret += '</div>';
+	return ret;
 }
 
 window.displayCommentForm = function()
 {
-  document.getElementById('leave_comment_button').style.display = 'none';
-  document.getElementById('comment_form').style.display = 'block';
-  if ( $dynano('commentform_message').object.allow_wysiwyg )
-    $dynano('commentform_message').makeSwitchable();
+	document.getElementById('leave_comment_button').style.display = 'none';
+	document.getElementById('comment_form').style.display = 'block';
+	if ( $dynano('commentform_message').object.allow_wysiwyg )
+		$dynano('commentform_message').makeSwitchable();
 }
 
 window.hideCommentForm = function()
 {
-  document.getElementById('leave_comment_button').style.display = 'inline';
-  document.getElementById('comment_form').style.display = 'none';
+	document.getElementById('leave_comment_button').style.display = 'inline';
+	document.getElementById('comment_form').style.display = 'none';
 }
 
 window.editComment = function(id, link)
 {
-  var ctr = document.getElementById('subject_'+id);
-  var subj = ( ctr.firstChild ) ? trim(ctr.firstChild.nodeValue) : ''; // If there's a span in there that says 'unapproved', this eliminates it
-  ctr.innerHTML = '';
-  var ipt = document.createElement('input');
-  ipt.id = 'subject_edit_'+id;
-  ipt.value = subj;
-  ctr.appendChild(ipt);
-  
-  var src = document.getElementById('comment_source_'+id).value;
-  var cmt = document.getElementById('comment_'+id);
-  cmt.innerHTML = '';
-  var ta = document.createElement('textarea');
-  ta.rows = '10';
-  ta.cols = '40';
-  ta.style.width = '98%';
-  ta.value = src;
-  ta.id = 'comment_edit_'+id;
-  cmt.appendChild(ta);
-  $dynano(ta).makeSwitchable();
-  
-  link.style.fontWeight = 'bold';
-  link.innerHTML = $lang.get('comment_btn_save');
-  link.onclick = function() { var id = this.id.substr(this.id.indexOf('_')+1); saveComment(id, this); return false; };
+	var ctr = document.getElementById('subject_'+id);
+	var subj = ( ctr.firstChild ) ? trim(ctr.firstChild.nodeValue) : ''; // If there's a span in there that says 'unapproved', this eliminates it
+	ctr.innerHTML = '';
+	var ipt = document.createElement('input');
+	ipt.id = 'subject_edit_'+id;
+	ipt.value = subj;
+	ctr.appendChild(ipt);
+	
+	var src = document.getElementById('comment_source_'+id).value;
+	var cmt = document.getElementById('comment_'+id);
+	cmt.innerHTML = '';
+	var ta = document.createElement('textarea');
+	ta.rows = '10';
+	ta.cols = '40';
+	ta.style.width = '98%';
+	ta.value = src;
+	ta.id = 'comment_edit_'+id;
+	cmt.appendChild(ta);
+	$dynano(ta).makeSwitchable();
+	
+	link.style.fontWeight = 'bold';
+	link.innerHTML = $lang.get('comment_btn_save');
+	link.onclick = function() { var id = this.id.substr(this.id.indexOf('_')+1); saveComment(id, this); return false; };
 }
 
 window.saveComment = function(id, link)
 {
-  var data = document.getElementById('comment_edit_'+id).value;
-  var subj = document.getElementById('subject_edit_'+id).value;
-  var div = document.getElementById('comment_holder_'+id);
-  var real_id = div.getElementsByTagName('input')[0]['value'];
-  var req = {
-    'mode' : 'edit',
-    'id'   : real_id,
-    'local_id' : id,
-    'data' : data,
-    'subj' : subj
-  };
-  link.style.fontWeight = 'normal';
-  link.innerHTML = $lang.get('comment_btn_edit');
-  link.onclick = function() { var id = this.id.substr(this.id.indexOf('_')+1); editComment(id, this); return false; };
-  ajaxComments(req);
+	var data = document.getElementById('comment_edit_'+id).value;
+	var subj = document.getElementById('subject_edit_'+id).value;
+	var div = document.getElementById('comment_holder_'+id);
+	var real_id = div.getElementsByTagName('input')[0]['value'];
+	var req = {
+		'mode' : 'edit',
+		'id'   : real_id,
+		'local_id' : id,
+		'data' : data,
+		'subj' : subj
+	};
+	link.style.fontWeight = 'normal';
+	link.innerHTML = $lang.get('comment_btn_edit');
+	link.onclick = function() { var id = this.id.substr(this.id.indexOf('_')+1); editComment(id, this); return false; };
+	ajaxComments(req);
 }
 
 window.deleteComment = function(id)
 {
-  if ( !shift )
-  {
-    var c = confirm($lang.get('comment_msg_delete_confirm'));
-    if(!c)
-      return false;
-  }
-  var div = document.getElementById('comment_holder_'+id);
-  var real_id = div.getElementsByTagName('input')[0]['value'];
-  var req = {
-    'mode' : 'delete',
-    'id'   : real_id,
-    'local_id' : id
-  };
-  ajaxComments(req);
+	if ( !shift )
+	{
+		var c = confirm($lang.get('comment_msg_delete_confirm'));
+		if(!c)
+			return false;
+	}
+	var div = document.getElementById('comment_holder_'+id);
+	var real_id = div.getElementsByTagName('input')[0]['value'];
+	var req = {
+		'mode' : 'delete',
+		'id'   : real_id,
+		'local_id' : id
+	};
+	ajaxComments(req);
 }
 
 window.submitComment = function()
 {
-  var name = document.getElementById('commentform_name').value;
-  var subj = document.getElementById('commentform_subject').value;
-  var text = $dynano('commentform_message').getContent();
-  if ( document.getElementById('commentform_captcha') )
-  {
-    var captcha_code = document.getElementById('commentform_captcha').value;
-    var captcha_id   = document.getElementById('commentform_captcha_id').value;
-  }
-  else
-  {
-    var captcha_code = '';
-    var captcha_id   = '';
-  }
-  if ( subj == '' )
-  {
-    load_component(['messagebox', 'fadefilter']);
-    new MessageBox(MB_OK|MB_ICONSTOP, 'Input validation failed', 'Please enter a subject for your comment.');
-    return false;
-  }
-  if ( text == '' )
-  {
-    load_component(['messagebox', 'fadefilter']);
-    new MessageBox(MB_OK|MB_ICONSTOP, 'Input validation failed', 'Please enter some text for the body of your comment .');
-    return false;
-  }
-  var req = {
-    'mode' : 'submit',
-    'name' : name,
-    'subj' : subj,
-    'text' : text,
-    'captcha_code' : captcha_code,
-    'captcha_id'   : captcha_id
-  };
-  ajaxComments(req);
+	var name = document.getElementById('commentform_name').value;
+	var subj = document.getElementById('commentform_subject').value;
+	var text = $dynano('commentform_message').getContent();
+	if ( document.getElementById('commentform_captcha') )
+	{
+		var captcha_code = document.getElementById('commentform_captcha').value;
+		var captcha_id   = document.getElementById('commentform_captcha_id').value;
+	}
+	else
+	{
+		var captcha_code = '';
+		var captcha_id   = '';
+	}
+	if ( subj == '' )
+	{
+		load_component(['messagebox', 'fadefilter']);
+		new MessageBox(MB_OK|MB_ICONSTOP, 'Input validation failed', 'Please enter a subject for your comment.');
+		return false;
+	}
+	if ( text == '' )
+	{
+		load_component(['messagebox', 'fadefilter']);
+		new MessageBox(MB_OK|MB_ICONSTOP, 'Input validation failed', 'Please enter some text for the body of your comment .');
+		return false;
+	}
+	var req = {
+		'mode' : 'submit',
+		'name' : name,
+		'subj' : subj,
+		'text' : text,
+		'captcha_code' : captcha_code,
+		'captcha_id'   : captcha_id
+	};
+	ajaxComments(req);
 }
 
 window.redrawComment = function(data)
 {
-  if ( data.subj )
-  {
-    document.getElementById('subject_' + data.id).innerHTML = data.subj;
-  }
-  if ( data.approved && data.approved != '1' )
-  {
-    document.getElementById('subject_' + data.id).innerHTML += ' <span style="color: #D84308">' + ( data.approved == '2' ? $lang.get('comment_msg_note_spam') : $lang.get('comment_msg_note_unapp') ) + '</span>';
-  }
-  if ( data.approved && ( typeof(data.approve_updated) == 'string' && data.approve_updated == 'yes' ) )
-  {
-    var appr = ( data.approved == '1' ) ? $lang.get('comment_btn_mod_unapprove') : $lang.get('comment_btn_mod_approve');
-    document.getElementById('comment_approve_'+data.id).innerHTML = appr;
-    
-    if ( data.approved == '1' )
-      comment_decrement_unapproval();
-    else
-      comment_increment_unapproval();
-  }
-  if ( data.text )
-  {
-    document.getElementById('comment_' + data.id).innerHTML = data.text;
-  }
-  if ( data.src )
-  {
-    document.getElementById('comment_source_' + data.id).value = data.src;
-  }
-  if ( data.ip_addr )
-  {
-    var span = $dynano('comment_ip_' + data.local_id).object;
-    if ( !span )
-      return false;
-    span.innerHTML = $lang.get('comment_msg_ip_address') + ' <a href="#rdns" onclick="ajaxReverseDNS(this); return false;">' + data.ip_addr + '</a>';
-  }
+	if ( data.subj )
+	{
+		document.getElementById('subject_' + data.id).innerHTML = data.subj;
+	}
+	if ( data.approved && data.approved != '1' )
+	{
+		document.getElementById('subject_' + data.id).innerHTML += ' <span style="color: #D84308">' + ( data.approved == '2' ? $lang.get('comment_msg_note_spam') : $lang.get('comment_msg_note_unapp') ) + '</span>';
+	}
+	if ( data.approved && ( typeof(data.approve_updated) == 'string' && data.approve_updated == 'yes' ) )
+	{
+		var appr = ( data.approved == '1' ) ? $lang.get('comment_btn_mod_unapprove') : $lang.get('comment_btn_mod_approve');
+		document.getElementById('comment_approve_'+data.id).innerHTML = appr;
+		
+		if ( data.approved == '1' )
+			comment_decrement_unapproval();
+		else
+			comment_increment_unapproval();
+	}
+	if ( data.text )
+	{
+		document.getElementById('comment_' + data.id).innerHTML = data.text;
+	}
+	if ( data.src )
+	{
+		document.getElementById('comment_source_' + data.id).value = data.src;
+	}
+	if ( data.ip_addr )
+	{
+		var span = $dynano('comment_ip_' + data.local_id).object;
+		if ( !span )
+			return false;
+		span.innerHTML = $lang.get('comment_msg_ip_address') + ' <a href="#rdns" onclick="ajaxReverseDNS(this); return false;">' + data.ip_addr + '</a>';
+	}
 }
 
 window.approveComment = function(id)
 {
-  var div = document.getElementById('comment_holder_'+id);
-  var real_id = div.getElementsByTagName('input')[0]['value'];
-  var req = {
-    'mode' : 'approve',
-    'id'   : real_id,
-    'local_id' : id
-  };
-  ajaxComments(req);
+	var div = document.getElementById('comment_holder_'+id);
+	var real_id = div.getElementsByTagName('input')[0]['value'];
+	var req = {
+		'mode' : 'approve',
+		'id'   : real_id,
+		'local_id' : id
+	};
+	ajaxComments(req);
 }
 
 // Does the actual DOM object removal
 window.annihiliateComment = function(id) // Did I spell that right?
 {
-  var approved = true;
-  if(document.getElementById('comment_approve_'+id))
-  {
-    var appr = document.getElementById('comment_approve_'+id).firstChild.nodeValue;
-    if ( appr == $lang.get('comment_btn_mod_approve') )
-    {
-      approved = false;
-    }
-  }
-  
-  var div = document.getElementById('comment_holder_'+id);
-  div.parentNode.removeChild(div);
-  
-  // update approval status
-  if ( document.getElementById('comment_count_unapp_inner') && !approved )
-  {
-    comment_decrement_unapproval();
-  }
+	var approved = true;
+	if(document.getElementById('comment_approve_'+id))
+	{
+		var appr = document.getElementById('comment_approve_'+id).firstChild.nodeValue;
+		if ( appr == $lang.get('comment_btn_mod_approve') )
+		{
+			approved = false;
+		}
+	}
+	
+	var div = document.getElementById('comment_holder_'+id);
+	div.parentNode.removeChild(div);
+	
+	// update approval status
+	if ( document.getElementById('comment_count_unapp_inner') && !approved )
+	{
+		comment_decrement_unapproval();
+	}
 }
 
 window.materializeComment = function(data)
 {
-  // Intelligently get an ID
+	// Intelligently get an ID
 
-  var i = 0;
-  var brother;
-  while ( true )
-  {
-    var x = document.getElementById('comment_holder_'+i);
-    if(!x)
-      break;
-    brother = x;
-    i++;
-  }
-  
-  var parser = new templateParser(comment_template);
-  var tplvars = new Object();
-  
-  if ( data.approved != '1' && !data.auth_mod_comments )
-    return false;
-  
-  tplvars.ID = i;
-  tplvars.DATETIME = data.time;
-  tplvars.SUBJECT = data.subject;
-  tplvars.DATA = data.comment_data;
-  tplvars.SIGNATURE = data.signature;
-  
-  tplvars.NAME = data.name;
-  if ( data.user_id > 1 )
-    tplvars.NAME = '<a href="' + makeUrlNS('User', data.name) + '">' + data.name + '</a>';
-  
-  if ( data.approved != '1' )
-    tplvars.SUBJECT += ' <span style="color: #D84308">' + ( data.approved == '2' ? $lang.get('comment_msg_note_spam') : $lang.get('comment_msg_note_unapp') ) + '</span>';
-  
-  // Name
-  tplvars.NAME = data.name;
-  if ( data.user_id > 1 )
-    tplvars.NAME = '<a href="' + makeUrlNS('User', data.name) + '" style="' + data.rank_data.rank_style + '">' + data.name + '</a>';
-  
-  // Avatar
-  if ( data.user_has_avatar == '1' )
-  {
-    tplvars.AVATAR_URL = data.avatar_path;
-    tplvars.USERPAGE_LINK = makeUrlNS('User', data.name);
-    tplvars.AVATAR_ALT = $lang.get('usercp_avatar_image_alt', { username: data.name });
-  }
-  
-  // User level
-  tplvars.USER_LEVEL = '';
-  if ( data.rank_data.user_title )
-    tplvars.USER_LEVEL += data.rank_data.user_title;
-  if ( data.rank_data.rank_title && data.rank_data.user_title )
-    tplvars.USER_LEVEL += '<br />';
-  if ( data.rank_data.rank_title )
-    tplvars.USER_LEVEL += $lang.get(data.rank_data.rank_title);
-  
-  // Send PM link
-  tplvars.SEND_PM_LINK=(data.user_id>1)?'<a class="abutton icon abutton_blue" style="background-image: url(' + cdnPath + '/images/icons/send_pm.png);" onclick="window.open(this.href); return false;" href="'+ makeUrlNS('Special', 'PrivateMessages/Compose/To/' + ( data.name.replace(/ /g, '_') )) +'">' + $lang.get('comment_btn_send_privmsg') + '</a><br /><br />':'';
-  
-  // Add buddy link
-  tplvars.ADD_BUDDY_LINK=(data.user_id>1)?'<a class="abutton icon abutton_green" style="background-image: url(' + cdnPath + '/images/icons/add_buddy.png);" onclick="window.open(this.href); return false;" href="'+ makeUrlNS('Special', 'PrivateMessages/FriendList/Add/' + ( data.name.replace(/ /g, '_') )) +'">' + $lang.get('comment_btn_add_buddy') + '</a><br />':'';
-  
-  // Edit link
-  tplvars.EDIT_LINK='<a href="#edit_'+i+'" onclick="editComment(\''+i+'\', this); return false;" id="cmteditlink_'+i+'">' + $lang.get('comment_btn_edit') + '</a>';
-  
-  // Delete link
-  tplvars.DELETE_LINK='<a href="#delete_'+i+'" onclick="deleteComment(\''+i+'\'); return false;">' + $lang.get('comment_btn_delete') + '</a>';
-  
-  // Moderation: (Un)approve link
-  var appr = ( data.approved == 1 ) ? $lang.get('comment_btn_mod_unapprove') : $lang.get('comment_btn_mod_approve');
-  tplvars.MOD_APPROVE_LINK='<a href="#approve_'+i+'" id="comment_approve_'+i+'" onclick="approveComment(\''+i+'\'); return false;">'+appr+'</a>';
-  
-  // Moderation: Delete post link
-  tplvars.MOD_DELETE_LINK='<a href="#mod_del_'+i+'" onclick="deleteComment(\''+i+'\'); return false;">' + $lang.get('comment_btn_mod_delete') + '</a>';
-  
-  // Moderation: IP address link
-  tplvars.MOD_IP_LINK = '<span id="comment_ip_' + i + '"><a href="#mod_ip_' + i + '" onclick="viewCommentIP(' + data.comment_id + ', ' + i + '); return false;">' + $lang.get('comment_btn_mod_ip_logged') + '</a></span>';
-  
-  var tplbool = new Object();
-  
-  tplbool.signature = ( data.signature == '' ) ? false : true;
-  tplbool.can_edit = ( data.auth_edit_comments && ( ( data.user_id == data.user_id && data.logged_in ) || data.auth_mod_comments ) );
-  tplbool.auth_mod = data.auth_mod_comments;
-  tplbool.user_has_avatar = ( data.user_has_avatar == '1' );
-  
-  parser.assign_vars(tplvars);
-  parser.assign_bool(tplbool);
-  
-  var div = document.createElement('div');
-  div.id = 'comment_holder_'+i;
-  
-  div.innerHTML = '<input type="hidden" value="'+data.comment_id+'" /><input type="hidden" id="comment_source_'+i+'" />' + parser.run();
-  
-  if ( brother )
-  {
-    brother.parentNode.insertBefore(div, brother.nextSibling);
-  }
-  else
-  {
-    // No comments in ajaxEditContainer, insert it after the header
-    var aec = document.getElementById("ajaxEditContainer");
-    aec.insertBefore(div, aec.firstChild.nextSibling.nextSibling);
-  }
-  
-  document.getElementById('comment_source_'+i).value = data.comment_source;
-  
-  var cnt = document.getElementById('comment_count_inner').innerHTML;
-  cnt = parseInt(cnt);
-  if ( isNaN(cnt) )
-    cnt = 0;
-  
-  var subst = {
-    num_comments: cnt,
-    page_type: ENANO_PAGE_TYPE
-  }
-  
-  var count_msg = ( cnt == 0 ) ? $lang.get('comment_msg_count_zero', subst) : ( ( cnt == 1 ) ? $lang.get('comment_msg_count_one', subst) : $lang.get('comment_msg_count_plural', subst) );
-  
-  document.getElementById('comment_status').firstChild.innerHTML = count_msg;
-  
-  if(document.getElementById('comment_approve_'+i))
-  {
-    var is_unappr = document.getElementById('comment_approve_'+i).firstChild.nodeValue;
-    is_unappr = ( is_unappr == $lang.get('comment_btn_mod_approve') );
-    if ( is_unappr )
-    {
-      comment_increment_unapproval();
-    }
-  }
-  
+	var i = 0;
+	var brother;
+	while ( true )
+	{
+		var x = document.getElementById('comment_holder_'+i);
+		if(!x)
+			break;
+		brother = x;
+		i++;
+	}
+	
+	var parser = new templateParser(comment_template);
+	var tplvars = new Object();
+	
+	if ( data.approved != '1' && !data.auth_mod_comments )
+		return false;
+	
+	tplvars.ID = i;
+	tplvars.DATETIME = data.time;
+	tplvars.SUBJECT = data.subject;
+	tplvars.DATA = data.comment_data;
+	tplvars.SIGNATURE = data.signature;
+	
+	tplvars.NAME = data.name;
+	if ( data.user_id > 1 )
+		tplvars.NAME = '<a href="' + makeUrlNS('User', data.name) + '">' + data.name + '</a>';
+	
+	if ( data.approved != '1' )
+		tplvars.SUBJECT += ' <span style="color: #D84308">' + ( data.approved == '2' ? $lang.get('comment_msg_note_spam') : $lang.get('comment_msg_note_unapp') ) + '</span>';
+	
+	// Name
+	tplvars.NAME = data.name;
+	if ( data.user_id > 1 )
+		tplvars.NAME = '<a href="' + makeUrlNS('User', data.name) + '" style="' + data.rank_data.rank_style + '">' + data.name + '</a>';
+	
+	// Avatar
+	if ( data.user_has_avatar == '1' )
+	{
+		tplvars.AVATAR_URL = data.avatar_path;
+		tplvars.USERPAGE_LINK = makeUrlNS('User', data.name);
+		tplvars.AVATAR_ALT = $lang.get('usercp_avatar_image_alt', { username: data.name });
+	}
+	
+	// User level
+	tplvars.USER_LEVEL = '';
+	if ( data.rank_data.user_title )
+		tplvars.USER_LEVEL += data.rank_data.user_title;
+	if ( data.rank_data.rank_title && data.rank_data.user_title )
+		tplvars.USER_LEVEL += '<br />';
+	if ( data.rank_data.rank_title )
+		tplvars.USER_LEVEL += $lang.get(data.rank_data.rank_title);
+	
+	// Send PM link
+	tplvars.SEND_PM_LINK=(data.user_id>1)?'<a class="abutton icon abutton_blue" style="background-image: url(' + cdnPath + '/images/icons/send_pm.png);" onclick="window.open(this.href); return false;" href="'+ makeUrlNS('Special', 'PrivateMessages/Compose/To/' + ( data.name.replace(/ /g, '_') )) +'">' + $lang.get('comment_btn_send_privmsg') + '</a><br /><br />':'';
+	
+	// Add buddy link
+	tplvars.ADD_BUDDY_LINK=(data.user_id>1)?'<a class="abutton icon abutton_green" style="background-image: url(' + cdnPath + '/images/icons/add_buddy.png);" onclick="window.open(this.href); return false;" href="'+ makeUrlNS('Special', 'PrivateMessages/FriendList/Add/' + ( data.name.replace(/ /g, '_') )) +'">' + $lang.get('comment_btn_add_buddy') + '</a><br />':'';
+	
+	// Edit link
+	tplvars.EDIT_LINK='<a href="#edit_'+i+'" onclick="editComment(\''+i+'\', this); return false;" id="cmteditlink_'+i+'">' + $lang.get('comment_btn_edit') + '</a>';
+	
+	// Delete link
+	tplvars.DELETE_LINK='<a href="#delete_'+i+'" onclick="deleteComment(\''+i+'\'); return false;">' + $lang.get('comment_btn_delete') + '</a>';
+	
+	// Moderation: (Un)approve link
+	var appr = ( data.approved == 1 ) ? $lang.get('comment_btn_mod_unapprove') : $lang.get('comment_btn_mod_approve');
+	tplvars.MOD_APPROVE_LINK='<a href="#approve_'+i+'" id="comment_approve_'+i+'" onclick="approveComment(\''+i+'\'); return false;">'+appr+'</a>';
+	
+	// Moderation: Delete post link
+	tplvars.MOD_DELETE_LINK='<a href="#mod_del_'+i+'" onclick="deleteComment(\''+i+'\'); return false;">' + $lang.get('comment_btn_mod_delete') + '</a>';
+	
+	// Moderation: IP address link
+	tplvars.MOD_IP_LINK = '<span id="comment_ip_' + i + '"><a href="#mod_ip_' + i + '" onclick="viewCommentIP(' + data.comment_id + ', ' + i + '); return false;">' + $lang.get('comment_btn_mod_ip_logged') + '</a></span>';
+	
+	var tplbool = new Object();
+	
+	tplbool.signature = ( data.signature == '' ) ? false : true;
+	tplbool.can_edit = ( data.auth_edit_comments && ( ( data.user_id == data.user_id && data.logged_in ) || data.auth_mod_comments ) );
+	tplbool.auth_mod = data.auth_mod_comments;
+	tplbool.user_has_avatar = ( data.user_has_avatar == '1' );
+	
+	parser.assign_vars(tplvars);
+	parser.assign_bool(tplbool);
+	
+	var div = document.createElement('div');
+	div.id = 'comment_holder_'+i;
+	
+	div.innerHTML = '<input type="hidden" value="'+data.comment_id+'" /><input type="hidden" id="comment_source_'+i+'" />' + parser.run();
+	
+	if ( brother )
+	{
+		brother.parentNode.insertBefore(div, brother.nextSibling);
+	}
+	else
+	{
+		// No comments in ajaxEditContainer, insert it after the header
+		var aec = document.getElementById("ajaxEditContainer");
+		aec.insertBefore(div, aec.firstChild.nextSibling.nextSibling);
+	}
+	
+	document.getElementById('comment_source_'+i).value = data.comment_source;
+	
+	var cnt = document.getElementById('comment_count_inner').innerHTML;
+	cnt = parseInt(cnt);
+	if ( isNaN(cnt) )
+		cnt = 0;
+	
+	var subst = {
+		num_comments: cnt,
+		page_type: ENANO_PAGE_TYPE
+	}
+	
+	var count_msg = ( cnt == 0 ) ? $lang.get('comment_msg_count_zero', subst) : ( ( cnt == 1 ) ? $lang.get('comment_msg_count_one', subst) : $lang.get('comment_msg_count_plural', subst) );
+	
+	document.getElementById('comment_status').firstChild.innerHTML = count_msg;
+	
+	if(document.getElementById('comment_approve_'+i))
+	{
+		var is_unappr = document.getElementById('comment_approve_'+i).firstChild.nodeValue;
+		is_unappr = ( is_unappr == $lang.get('comment_btn_mod_approve') );
+		if ( is_unappr )
+		{
+			comment_increment_unapproval();
+		}
+	}
+	
 }
 
 window.comment_decrement_unapproval = function()
 {
-  if ( document.getElementById('comment_count_unapp_inner') )
-  {
-    var num_unapp = parseInt(document.getElementById('comment_count_unapp_inner').innerHTML);
-    if ( !isNaN(num_unapp) )
-    {
-      num_unapp = num_unapp - 1;
-      if ( num_unapp == 0 )
-      {
-        var p = document.getElementById('comment_status');
-        p.removeChild(p.childNodes[2]);
-        p.removeChild(p.childNodes[1]);
-      }
-      else
-      {
-        var count_msg = $lang.get('comment_msg_count_unapp_mod', { num_unapp: num_unapp });
-        document.getElementById('comment_count_unapp_inner').parentNode.innerHTML = count_msg;
-      }
-    }
-  }
+	if ( document.getElementById('comment_count_unapp_inner') )
+	{
+		var num_unapp = parseInt(document.getElementById('comment_count_unapp_inner').innerHTML);
+		if ( !isNaN(num_unapp) )
+		{
+			num_unapp = num_unapp - 1;
+			if ( num_unapp == 0 )
+			{
+				var p = document.getElementById('comment_status');
+				p.removeChild(p.childNodes[2]);
+				p.removeChild(p.childNodes[1]);
+			}
+			else
+			{
+				var count_msg = $lang.get('comment_msg_count_unapp_mod', { num_unapp: num_unapp });
+				document.getElementById('comment_count_unapp_inner').parentNode.innerHTML = count_msg;
+			}
+		}
+	}
 }
 
 window.comment_increment_unapproval = function()
 {
-  if ( document.getElementById('comment_count_unapp_inner') )
-  {
-    var num_unapp = parseInt(document.getElementById('comment_count_unapp_inner').innerHTML);
-    if ( isNaN(num_unapp) )
-      num_unapp = 0;
-    num_unapp = num_unapp + 1;
-    var count_msg = $lang.get('comment_msg_count_unapp_mod', { num_unapp: num_unapp });
-    document.getElementById('comment_count_unapp_inner').parentNode.innerHTML = count_msg;
-  }
-  else
-  {
-    var count_msg = $lang.get('comment_msg_count_unapp_mod', { num_unapp: 1 });
-    var status = document.getElementById('comment_status');
-    if ( !status.childNodes[1] )
-      status.appendChild(document.createTextNode(' '));
-    var span = document.createElement('span');
-    span.id = 'comment_status_unapp';
-    span.style.color = '#D84308';
-    span.innerHTML = count_msg;
-    status.appendChild(span);
-  }
+	if ( document.getElementById('comment_count_unapp_inner') )
+	{
+		var num_unapp = parseInt(document.getElementById('comment_count_unapp_inner').innerHTML);
+		if ( isNaN(num_unapp) )
+			num_unapp = 0;
+		num_unapp = num_unapp + 1;
+		var count_msg = $lang.get('comment_msg_count_unapp_mod', { num_unapp: num_unapp });
+		document.getElementById('comment_count_unapp_inner').parentNode.innerHTML = count_msg;
+	}
+	else
+	{
+		var count_msg = $lang.get('comment_msg_count_unapp_mod', { num_unapp: 1 });
+		var status = document.getElementById('comment_status');
+		if ( !status.childNodes[1] )
+			status.appendChild(document.createTextNode(' '));
+		var span = document.createElement('span');
+		span.id = 'comment_status_unapp';
+		span.style.color = '#D84308';
+		span.innerHTML = count_msg;
+		status.appendChild(span);
+	}
 }
 
 window._comment_page_flip = function(paginator, page_number)
 {
-  // get ID
-  var random_id = paginator.random_id;
-  // update paginate control
-  paginator.set_page(page_number);
-  $('.' + random_id + '_control').html(paginator._build_control(page_number));
-  paginator.offset = page_number;
-  // set to loading state
-  $('#' + random_id + '_0')
-    .css('height', 500)
-    .html('')
-    .fadeTo("fast", 0.7)
-    .css('background-position', 'center 51px')
-    .css('background-repeat', 'no-repeat')
-    .css('background-color', 'white')
-    .css('background-image', 'url(' + cdnPath + '/images/loading-big.gif' + ')')
-    .animate({ height: 150 }, 500, function()
-      {
-        // load the new comments
-        ajaxComments({
-            mode: 'fetch',
-            pagenum: page_number,
-            passback: {
-              paginator_id: random_id
-            }
-          });
-      });
+	// get ID
+	var random_id = paginator.random_id;
+	// update paginate control
+	paginator.set_page(page_number);
+	$('.' + random_id + '_control').html(paginator._build_control(page_number));
+	paginator.offset = page_number;
+	// set to loading state
+	$('#' + random_id + '_0')
+		.css('height', 500)
+		.html('')
+		.fadeTo("fast", 0.7)
+		.css('background-position', 'center 51px')
+		.css('background-repeat', 'no-repeat')
+		.css('background-color', 'white')
+		.css('background-image', 'url(' + cdnPath + '/images/loading-big.gif' + ')')
+		.animate({ height: 150 }, 500, function()
+			{
+				// load the new comments
+				ajaxComments({
+						mode: 'fetch',
+						pagenum: page_number,
+						passback: {
+							paginator_id: random_id
+						}
+					});
+			});
 }
 
 window.viewCommentIP = function(id, local_id)
 {
-  // set "loading" indicator on IP button
-  var span = $dynano('comment_ip_' + local_id).object;
-  if ( !span )
-    return false;
-  span.innerHTML = '<img alt="..." src="' + ajax_load_icon + '" />';
-  
-  var parms = {
-    mode: 'view_ip',
-    id: id,
-    local_id: local_id
-  }
-  ajaxComments(parms);
+	// set "loading" indicator on IP button
+	var span = $dynano('comment_ip_' + local_id).object;
+	if ( !span )
+		return false;
+	span.innerHTML = '<img alt="..." src="' + ajax_load_icon + '" />';
+	
+	var parms = {
+		mode: 'view_ip',
+		id: id,
+		local_id: local_id
+	}
+	ajaxComments(parms);
 }
 
--- a/includes/clientside/static/crypto.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/crypto.js	Sun Mar 28 23:10:46 2010 -0400
@@ -188,471 +188,471 @@
 md_q1=t; md_q2=t; md_q3=t; md_r=t; md_r1=t; md_r2=t; md_tt=t; //used in mod_()
 
 primes=t; pows=t; s_i=t; s_i2=t; s_R=t; s_rm=t; s_q=t; s_n1=t; 
-  s_a=t; s_r2=t; s_n=t; s_b=t; s_d=t; s_x1=t; s_x2=t, s_aa=t; //used in randTruePrime_()
+	s_a=t; s_r2=t; s_n=t; s_b=t; s_d=t; s_x1=t; s_x2=t, s_aa=t; //used in randTruePrime_()
 
 ////////////////////////////////////////////////////////////////////////////////////////
 
 //return array of all primes less than integer n
 function findPrimes(n) {
-  var i,s,p,ans;
-  s=new Array(n);
-  for (i=0;i<n;i++)
-    s[i]=0;
-  s[0]=2;
-  p=0;    //first p elements of s are primes, the rest are a sieve
-  for(;s[p]<n;) {                  //s[p] is the pth prime
-    for(i=s[p]*s[p]; i<n; i+=s[p]) //mark multiples of s[p]
-      s[i]=1;
-    p++;
-    s[p]=s[p-1]+1;
-    for(; s[p]<n && s[s[p]]; s[p]++); //find next prime (where s[p]==0)
-  }
-  ans=new Array(p);
-  for(i=0;i<p;i++)
-    ans[i]=s[i];
-  return ans;
+	var i,s,p,ans;
+	s=new Array(n);
+	for (i=0;i<n;i++)
+		s[i]=0;
+	s[0]=2;
+	p=0;    //first p elements of s are primes, the rest are a sieve
+	for(;s[p]<n;) {                  //s[p] is the pth prime
+		for(i=s[p]*s[p]; i<n; i+=s[p]) //mark multiples of s[p]
+			s[i]=1;
+		p++;
+		s[p]=s[p-1]+1;
+		for(; s[p]<n && s[s[p]]; s[p]++); //find next prime (where s[p]==0)
+	}
+	ans=new Array(p);
+	for(i=0;i<p;i++)
+		ans[i]=s[i];
+	return ans;
 }
 
 //does a single round of Miller-Rabin base b consider x to be a possible prime?
 //x is a bigInt, and b is an integer
 function millerRabin(x,b) {
-  var i,j,k,s;
+	var i,j,k,s;
 
-  if (mr_x1.length!=x.length) {
-    mr_x1=dup(x);
-    mr_r=dup(x);
-    mr_a=dup(x);
-  }
+	if (mr_x1.length!=x.length) {
+		mr_x1=dup(x);
+		mr_r=dup(x);
+		mr_a=dup(x);
+	}
 
-  copyInt_(mr_a,b);
-  copy_(mr_r,x);
-  copy_(mr_x1,x);
+	copyInt_(mr_a,b);
+	copy_(mr_r,x);
+	copy_(mr_x1,x);
 
-  addInt_(mr_r,-1);
-  addInt_(mr_x1,-1);
+	addInt_(mr_r,-1);
+	addInt_(mr_x1,-1);
 
-  //s=the highest power of two that divides mr_r
-  k=0;
-  for (i=0;i<mr_r.length;i++)
-    for (j=1;j<mask;j<<=1)
-      if (x[i] & j) {
-        s=(k<mr_r.length+bpe ? k : 0); 
-         i=mr_r.length;
-         j=mask;
-      } else
-        k++;
+	//s=the highest power of two that divides mr_r
+	k=0;
+	for (i=0;i<mr_r.length;i++)
+		for (j=1;j<mask;j<<=1)
+			if (x[i] & j) {
+				s=(k<mr_r.length+bpe ? k : 0); 
+ 				i=mr_r.length;
+ 				j=mask;
+			} else
+				k++;
 
-  if (s)                
-    rightShift_(mr_r,s);
+	if (s)                
+		rightShift_(mr_r,s);
 
-  powMod_(mr_a,mr_r,x);
+	powMod_(mr_a,mr_r,x);
 
-  if (!equalsInt(mr_a,1) && !equals(mr_a,mr_x1)) {
-    j=1;
-    while (j<=s-1 && !equals(mr_a,mr_x1)) {
-      squareMod_(mr_a,x);
-      if (equalsInt(mr_a,1)) {
-        return 0;
-      }
-      j++;
-    }
-    if (!equals(mr_a,mr_x1)) {
-      return 0;
-    }
-  }
-  return 1;  
+	if (!equalsInt(mr_a,1) && !equals(mr_a,mr_x1)) {
+		j=1;
+		while (j<=s-1 && !equals(mr_a,mr_x1)) {
+			squareMod_(mr_a,x);
+			if (equalsInt(mr_a,1)) {
+				return 0;
+			}
+			j++;
+		}
+		if (!equals(mr_a,mr_x1)) {
+			return 0;
+		}
+	}
+	return 1;  
 }
 
 //returns how many bits long the bigInt is, not counting leading zeros.
 function bitSize(x) {
-  var j,z,w;
-  for (j=x.length-1; (x[j]==0) && (j>0); j--);
-  for (z=0,w=x[j]; w; (w>>=1),z++);
-  z+=bpe*j;
-  return z;
+	var j,z,w;
+	for (j=x.length-1; (x[j]==0) && (j>0); j--);
+	for (z=0,w=x[j]; w; (w>>=1),z++);
+	z+=bpe*j;
+	return z;
 }
 
 //return a copy of x with at least n elements, adding leading zeros if needed
 function expand(x,n) {
-  var ans=int2bigInt(0,(x.length>n ? x.length : n)*bpe,0);
-  copy_(ans,x);
-  return ans;
+	var ans=int2bigInt(0,(x.length>n ? x.length : n)*bpe,0);
+	copy_(ans,x);
+	return ans;
 }
 
 //return a k-bit true random prime using Maurer's algorithm.
 function randTruePrime(k) {
-  var ans=int2bigInt(0,k,0);
-  randTruePrime_(ans,k);
-  return bigint_trim(ans,1);
+	var ans=int2bigInt(0,k,0);
+	randTruePrime_(ans,k);
+	return bigint_trim(ans,1);
 }
 
 //return a new bigInt equal to (x mod n) for bigInts x and n.
 function mod(x,n) {
-  var ans=dup(x);
-  mod_(ans,n);
-  return bigint_trim(ans,1);
+	var ans=dup(x);
+	mod_(ans,n);
+	return bigint_trim(ans,1);
 }
 
 //return (x+n) where x is a bigInt and n is an integer.
 function addInt(x,n) {
-  var ans=expand(x,x.length+1);
-  addInt_(ans,n);
-  return bigint_trim(ans,1);
+	var ans=expand(x,x.length+1);
+	addInt_(ans,n);
+	return bigint_trim(ans,1);
 }
 
 //return x*y for bigInts x and y. This is faster when y<x.
 function mult(x,y) {
-  var ans=expand(x,x.length+y.length);
-  mult_(ans,y);
-  return bigint_trim(ans,1);
+	var ans=expand(x,x.length+y.length);
+	mult_(ans,y);
+	return bigint_trim(ans,1);
 }
 
 //return (x**y mod n) where x,y,n are bigInts and ** is exponentiation.  0**0=1. Faster for odd n.
 function powMod(x,y,n) {
-  var ans=expand(x,n.length);  
-  powMod_(ans,bigint_trim(y,2),bigint_trim(n,2),0);  //this should work without the trim, but doesn't
-  return bigint_trim(ans,1);
+	var ans=expand(x,n.length);  
+	powMod_(ans,bigint_trim(y,2),bigint_trim(n,2),0);  //this should work without the trim, but doesn't
+	return bigint_trim(ans,1);
 }
 
 //return (x-y) for bigInts x and y.  Negative answers will be 2s complement
 function sub(x,y) {
-  var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1)); 
-  sub_(ans,y);
-  return bigint_trim(ans,1);
+	var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1)); 
+	sub_(ans,y);
+	return bigint_trim(ans,1);
 }
 
 //return (x+y) for bigInts x and y.  
 function add(x,y) {
-  var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1)); 
-  add_(ans,y);
-  return bigint_trim(ans,1);
+	var ans=expand(x,(x.length>y.length ? x.length+1 : y.length+1)); 
+	add_(ans,y);
+	return bigint_trim(ans,1);
 }
 
 //return (x**(-1) mod n) for bigInts x and n.  If no inverse exists, it returns null
 function inverseMod(x,n) {
-  var ans=expand(x,n.length); 
-  var s;
-  s=inverseMod_(ans,n);
-  return s ? bigint_trim(ans,1) : null;
+	var ans=expand(x,n.length); 
+	var s;
+	s=inverseMod_(ans,n);
+	return s ? bigint_trim(ans,1) : null;
 }
 
 //return (x*y mod n) for bigInts x,y,n.  For greater speed, let y<x.
 function multMod(x,y,n) {
-  var ans=expand(x,n.length);
-  multMod_(ans,y,n);
-  return bigint_trim(ans,1);
+	var ans=expand(x,n.length);
+	multMod_(ans,y,n);
+	return bigint_trim(ans,1);
 }
 
 //generate a k-bit true random prime using Maurer's algorithm,
 //and put it into ans.  The bigInt ans must be large enough to hold it.
 function randTruePrime_(ans,k) {
-  var c,m,pm,dd,j,r,B,divisible,z,zz,recSize;
+	var c,m,pm,dd,j,r,B,divisible,z,zz,recSize;
 
-  if (primes.length==0)
-    primes=findPrimes(30000);  //check for divisibility by primes <=30000
+	if (primes.length==0)
+		primes=findPrimes(30000);  //check for divisibility by primes <=30000
 
-  if (pows.length==0) {
-    pows=new Array(512);
-    for (j=0;j<512;j++) {
-      pows[j]=Math.pow(2,j/511.-1.);
-    }
-  }
+	if (pows.length==0) {
+		pows=new Array(512);
+		for (j=0;j<512;j++) {
+			pows[j]=Math.pow(2,j/511.-1.);
+		}
+	}
 
-  //c and m should be tuned for a particular machine and value of k, to maximize speed
-  c=0.1;  //c=0.1 in HAC
-  m=20;   //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits
-  recLimit=20; //stop recursion when k <=recLimit.  Must have recLimit >= 2
+	//c and m should be tuned for a particular machine and value of k, to maximize speed
+	c=0.1;  //c=0.1 in HAC
+	m=20;   //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits
+	recLimit=20; //stop recursion when k <=recLimit.  Must have recLimit >= 2
 
-  if (s_i2.length!=ans.length) {
-    s_i2=dup(ans);
-    s_R =dup(ans);
-    s_n1=dup(ans);
-    s_r2=dup(ans);
-    s_d =dup(ans);
-    s_x1=dup(ans);
-    s_x2=dup(ans);
-    s_b =dup(ans);
-    s_n =dup(ans);
-    s_i =dup(ans);
-    s_rm=dup(ans);
-    s_q =dup(ans);
-    s_a =dup(ans);
-    s_aa=dup(ans);
-  }
+	if (s_i2.length!=ans.length) {
+		s_i2=dup(ans);
+		s_R =dup(ans);
+		s_n1=dup(ans);
+		s_r2=dup(ans);
+		s_d =dup(ans);
+		s_x1=dup(ans);
+		s_x2=dup(ans);
+		s_b =dup(ans);
+		s_n =dup(ans);
+		s_i =dup(ans);
+		s_rm=dup(ans);
+		s_q =dup(ans);
+		s_a =dup(ans);
+		s_aa=dup(ans);
+	}
 
-  if (k <= recLimit) {  //generate small random primes by trial division up to its square root
-    pm=(1<<((k+2)>>1))-1; //pm is binary number with all ones, just over sqrt(2^k)
-    copyInt_(ans,0);
-    for (dd=1;dd;) {
-      dd=0;
-      ans[0]= 1 | (1<<(k-1)) | Math.floor(Math.random()*(1<<k));  //random, k-bit, odd integer, with msb 1
-      for (j=1;(j<primes.length) && ((primes[j]&pm)==primes[j]);j++) { //trial division by all primes 3...sqrt(2^k)
-        if (0==(ans[0]%primes[j])) {
-          dd=1;
-          break;
-        }
-      }
-    }
-    carry_(ans);
-    return;
-  }
+	if (k <= recLimit) {  //generate small random primes by trial division up to its square root
+		pm=(1<<((k+2)>>1))-1; //pm is binary number with all ones, just over sqrt(2^k)
+		copyInt_(ans,0);
+		for (dd=1;dd;) {
+			dd=0;
+			ans[0]= 1 | (1<<(k-1)) | Math.floor(Math.random()*(1<<k));  //random, k-bit, odd integer, with msb 1
+			for (j=1;(j<primes.length) && ((primes[j]&pm)==primes[j]);j++) { //trial division by all primes 3...sqrt(2^k)
+				if (0==(ans[0]%primes[j])) {
+					dd=1;
+					break;
+				}
+			}
+		}
+		carry_(ans);
+		return;
+	}
 
-  B=c*k*k;    //try small primes up to B (or all the primes[] array if the largest is less than B).
-  if (k>2*m)  //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits
-    for (r=1; k-k*r<=m; )
-      r=pows[Math.floor(Math.random()*512)];   //r=Math.pow(2,Math.random()-1);
-  else
-    r=.5;
+	B=c*k*k;    //try small primes up to B (or all the primes[] array if the largest is less than B).
+	if (k>2*m)  //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits
+		for (r=1; k-k*r<=m; )
+			r=pows[Math.floor(Math.random()*512)];   //r=Math.pow(2,Math.random()-1);
+	else
+		r=.5;
 
-  //simulation suggests the more complex algorithm using r=.333 is only slightly faster.
+	//simulation suggests the more complex algorithm using r=.333 is only slightly faster.
 
-  recSize=Math.floor(r*k)+1;
+	recSize=Math.floor(r*k)+1;
 
-  randTruePrime_(s_q,recSize);
-  copyInt_(s_i2,0);
-  s_i2[Math.floor((k-2)/bpe)] |= (1<<((k-2)%bpe));   //s_i2=2^(k-2)
-  divide_(s_i2,s_q,s_i,s_rm);                        //s_i=floor((2^(k-1))/(2q))
+	randTruePrime_(s_q,recSize);
+	copyInt_(s_i2,0);
+	s_i2[Math.floor((k-2)/bpe)] |= (1<<((k-2)%bpe));   //s_i2=2^(k-2)
+	divide_(s_i2,s_q,s_i,s_rm);                        //s_i=floor((2^(k-1))/(2q))
 
-  z=bitSize(s_i);
+	z=bitSize(s_i);
 
-  for (;;) {
-    for (;;) {  //generate z-bit numbers until one falls in the range [0,s_i-1]
-      randBigInt_(s_R,z,0);
-      if (greater(s_i,s_R))
-        break;
-    }                //now s_R is in the range [0,s_i-1]
-    addInt_(s_R,1);  //now s_R is in the range [1,s_i]
-    add_(s_R,s_i);   //now s_R is in the range [s_i+1,2*s_i]
+	for (;;) {
+		for (;;) {  //generate z-bit numbers until one falls in the range [0,s_i-1]
+			randBigInt_(s_R,z,0);
+			if (greater(s_i,s_R))
+				break;
+		}                //now s_R is in the range [0,s_i-1]
+		addInt_(s_R,1);  //now s_R is in the range [1,s_i]
+		add_(s_R,s_i);   //now s_R is in the range [s_i+1,2*s_i]
 
-    copy_(s_n,s_q);
-    mult_(s_n,s_R); 
-    multInt_(s_n,2);
-    addInt_(s_n,1);    //s_n=2*s_R*s_q+1
-    
-    copy_(s_r2,s_R);
-    multInt_(s_r2,2);  //s_r2=2*s_R
+		copy_(s_n,s_q);
+		mult_(s_n,s_R); 
+		multInt_(s_n,2);
+		addInt_(s_n,1);    //s_n=2*s_R*s_q+1
+		
+		copy_(s_r2,s_R);
+		multInt_(s_r2,2);  //s_r2=2*s_R
 
-    //check s_n for divisibility by small primes up to B
-    for (divisible=0,j=0; (j<primes.length) && (primes[j]<B); j++)
-      if (modInt(s_n,primes[j])==0) {
-        divisible=1;
-        break;
-      }      
+		//check s_n for divisibility by small primes up to B
+		for (divisible=0,j=0; (j<primes.length) && (primes[j]<B); j++)
+			if (modInt(s_n,primes[j])==0) {
+				divisible=1;
+				break;
+			}      
 
-    if (!divisible)    //if it passes small primes check, then try a single Miller-Rabin base 2
-      if (!millerRabin(s_n,2)) //this line represents 75% of the total runtime for randTruePrime_ 
-        divisible=1;
+		if (!divisible)    //if it passes small primes check, then try a single Miller-Rabin base 2
+			if (!millerRabin(s_n,2)) //this line represents 75% of the total runtime for randTruePrime_ 
+				divisible=1;
 
-    if (!divisible) {  //if it passes that test, continue checking s_n
-      addInt_(s_n,-3);
-      for (j=s_n.length-1;(s_n[j]==0) && (j>0); j--);  //strip leading zeros
-      for (zz=0,w=s_n[j]; w; (w>>=1),zz++);
-      zz+=bpe*j;                             //zz=number of bits in s_n, ignoring leading zeros
-      for (;;) {  //generate z-bit numbers until one falls in the range [0,s_n-1]
-        randBigInt_(s_a,zz,0);
-        if (greater(s_n,s_a))
-          break;
-      }                //now s_a is in the range [0,s_n-1]
-      addInt_(s_n,3);  //now s_a is in the range [0,s_n-4]
-      addInt_(s_a,2);  //now s_a is in the range [2,s_n-2]
-      copy_(s_b,s_a);
-      copy_(s_n1,s_n);
-      addInt_(s_n1,-1);
-      powMod_(s_b,s_n1,s_n);   //s_b=s_a^(s_n-1) modulo s_n
-      addInt_(s_b,-1);
-      if (isZero(s_b)) {
-        copy_(s_b,s_a);
-        powMod_(s_b,s_r2,s_n);
-        addInt_(s_b,-1);
-        copy_(s_aa,s_n);
-        copy_(s_d,s_b);
-        GCD_(s_d,s_n);  //if s_b and s_n are relatively prime, then s_n is a prime
-        if (equalsInt(s_d,1)) {
-          copy_(ans,s_aa);
-          return;     //if we've made it this far, then s_n is absolutely guaranteed to be prime
-        }
-      }
-    }
-  }
+		if (!divisible) {  //if it passes that test, continue checking s_n
+			addInt_(s_n,-3);
+			for (j=s_n.length-1;(s_n[j]==0) && (j>0); j--);  //strip leading zeros
+			for (zz=0,w=s_n[j]; w; (w>>=1),zz++);
+			zz+=bpe*j;                             //zz=number of bits in s_n, ignoring leading zeros
+			for (;;) {  //generate z-bit numbers until one falls in the range [0,s_n-1]
+				randBigInt_(s_a,zz,0);
+				if (greater(s_n,s_a))
+					break;
+			}                //now s_a is in the range [0,s_n-1]
+			addInt_(s_n,3);  //now s_a is in the range [0,s_n-4]
+			addInt_(s_a,2);  //now s_a is in the range [2,s_n-2]
+			copy_(s_b,s_a);
+			copy_(s_n1,s_n);
+			addInt_(s_n1,-1);
+			powMod_(s_b,s_n1,s_n);   //s_b=s_a^(s_n-1) modulo s_n
+			addInt_(s_b,-1);
+			if (isZero(s_b)) {
+				copy_(s_b,s_a);
+				powMod_(s_b,s_r2,s_n);
+				addInt_(s_b,-1);
+				copy_(s_aa,s_n);
+				copy_(s_d,s_b);
+				GCD_(s_d,s_n);  //if s_b and s_n are relatively prime, then s_n is a prime
+				if (equalsInt(s_d,1)) {
+					copy_(ans,s_aa);
+					return;     //if we've made it this far, then s_n is absolutely guaranteed to be prime
+				}
+			}
+		}
+	}
 }
 
 //Return an n-bit random BigInt (n>=1).  If s=1, then the most significant of those n bits is set to 1.
 function randBigInt(n,s) {
-  var a,b;
-  a=Math.floor((n-1)/bpe)+2; //# array elements to hold the BigInt with a leading 0 element
-  b=int2bigInt(0,0,a);
-  randBigInt_(b,n,s);
-  return b;
+	var a,b;
+	a=Math.floor((n-1)/bpe)+2; //# array elements to hold the BigInt with a leading 0 element
+	b=int2bigInt(0,0,a);
+	randBigInt_(b,n,s);
+	return b;
 }
 
 //Set b to an n-bit random BigInt.  If s=1, then the most significant of those n bits is set to 1.
 //Array b must be big enough to hold the result. Must have n>=1
 function randBigInt_(b,n,s) {
-  var i,a;
-  for (i=0;i<b.length;i++)
-    b[i]=0;
-  a=Math.floor((n-1)/bpe)+1; //# array elements to hold the BigInt
-  for (i=0;i<a;i++) {
-    b[i]=Math.floor(Math.random()*(1<<(bpe-1)));
-  }
-  b[a-1] &= (2<<((n-1)%bpe))-1;
-  if (s==1)
-    b[a-1] |= (1<<((n-1)%bpe));
+	var i,a;
+	for (i=0;i<b.length;i++)
+		b[i]=0;
+	a=Math.floor((n-1)/bpe)+1; //# array elements to hold the BigInt
+	for (i=0;i<a;i++) {
+		b[i]=Math.floor(Math.random()*(1<<(bpe-1)));
+	}
+	b[a-1] &= (2<<((n-1)%bpe))-1;
+	if (s==1)
+		b[a-1] |= (1<<((n-1)%bpe));
 }
 
 //Return the greatest common divisor of bigInts x and y (each with same number of elements).
 function GCD(x,y) {
-  var xc,yc;
-  xc=dup(x);
-  yc=dup(y);
-  GCD_(xc,yc);
-  return xc;
+	var xc,yc;
+	xc=dup(x);
+	yc=dup(y);
+	GCD_(xc,yc);
+	return xc;
 }
 
 //set x to the greatest common divisor of bigInts x and y (each with same number of elements).
 //y is destroyed.
 function GCD_(x,y) {
-  var i,xp,yp,A,B,C,D,q,sing;
-  if (T.length!=x.length)
-    T=dup(x);
+	var i,xp,yp,A,B,C,D,q,sing;
+	if (T.length!=x.length)
+		T=dup(x);
 
-  sing=1;
-  while (sing) { //while y has nonzero elements other than y[0]
-    sing=0;
-    for (i=1;i<y.length;i++) //check if y has nonzero elements other than 0
-      if (y[i]) {
-        sing=1;
-        break;
-      }
-    if (!sing) break; //quit when y all zero elements except possibly y[0]
+	sing=1;
+	while (sing) { //while y has nonzero elements other than y[0]
+		sing=0;
+		for (i=1;i<y.length;i++) //check if y has nonzero elements other than 0
+			if (y[i]) {
+				sing=1;
+				break;
+			}
+		if (!sing) break; //quit when y all zero elements except possibly y[0]
 
-    for (i=x.length;!x[i] && i>=0;i--);  //find most significant element of x
-    xp=x[i];
-    yp=y[i];
-    A=1; B=0; C=0; D=1;
-    while ((yp+C) && (yp+D)) {
-      q =Math.floor((xp+A)/(yp+C));
-      qp=Math.floor((xp+B)/(yp+D));
-      if (q!=qp)
-        break;
-      t= A-q*C;   A=C;   C=t;    //  do (A,B,xp, C,D,yp) = (C,D,yp, A,B,xp) - q*(0,0,0, C,D,yp)      
-      t= B-q*D;   B=D;   D=t;
-      t=xp-q*yp; xp=yp; yp=t;
-    }
-    if (B) {
-      copy_(T,x);
-      linComb_(x,y,A,B); //x=A*x+B*y
-      linComb_(y,T,D,C); //y=D*y+C*T
-    } else {
-      mod_(x,y);
-      copy_(T,x);
-      copy_(x,y);
-      copy_(y,T);
-    } 
-  }
-  if (y[0]==0)
-    return;
-  t=modInt(x,y[0]);
-  copyInt_(x,y[0]);
-  y[0]=t;
-  while (y[0]) {
-    x[0]%=y[0];
-    t=x[0]; x[0]=y[0]; y[0]=t;
-  }
+		for (i=x.length;!x[i] && i>=0;i--);  //find most significant element of x
+		xp=x[i];
+		yp=y[i];
+		A=1; B=0; C=0; D=1;
+		while ((yp+C) && (yp+D)) {
+			q =Math.floor((xp+A)/(yp+C));
+			qp=Math.floor((xp+B)/(yp+D));
+			if (q!=qp)
+				break;
+			t= A-q*C;   A=C;   C=t;    //  do (A,B,xp, C,D,yp) = (C,D,yp, A,B,xp) - q*(0,0,0, C,D,yp)      
+			t= B-q*D;   B=D;   D=t;
+			t=xp-q*yp; xp=yp; yp=t;
+		}
+		if (B) {
+			copy_(T,x);
+			linComb_(x,y,A,B); //x=A*x+B*y
+			linComb_(y,T,D,C); //y=D*y+C*T
+		} else {
+			mod_(x,y);
+			copy_(T,x);
+			copy_(x,y);
+			copy_(y,T);
+		} 
+	}
+	if (y[0]==0)
+		return;
+	t=modInt(x,y[0]);
+	copyInt_(x,y[0]);
+	y[0]=t;
+	while (y[0]) {
+		x[0]%=y[0];
+		t=x[0]; x[0]=y[0]; y[0]=t;
+	}
 }
 
 //do x=x**(-1) mod n, for bigInts x and n.
 //If no inverse exists, it sets x to zero and returns 0, else it returns 1.
 //The x array must be at least as large as the n array.
 function inverseMod_(x,n) {
-  var k=1+2*Math.max(x.length,n.length);
+	var k=1+2*Math.max(x.length,n.length);
 
-  if(!(x[0]&1)  && !(n[0]&1)) {  //if both inputs are even, then inverse doesn't exist
-    copyInt_(x,0);
-    return 0;
-  }
+	if(!(x[0]&1)  && !(n[0]&1)) {  //if both inputs are even, then inverse doesn't exist
+		copyInt_(x,0);
+		return 0;
+	}
 
-  if (eg_u.length!=k) {
-    eg_u=new Array(k);
-    eg_v=new Array(k);
-    eg_A=new Array(k);
-    eg_B=new Array(k);
-    eg_C=new Array(k);
-    eg_D=new Array(k);
-  }
+	if (eg_u.length!=k) {
+		eg_u=new Array(k);
+		eg_v=new Array(k);
+		eg_A=new Array(k);
+		eg_B=new Array(k);
+		eg_C=new Array(k);
+		eg_D=new Array(k);
+	}
 
-  copy_(eg_u,x);
-  copy_(eg_v,n);
-  copyInt_(eg_A,1);
-  copyInt_(eg_B,0);
-  copyInt_(eg_C,0);
-  copyInt_(eg_D,1);
-  for (;;) {
-    while(!(eg_u[0]&1)) {  //while eg_u is even
-      halve_(eg_u);
-      if (!(eg_A[0]&1) && !(eg_B[0]&1)) { //if eg_A==eg_B==0 mod 2
-        halve_(eg_A);
-        halve_(eg_B);      
-      } else {
-        add_(eg_A,n);  halve_(eg_A);
-        sub_(eg_B,x);  halve_(eg_B);
-      }
-    }
+	copy_(eg_u,x);
+	copy_(eg_v,n);
+	copyInt_(eg_A,1);
+	copyInt_(eg_B,0);
+	copyInt_(eg_C,0);
+	copyInt_(eg_D,1);
+	for (;;) {
+		while(!(eg_u[0]&1)) {  //while eg_u is even
+			halve_(eg_u);
+			if (!(eg_A[0]&1) && !(eg_B[0]&1)) { //if eg_A==eg_B==0 mod 2
+				halve_(eg_A);
+				halve_(eg_B);      
+			} else {
+				add_(eg_A,n);  halve_(eg_A);
+				sub_(eg_B,x);  halve_(eg_B);
+			}
+		}
 
-    while (!(eg_v[0]&1)) {  //while eg_v is even
-      halve_(eg_v);
-      if (!(eg_C[0]&1) && !(eg_D[0]&1)) { //if eg_C==eg_D==0 mod 2
-        halve_(eg_C);
-        halve_(eg_D);      
-      } else {
-        add_(eg_C,n);  halve_(eg_C);
-        sub_(eg_D,x);  halve_(eg_D);
-      }
-    }
+		while (!(eg_v[0]&1)) {  //while eg_v is even
+			halve_(eg_v);
+			if (!(eg_C[0]&1) && !(eg_D[0]&1)) { //if eg_C==eg_D==0 mod 2
+				halve_(eg_C);
+				halve_(eg_D);      
+			} else {
+				add_(eg_C,n);  halve_(eg_C);
+				sub_(eg_D,x);  halve_(eg_D);
+			}
+		}
 
-    if (!greater(eg_v,eg_u)) { //eg_v <= eg_u
-      sub_(eg_u,eg_v);
-      sub_(eg_A,eg_C);
-      sub_(eg_B,eg_D);
-    } else {                   //eg_v > eg_u
-      sub_(eg_v,eg_u);
-      sub_(eg_C,eg_A);
-      sub_(eg_D,eg_B);
-    }
-  
-    if (equalsInt(eg_u,0)) {
-      if (negative(eg_C)) //make sure answer is nonnegative
-        add_(eg_C,n);
-      copy_(x,eg_C);
+		if (!greater(eg_v,eg_u)) { //eg_v <= eg_u
+			sub_(eg_u,eg_v);
+			sub_(eg_A,eg_C);
+			sub_(eg_B,eg_D);
+		} else {                   //eg_v > eg_u
+			sub_(eg_v,eg_u);
+			sub_(eg_C,eg_A);
+			sub_(eg_D,eg_B);
+		}
+	
+		if (equalsInt(eg_u,0)) {
+			if (negative(eg_C)) //make sure answer is nonnegative
+				add_(eg_C,n);
+			copy_(x,eg_C);
 
-      if (!equalsInt(eg_v,1)) { //if GCD_(x,n)!=1, then there is no inverse
-        copyInt_(x,0);
-        return 0;
-      }
-      return 1;
-    }
-  }
+			if (!equalsInt(eg_v,1)) { //if GCD_(x,n)!=1, then there is no inverse
+				copyInt_(x,0);
+				return 0;
+			}
+			return 1;
+		}
+	}
 }
 
 //return x**(-1) mod n, for integers x and n.  Return 0 if there is no inverse
 function inverseModInt(x,n) {
-  var a=1,b=0,t;
-  for (;;) {
-    if (x==1) return a;
-    if (x==0) return 0;
-    b-=a*Math.floor(n/x);
-    n%=x;
+	var a=1,b=0,t;
+	for (;;) {
+		if (x==1) return a;
+		if (x==0) return 0;
+		b-=a*Math.floor(n/x);
+		n%=x;
 
-    if (n==1) return b; //to avoid negatives, change this b to n-b, and each -= to +=
-    if (n==0) return 0;
-    a-=b*Math.floor(x/n);
-    x%=n;
-  }
+		if (n==1) return b; //to avoid negatives, change this b to n-b, and each -= to +=
+		if (n==0) return 0;
+		a-=b*Math.floor(x/n);
+		x%=n;
+	}
 }
 
 //this deprecated function is for backward compatibility only. 
 function inverseModInt_(x,n) {
-   return inverseModInt(x,n);
+ 	return inverseModInt(x,n);
 }
 
 
@@ -660,76 +660,76 @@
 //     v = GCD_(x,y) = a*x-b*y
 //The bigInts v, a, b, must have exactly as many elements as the larger of x and y.
 function eGCD_(x,y,v,a,b) {
-  var g=0;
-  var k=Math.max(x.length,y.length);
-  if (eg_u.length!=k) {
-    eg_u=new Array(k);
-    eg_A=new Array(k);
-    eg_B=new Array(k);
-    eg_C=new Array(k);
-    eg_D=new Array(k);
-  }
-  while(!(x[0]&1)  && !(y[0]&1)) {  //while x and y both even
-    halve_(x);
-    halve_(y);
-    g++;
-  }
-  copy_(eg_u,x);
-  copy_(v,y);
-  copyInt_(eg_A,1);
-  copyInt_(eg_B,0);
-  copyInt_(eg_C,0);
-  copyInt_(eg_D,1);
-  for (;;) {
-    while(!(eg_u[0]&1)) {  //while u is even
-      halve_(eg_u);
-      if (!(eg_A[0]&1) && !(eg_B[0]&1)) { //if A==B==0 mod 2
-        halve_(eg_A);
-        halve_(eg_B);      
-      } else {
-        add_(eg_A,y);  halve_(eg_A);
-        sub_(eg_B,x);  halve_(eg_B);
-      }
-    }
+	var g=0;
+	var k=Math.max(x.length,y.length);
+	if (eg_u.length!=k) {
+		eg_u=new Array(k);
+		eg_A=new Array(k);
+		eg_B=new Array(k);
+		eg_C=new Array(k);
+		eg_D=new Array(k);
+	}
+	while(!(x[0]&1)  && !(y[0]&1)) {  //while x and y both even
+		halve_(x);
+		halve_(y);
+		g++;
+	}
+	copy_(eg_u,x);
+	copy_(v,y);
+	copyInt_(eg_A,1);
+	copyInt_(eg_B,0);
+	copyInt_(eg_C,0);
+	copyInt_(eg_D,1);
+	for (;;) {
+		while(!(eg_u[0]&1)) {  //while u is even
+			halve_(eg_u);
+			if (!(eg_A[0]&1) && !(eg_B[0]&1)) { //if A==B==0 mod 2
+				halve_(eg_A);
+				halve_(eg_B);      
+			} else {
+				add_(eg_A,y);  halve_(eg_A);
+				sub_(eg_B,x);  halve_(eg_B);
+			}
+		}
 
-    while (!(v[0]&1)) {  //while v is even
-      halve_(v);
-      if (!(eg_C[0]&1) && !(eg_D[0]&1)) { //if C==D==0 mod 2
-        halve_(eg_C);
-        halve_(eg_D);      
-      } else {
-        add_(eg_C,y);  halve_(eg_C);
-        sub_(eg_D,x);  halve_(eg_D);
-      }
-    }
+		while (!(v[0]&1)) {  //while v is even
+			halve_(v);
+			if (!(eg_C[0]&1) && !(eg_D[0]&1)) { //if C==D==0 mod 2
+				halve_(eg_C);
+				halve_(eg_D);      
+			} else {
+				add_(eg_C,y);  halve_(eg_C);
+				sub_(eg_D,x);  halve_(eg_D);
+			}
+		}
 
-    if (!greater(v,eg_u)) { //v<=u
-      sub_(eg_u,v);
-      sub_(eg_A,eg_C);
-      sub_(eg_B,eg_D);
-    } else {                //v>u
-      sub_(v,eg_u);
-      sub_(eg_C,eg_A);
-      sub_(eg_D,eg_B);
-    }
-    if (equalsInt(eg_u,0)) {
-      if (negative(eg_C)) {   //make sure a (C)is nonnegative
-        add_(eg_C,y);
-        sub_(eg_D,x);
-      }
-      multInt_(eg_D,-1);  ///make sure b (D) is nonnegative
-      copy_(a,eg_C);
-      copy_(b,eg_D);
-      leftShift_(v,g);
-      return;
-    }
-  }
+		if (!greater(v,eg_u)) { //v<=u
+			sub_(eg_u,v);
+			sub_(eg_A,eg_C);
+			sub_(eg_B,eg_D);
+		} else {                //v>u
+			sub_(v,eg_u);
+			sub_(eg_C,eg_A);
+			sub_(eg_D,eg_B);
+		}
+		if (equalsInt(eg_u,0)) {
+			if (negative(eg_C)) {   //make sure a (C)is nonnegative
+				add_(eg_C,y);
+				sub_(eg_D,x);
+			}
+			multInt_(eg_D,-1);  ///make sure b (D) is nonnegative
+			copy_(a,eg_C);
+			copy_(b,eg_D);
+			leftShift_(v,g);
+			return;
+		}
+	}
 }
 
 
 //is bigInt x negative?
 function negative(x) {
-  return ((x[x.length-1]>>(bpe-1))&1);
+	return ((x[x.length-1]>>(bpe-1))&1);
 }
 
 
@@ -737,39 +737,39 @@
 //x and y are nonnegative bigInts
 //shift is a nonnegative integer
 function greaterShift(x,y,shift) {
-  var kx=x.length, ky=y.length;
-  k=((kx+shift)<ky) ? (kx+shift) : ky;
-  for (i=ky-1-shift; i<kx && i>=0; i++) 
-    if (x[i]>0)
-      return 1; //if there are nonzeros in x to the left of the first column of y, then x is bigger
-  for (i=kx-1+shift; i<ky; i++)
-    if (y[i]>0)
-      return 0; //if there are nonzeros in y to the left of the first column of x, then x is not bigger
-  for (i=k-1; i>=shift; i--)
-    if      (x[i-shift]>y[i]) return 1;
-    else if (x[i-shift]<y[i]) return 0;
-  return 0;
+	var kx=x.length, ky=y.length;
+	k=((kx+shift)<ky) ? (kx+shift) : ky;
+	for (i=ky-1-shift; i<kx && i>=0; i++) 
+		if (x[i]>0)
+			return 1; //if there are nonzeros in x to the left of the first column of y, then x is bigger
+	for (i=kx-1+shift; i<ky; i++)
+		if (y[i]>0)
+			return 0; //if there are nonzeros in y to the left of the first column of x, then x is not bigger
+	for (i=k-1; i>=shift; i--)
+		if      (x[i-shift]>y[i]) return 1;
+		else if (x[i-shift]<y[i]) return 0;
+	return 0;
 }
 
 //is x > y? (x and y both nonnegative)
 function greater(x,y) {
-  var i;
-  var k=(x.length<y.length) ? x.length : y.length;
+	var i;
+	var k=(x.length<y.length) ? x.length : y.length;
 
-  for (i=x.length;i<y.length;i++)
-    if (y[i])
-      return 0;  //y has more digits
+	for (i=x.length;i<y.length;i++)
+		if (y[i])
+			return 0;  //y has more digits
 
-  for (i=y.length;i<x.length;i++)
-    if (x[i])
-      return 1;  //x has more digits
+	for (i=y.length;i<x.length;i++)
+		if (x[i])
+			return 1;  //x has more digits
 
-  for (i=k-1;i>=0;i--)
-    if (x[i]>y[i])
-      return 1;
-    else if (x[i]<y[i])
-      return 0;
-  return 0;
+	for (i=k-1;i>=0;i--)
+		if (x[i]>y[i])
+			return 1;
+		else if (x[i]<y[i])
+			return 0;
+	return 0;
 }
 
 //divide x by y giving quotient q and remainder r.  (q=floor(x/y),  r=x mod y).  All 4 are bigints.
@@ -778,87 +778,87 @@
 //q and r must be arrays that are exactly the same length as x. (Or q can have more).
 //Must have x.length >= y.length >= 2.
 function divide_(x,y,q,r) {
-  var kx, ky;
-  var i,j,y1,y2,c,a,b;
-  copy_(r,x);
-  for (ky=y.length;y[ky-1]==0;ky--); //ky is number of elements in y, not including leading zeros
+	var kx, ky;
+	var i,j,y1,y2,c,a,b;
+	copy_(r,x);
+	for (ky=y.length;y[ky-1]==0;ky--); //ky is number of elements in y, not including leading zeros
 
-  //normalize: ensure the most significant element of y has its highest bit set  
-  b=y[ky-1];
-  for (a=0; b; a++)
-    b>>=1;  
-  a=bpe-a;  //a is how many bits to shift so that the high order bit of y is leftmost in its array element
-  leftShift_(y,a);  //multiply both by 1<<a now, then divide both by that at the end
-  leftShift_(r,a);
+	//normalize: ensure the most significant element of y has its highest bit set  
+	b=y[ky-1];
+	for (a=0; b; a++)
+		b>>=1;  
+	a=bpe-a;  //a is how many bits to shift so that the high order bit of y is leftmost in its array element
+	leftShift_(y,a);  //multiply both by 1<<a now, then divide both by that at the end
+	leftShift_(r,a);
 
-  //Rob Visser discovered a bug: the following line was originally just before the normalization.
-  for (kx=r.length;r[kx-1]==0 && kx>ky;kx--); //kx is number of elements in normalized x, not including leading zeros
+	//Rob Visser discovered a bug: the following line was originally just before the normalization.
+	for (kx=r.length;r[kx-1]==0 && kx>ky;kx--); //kx is number of elements in normalized x, not including leading zeros
 
-  copyInt_(q,0);                      // q=0
-  while (!greaterShift(y,r,kx-ky)) {  // while (leftShift_(y,kx-ky) <= r) {
-    subShift_(r,y,kx-ky);             //   r=r-leftShift_(y,kx-ky)
-    q[kx-ky]++;                       //   q[kx-ky]++;
-  }                                   // }
+	copyInt_(q,0);                      // q=0
+	while (!greaterShift(y,r,kx-ky)) {  // while (leftShift_(y,kx-ky) <= r) {
+		subShift_(r,y,kx-ky);             //   r=r-leftShift_(y,kx-ky)
+		q[kx-ky]++;                       //   q[kx-ky]++;
+	}                                   // }
 
-  for (i=kx-1; i>=ky; i--) {
-    if (r[i]==y[ky-1])
-      q[i-ky]=mask;
-    else
-      q[i-ky]=Math.floor((r[i]*radix+r[i-1])/y[ky-1]);	
+	for (i=kx-1; i>=ky; i--) {
+		if (r[i]==y[ky-1])
+			q[i-ky]=mask;
+		else
+			q[i-ky]=Math.floor((r[i]*radix+r[i-1])/y[ky-1]);	
 
-    //The following for(;;) loop is equivalent to the commented while loop, 
-    //except that the uncommented version avoids overflow.
-    //The commented loop comes from HAC, which assumes r[-1]==y[-1]==0
-    //  while (q[i-ky]*(y[ky-1]*radix+y[ky-2]) > r[i]*radix*radix+r[i-1]*radix+r[i-2])
-    //    q[i-ky]--;    
-    for (;;) {
-      y2=(ky>1 ? y[ky-2] : 0)*q[i-ky];
-      c=y2>>bpe;
-      y2=y2 & mask;
-      y1=c+q[i-ky]*y[ky-1];
-      c=y1>>bpe;
-      y1=y1 & mask;
+		//The following for(;;) loop is equivalent to the commented while loop, 
+		//except that the uncommented version avoids overflow.
+		//The commented loop comes from HAC, which assumes r[-1]==y[-1]==0
+		//  while (q[i-ky]*(y[ky-1]*radix+y[ky-2]) > r[i]*radix*radix+r[i-1]*radix+r[i-2])
+		//    q[i-ky]--;    
+		for (;;) {
+			y2=(ky>1 ? y[ky-2] : 0)*q[i-ky];
+			c=y2>>bpe;
+			y2=y2 & mask;
+			y1=c+q[i-ky]*y[ky-1];
+			c=y1>>bpe;
+			y1=y1 & mask;
 
-      if (c==r[i] ? y1==r[i-1] ? y2>(i>1 ? r[i-2] : 0) : y1>r[i-1] : c>r[i]) 
-        q[i-ky]--;
-      else
-        break;
-    }
+			if (c==r[i] ? y1==r[i-1] ? y2>(i>1 ? r[i-2] : 0) : y1>r[i-1] : c>r[i]) 
+				q[i-ky]--;
+			else
+				break;
+		}
 
-    linCombShift_(r,y,-q[i-ky],i-ky);    //r=r-q[i-ky]*leftShift_(y,i-ky)
-    if (negative(r)) {
-      addShift_(r,y,i-ky);         //r=r+leftShift_(y,i-ky)
-      q[i-ky]--;
-    }
-  }
+		linCombShift_(r,y,-q[i-ky],i-ky);    //r=r-q[i-ky]*leftShift_(y,i-ky)
+		if (negative(r)) {
+			addShift_(r,y,i-ky);         //r=r+leftShift_(y,i-ky)
+			q[i-ky]--;
+		}
+	}
 
-  rightShift_(y,a);  //undo the normalization step
-  rightShift_(r,a);  //undo the normalization step
+	rightShift_(y,a);  //undo the normalization step
+	rightShift_(r,a);  //undo the normalization step
 }
 
 //do carries and borrows so each element of the bigInt x fits in bpe bits.
 function carry_(x) {
-  var i,k,c,b;
-  k=x.length;
-  c=0;
-  for (i=0;i<k;i++) {
-    c+=x[i];
-    b=0;
-    if (c<0) {
-      b=-(c>>bpe);
-      c+=b*radix;
-    }
-    x[i]=c & mask;
-    c=(c>>bpe)-b;
-  }
+	var i,k,c,b;
+	k=x.length;
+	c=0;
+	for (i=0;i<k;i++) {
+		c+=x[i];
+		b=0;
+		if (c<0) {
+			b=-(c>>bpe);
+			c+=b*radix;
+		}
+		x[i]=c & mask;
+		c=(c>>bpe)-b;
+	}
 }
 
 //return x mod n for bigInt x and integer n.
 function modInt(x,n) {
-  var i,c=0;
-  for (i=x.length-1; i>=0; i--)
-    c=(c*radix+x[i])%n;
-  return c;
+	var i,c=0;
+	for (i=x.length-1; i>=0; i--)
+		c=(c*radix+x[i])%n;
+	return c;
 }
 
 //convert the integer t into a bigInt with at least the given number of bits.
@@ -866,12 +866,12 @@
 //Pad the array with leading zeros so that it has at least minSize elements.
 //There will always be at least one leading 0 element.
 function int2bigInt(t,bits,minSize) {   
-  var i,k;
-  k=Math.ceil(bits/bpe)+1;
-  k=minSize>k ? minSize : k;
-  buff=new Array(k);
-  copyInt_(buff,t);
-  return buff;
+	var i,k;
+	k=Math.ceil(bits/bpe)+1;
+	k=minSize>k ? minSize : k;
+	buff=new Array(k);
+	copyInt_(buff,t);
+	return buff;
 }
 
 //return the bigInt given a string representation in a given base.  
@@ -879,472 +879,472 @@
 //If base=-1, then it reads in a space-separated list of array elements in decimal.
 //The array will always have at least one leading zero, unless base=-1.
 function str2bigInt(s,base,minSize) {
-  var d, i, j, x, y, kk;
-  var k=s.length;
-  if (base==-1) { //comma-separated list of array elements in decimal
-    x=new Array(0);
-    for (;;) {
-      y=new Array(x.length+1);
-      for (i=0;i<x.length;i++)
-        y[i+1]=x[i];
-      y[0]=parseInt(s,10);
-      x=y;
-      d=s.indexOf(',',0);
-      if (d<1) 
-        break;
-      s=s.substring(d+1);
-      if (s.length==0)
-        break;
-    }
-    if (x.length<minSize) {
-      y=new Array(minSize);
-      copy_(y,x);
-      return y;
-    }
-    return x;
-  }
+	var d, i, j, x, y, kk;
+	var k=s.length;
+	if (base==-1) { //comma-separated list of array elements in decimal
+		x=new Array(0);
+		for (;;) {
+			y=new Array(x.length+1);
+			for (i=0;i<x.length;i++)
+				y[i+1]=x[i];
+			y[0]=parseInt(s,10);
+			x=y;
+			d=s.indexOf(',',0);
+			if (d<1) 
+				break;
+			s=s.substring(d+1);
+			if (s.length==0)
+				break;
+		}
+		if (x.length<minSize) {
+			y=new Array(minSize);
+			copy_(y,x);
+			return y;
+		}
+		return x;
+	}
 
-  x=int2bigInt(0,base*k,0);
-  for (i=0;i<k;i++) {
-    d=digitsStr.indexOf(s.substring(i,i+1),0);
-    if (base<=36 && d>=36)  //convert lowercase to uppercase if base<=36
-      d-=26;
-    if (d<base && d>=0) {   //ignore illegal characters
-      multInt_(x,base);
-      addInt_(x,d);
-    }
-  }
+	x=int2bigInt(0,base*k,0);
+	for (i=0;i<k;i++) {
+		d=digitsStr.indexOf(s.substring(i,i+1),0);
+		if (base<=36 && d>=36)  //convert lowercase to uppercase if base<=36
+			d-=26;
+		if (d<base && d>=0) {   //ignore illegal characters
+			multInt_(x,base);
+			addInt_(x,d);
+		}
+	}
 
-  for (k=x.length;k>0 && !x[k-1];k--); //strip off leading zeros
-  k=minSize>k+1 ? minSize : k+1;
-  y=new Array(k);
-  kk=k<x.length ? k : x.length;
-  for (i=0;i<kk;i++)
-    y[i]=x[i];
-  for (;i<k;i++)
-    y[i]=0;
-  return y;
+	for (k=x.length;k>0 && !x[k-1];k--); //strip off leading zeros
+	k=minSize>k+1 ? minSize : k+1;
+	y=new Array(k);
+	kk=k<x.length ? k : x.length;
+	for (i=0;i<kk;i++)
+		y[i]=x[i];
+	for (;i<k;i++)
+		y[i]=0;
+	return y;
 }
 
 //is bigint x equal to integer y?
 //y must have less than bpe bits
 function equalsInt(x,y) {
-  var i;
-  if (x[0]!=y)
-    return 0;
-  for (i=1;i<x.length;i++)
-    if (x[i])
-      return 0;
-  return 1;
+	var i;
+	if (x[0]!=y)
+		return 0;
+	for (i=1;i<x.length;i++)
+		if (x[i])
+			return 0;
+	return 1;
 }
 
 //are bigints x and y equal?
 //this works even if x and y are different lengths and have arbitrarily many leading zeros
 function equals(x,y) {
-  var i;
-  var k=x.length<y.length ? x.length : y.length;
-  for (i=0;i<k;i++)
-    if (x[i]!=y[i])
-      return 0;
-  if (x.length>y.length) {
-    for (;i<x.length;i++)
-      if (x[i])
-        return 0;
-  } else {
-    for (;i<y.length;i++)
-      if (y[i])
-        return 0;
-  }
-  return 1;
+	var i;
+	var k=x.length<y.length ? x.length : y.length;
+	for (i=0;i<k;i++)
+		if (x[i]!=y[i])
+			return 0;
+	if (x.length>y.length) {
+		for (;i<x.length;i++)
+			if (x[i])
+				return 0;
+	} else {
+		for (;i<y.length;i++)
+			if (y[i])
+				return 0;
+	}
+	return 1;
 }
 
 //is the bigInt x equal to zero?
 function isZero(x) {
-  var i;
-  for (i=0;i<x.length;i++)
-    if (x[i])
-      return 0;
-  return 1;
+	var i;
+	for (i=0;i<x.length;i++)
+		if (x[i])
+			return 0;
+	return 1;
 }
 
 //convert a bigInt into a string in a given base, from base 2 up to base 95.
 //Base -1 prints the contents of the array representing the number.
 function bigInt2str(x,base) {
-  var i,t,s="";
+	var i,t,s="";
 
-  if (s6.length!=x.length) 
-    s6=dup(x);
-  else
-    copy_(s6,x);
+	if (s6.length!=x.length) 
+		s6=dup(x);
+	else
+		copy_(s6,x);
 
-  if (base==-1) { //return the list of array contents
-    for (i=x.length-1;i>0;i--)
-      s+=x[i]+',';
-    s+=x[0];
-  }
-  else { //return it in the given base
-    while (!isZero(s6)) {
-      t=divInt_(s6,base);  //t=s6 % base; s6=floor(s6/base);
-      s=digitsStr.substring(t,t+1)+s;
-    }
-  }
-  if (s.length==0)
-    s="0";
-  return s;
+	if (base==-1) { //return the list of array contents
+		for (i=x.length-1;i>0;i--)
+			s+=x[i]+',';
+		s+=x[0];
+	}
+	else { //return it in the given base
+		while (!isZero(s6)) {
+			t=divInt_(s6,base);  //t=s6 % base; s6=floor(s6/base);
+			s=digitsStr.substring(t,t+1)+s;
+		}
+	}
+	if (s.length==0)
+		s="0";
+	return s;
 }
 
 //returns a duplicate of bigInt x
 function dup(x) {
-  var i;
-  buff=new Array(x.length);
-  copy_(buff,x);
-  return buff;
+	var i;
+	buff=new Array(x.length);
+	copy_(buff,x);
+	return buff;
 }
 
 //do x=y on bigInts x and y.  x must be an array at least as big as y (not counting the leading zeros in y).
 function copy_(x,y) {
-  var i;
-  var k=x.length<y.length ? x.length : y.length;
-  for (i=0;i<k;i++)
-    x[i]=y[i];
-  for (i=k;i<x.length;i++)
-    x[i]=0;
+	var i;
+	var k=x.length<y.length ? x.length : y.length;
+	for (i=0;i<k;i++)
+		x[i]=y[i];
+	for (i=k;i<x.length;i++)
+		x[i]=0;
 }
 
 //do x=y on bigInt x and integer y.  
 function copyInt_(x,n) {
-  var i,c;
-  for (c=n,i=0;i<x.length;i++) {
-    x[i]=c & mask;
-    c>>=bpe;
-  }
+	var i,c;
+	for (c=n,i=0;i<x.length;i++) {
+		x[i]=c & mask;
+		c>>=bpe;
+	}
 }
 
 //do x=x+n where x is a bigInt and n is an integer.
 //x must be large enough to hold the result.
 function addInt_(x,n) {
-  var i,k,c,b;
-  x[0]+=n;
-  k=x.length;
-  c=0;
-  for (i=0;i<k;i++) {
-    c+=x[i];
-    b=0;
-    if (c<0) {
-      b=-(c>>bpe);
-      c+=b*radix;
-    }
-    x[i]=c & mask;
-    c=(c>>bpe)-b;
-    if (!c) return; //stop carrying as soon as the carry_ is zero
-  }
+	var i,k,c,b;
+	x[0]+=n;
+	k=x.length;
+	c=0;
+	for (i=0;i<k;i++) {
+		c+=x[i];
+		b=0;
+		if (c<0) {
+			b=-(c>>bpe);
+			c+=b*radix;
+		}
+		x[i]=c & mask;
+		c=(c>>bpe)-b;
+		if (!c) return; //stop carrying as soon as the carry_ is zero
+	}
 }
 
 //right shift bigInt x by n bits.  0 <= n < bpe.
 function rightShift_(x,n) {
-  var i;
-  var k=Math.floor(n/bpe);
-  if (k) {
-    for (i=0;i<x.length-k;i++) //right shift x by k elements
-      x[i]=x[i+k];
-    for (;i<x.length;i++)
-      x[i]=0;
-    n%=bpe;
-  }
-  for (i=0;i<x.length-1;i++) {
-    x[i]=mask & ((x[i+1]<<(bpe-n)) | (x[i]>>n));
-  }
-  x[i]>>=n;
+	var i;
+	var k=Math.floor(n/bpe);
+	if (k) {
+		for (i=0;i<x.length-k;i++) //right shift x by k elements
+			x[i]=x[i+k];
+		for (;i<x.length;i++)
+			x[i]=0;
+		n%=bpe;
+	}
+	for (i=0;i<x.length-1;i++) {
+		x[i]=mask & ((x[i+1]<<(bpe-n)) | (x[i]>>n));
+	}
+	x[i]>>=n;
 }
 
 //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement
 function halve_(x) {
-  var i;
-  for (i=0;i<x.length-1;i++) {
-    x[i]=mask & ((x[i+1]<<(bpe-1)) | (x[i]>>1));
-  }
-  x[i]=(x[i]>>1) | (x[i] & (radix>>1));  //most significant bit stays the same
+	var i;
+	for (i=0;i<x.length-1;i++) {
+		x[i]=mask & ((x[i+1]<<(bpe-1)) | (x[i]>>1));
+	}
+	x[i]=(x[i]>>1) | (x[i] & (radix>>1));  //most significant bit stays the same
 }
 
 //left shift bigInt x by n bits.
 function leftShift_(x,n) {
-  var i;
-  var k=Math.floor(n/bpe);
-  if (k) {
-    for (i=x.length; i>=k; i--) //left shift x by k elements
-      x[i]=x[i-k];
-    for (;i>=0;i--)
-      x[i]=0;  
-    n%=bpe;
-  }
-  if (!n)
-    return;
-  for (i=x.length-1;i>0;i--) {
-    x[i]=mask & ((x[i]<<n) | (x[i-1]>>(bpe-n)));
-  }
-  x[i]=mask & (x[i]<<n);
+	var i;
+	var k=Math.floor(n/bpe);
+	if (k) {
+		for (i=x.length; i>=k; i--) //left shift x by k elements
+			x[i]=x[i-k];
+		for (;i>=0;i--)
+			x[i]=0;  
+		n%=bpe;
+	}
+	if (!n)
+		return;
+	for (i=x.length-1;i>0;i--) {
+		x[i]=mask & ((x[i]<<n) | (x[i-1]>>(bpe-n)));
+	}
+	x[i]=mask & (x[i]<<n);
 }
 
 //do x=x*n where x is a bigInt and n is an integer.
 //x must be large enough to hold the result.
 function multInt_(x,n) {
-  var i,k,c,b;
-  if (!n)
-    return;
-  k=x.length;
-  c=0;
-  for (i=0;i<k;i++) {
-    c+=x[i]*n;
-    b=0;
-    if (c<0) {
-      b=-(c>>bpe);
-      c+=b*radix;
-    }
-    x[i]=c & mask;
-    c=(c>>bpe)-b;
-  }
+	var i,k,c,b;
+	if (!n)
+		return;
+	k=x.length;
+	c=0;
+	for (i=0;i<k;i++) {
+		c+=x[i]*n;
+		b=0;
+		if (c<0) {
+			b=-(c>>bpe);
+			c+=b*radix;
+		}
+		x[i]=c & mask;
+		c=(c>>bpe)-b;
+	}
 }
 
 //do x=floor(x/n) for bigInt x and integer n, and return the remainder
 function divInt_(x,n) {
-  var i,r=0,s;
-  for (i=x.length-1;i>=0;i--) {
-    s=r*radix+x[i];
-    x[i]=Math.floor(s/n);
-    r=s%n;
-  }
-  return r;
+	var i,r=0,s;
+	for (i=x.length-1;i>=0;i--) {
+		s=r*radix+x[i];
+		x[i]=Math.floor(s/n);
+		r=s%n;
+	}
+	return r;
 }
 
 //do the linear combination x=a*x+b*y for bigInts x and y, and integers a and b.
 //x must be large enough to hold the answer.
 function linComb_(x,y,a,b) {
-  var i,c,k,kk;
-  k=x.length<y.length ? x.length : y.length;
-  kk=x.length;
-  for (c=0,i=0;i<k;i++) {
-    c+=a*x[i]+b*y[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
-  for (i=k;i<kk;i++) {
-    c+=a*x[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
+	var i,c,k,kk;
+	k=x.length<y.length ? x.length : y.length;
+	kk=x.length;
+	for (c=0,i=0;i<k;i++) {
+		c+=a*x[i]+b*y[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
+	for (i=k;i<kk;i++) {
+		c+=a*x[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
 }
 
 //do the linear combination x=a*x+b*(y<<(ys*bpe)) for bigInts x and y, and integers a, b and ys.
 //x must be large enough to hold the answer.
 function linCombShift_(x,y,b,ys) {
-  var i,c,k,kk;
-  k=x.length<ys+y.length ? x.length : ys+y.length;
-  kk=x.length;
-  for (c=0,i=ys;i<k;i++) {
-    c+=x[i]+b*y[i-ys];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
-  for (i=k;c && i<kk;i++) {
-    c+=x[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
+	var i,c,k,kk;
+	k=x.length<ys+y.length ? x.length : ys+y.length;
+	kk=x.length;
+	for (c=0,i=ys;i<k;i++) {
+		c+=x[i]+b*y[i-ys];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
+	for (i=k;c && i<kk;i++) {
+		c+=x[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
 }
 
 //do x=x+(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys.
 //x must be large enough to hold the answer.
 function addShift_(x,y,ys) {
-  var i,c,k,kk;
-  k=x.length<ys+y.length ? x.length : ys+y.length;
-  kk=x.length;
-  for (c=0,i=ys;i<k;i++) {
-    c+=x[i]+y[i-ys];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
-  for (i=k;c && i<kk;i++) {
-    c+=x[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
+	var i,c,k,kk;
+	k=x.length<ys+y.length ? x.length : ys+y.length;
+	kk=x.length;
+	for (c=0,i=ys;i<k;i++) {
+		c+=x[i]+y[i-ys];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
+	for (i=k;c && i<kk;i++) {
+		c+=x[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
 }
 
 //do x=x-(y<<(ys*bpe)) for bigInts x and y, and integers a,b and ys.
 //x must be large enough to hold the answer.
 function subShift_(x,y,ys) {
-  var i,c,k,kk;
-  k=x.length<ys+y.length ? x.length : ys+y.length;
-  kk=x.length;
-  for (c=0,i=ys;i<k;i++) {
-    c+=x[i]-y[i-ys];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
-  for (i=k;c && i<kk;i++) {
-    c+=x[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
+	var i,c,k,kk;
+	k=x.length<ys+y.length ? x.length : ys+y.length;
+	kk=x.length;
+	for (c=0,i=ys;i<k;i++) {
+		c+=x[i]-y[i-ys];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
+	for (i=k;c && i<kk;i++) {
+		c+=x[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
 }
 
 //do x=x-y for bigInts x and y.
 //x must be large enough to hold the answer.
 //negative answers will be 2s complement
 function sub_(x,y) {
-  var i,c,k,kk;
-  k=x.length<y.length ? x.length : y.length;
-  for (c=0,i=0;i<k;i++) {
-    c+=x[i]-y[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
-  for (i=k;c && i<x.length;i++) {
-    c+=x[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
+	var i,c,k,kk;
+	k=x.length<y.length ? x.length : y.length;
+	for (c=0,i=0;i<k;i++) {
+		c+=x[i]-y[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
+	for (i=k;c && i<x.length;i++) {
+		c+=x[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
 }
 
 //do x=x+y for bigInts x and y.
 //x must be large enough to hold the answer.
 function add_(x,y) {
-  var i,c,k,kk;
-  k=x.length<y.length ? x.length : y.length;
-  for (c=0,i=0;i<k;i++) {
-    c+=x[i]+y[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
-  for (i=k;c && i<x.length;i++) {
-    c+=x[i];
-    x[i]=c & mask;
-    c>>=bpe;
-  }
+	var i,c,k,kk;
+	k=x.length<y.length ? x.length : y.length;
+	for (c=0,i=0;i<k;i++) {
+		c+=x[i]+y[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
+	for (i=k;c && i<x.length;i++) {
+		c+=x[i];
+		x[i]=c & mask;
+		c>>=bpe;
+	}
 }
 
 //do x=x*y for bigInts x and y.  This is faster when y<x.
 function mult_(x,y) {
-  var i;
-  if (ss.length!=2*x.length)
-    ss=new Array(2*x.length);
-  copyInt_(ss,0);
-  for (i=0;i<y.length;i++)
-    if (y[i])
-      linCombShift_(ss,x,y[i],i);   //ss=1*ss+y[i]*(x<<(i*bpe))
-  copy_(x,ss);
+	var i;
+	if (ss.length!=2*x.length)
+		ss=new Array(2*x.length);
+	copyInt_(ss,0);
+	for (i=0;i<y.length;i++)
+		if (y[i])
+			linCombShift_(ss,x,y[i],i);   //ss=1*ss+y[i]*(x<<(i*bpe))
+	copy_(x,ss);
 }
 
 //do x=x mod n for bigInts x and n.
 function mod_(x,n) {
-  if (s4.length!=x.length)
-    s4=dup(x);
-  else
-    copy_(s4,x);
-  if (s5.length!=x.length)
-    s5=dup(x);  
-  divide_(s4,n,s5,x);  //x = remainder of s4 / n
+	if (s4.length!=x.length)
+		s4=dup(x);
+	else
+		copy_(s4,x);
+	if (s5.length!=x.length)
+		s5=dup(x);  
+	divide_(s4,n,s5,x);  //x = remainder of s4 / n
 }
 
 //do x=x*y mod n for bigInts x,y,n.
 //for greater speed, let y<x.
 function multMod_(x,y,n) {
-  var i;
-  if (s0.length!=2*x.length)
-    s0=new Array(2*x.length);
-  copyInt_(s0,0);
-  for (i=0;i<y.length;i++)
-    if (y[i])
-      linCombShift_(s0,x,y[i],i);   //s0=1*s0+y[i]*(x<<(i*bpe))
-  mod_(s0,n);
-  copy_(x,s0);
+	var i;
+	if (s0.length!=2*x.length)
+		s0=new Array(2*x.length);
+	copyInt_(s0,0);
+	for (i=0;i<y.length;i++)
+		if (y[i])
+			linCombShift_(s0,x,y[i],i);   //s0=1*s0+y[i]*(x<<(i*bpe))
+	mod_(s0,n);
+	copy_(x,s0);
 }
 
 //do x=x*x mod n for bigInts x,n.
 function squareMod_(x,n) {
-  var i,j,d,c,kx,kn,k;
-  for (kx=x.length; kx>0 && !x[kx-1]; kx--);  //ignore leading zeros in x
-  k=kx>n.length ? 2*kx : 2*n.length; //k=# elements in the product, which is twice the elements in the larger of x and n
-  if (s0.length!=k) 
-    s0=new Array(k);
-  copyInt_(s0,0);
-  for (i=0;i<kx;i++) {
-    c=s0[2*i]+x[i]*x[i];
-    s0[2*i]=c & mask;
-    c>>=bpe;
-    for (j=i+1;j<kx;j++) {
-      c=s0[i+j]+2*x[i]*x[j]+c;
-      s0[i+j]=(c & mask);
-      c>>=bpe;
-    }
-    s0[i+kx]=c;
-  }
-  mod_(s0,n);
-  copy_(x,s0);
+	var i,j,d,c,kx,kn,k;
+	for (kx=x.length; kx>0 && !x[kx-1]; kx--);  //ignore leading zeros in x
+	k=kx>n.length ? 2*kx : 2*n.length; //k=# elements in the product, which is twice the elements in the larger of x and n
+	if (s0.length!=k) 
+		s0=new Array(k);
+	copyInt_(s0,0);
+	for (i=0;i<kx;i++) {
+		c=s0[2*i]+x[i]*x[i];
+		s0[2*i]=c & mask;
+		c>>=bpe;
+		for (j=i+1;j<kx;j++) {
+			c=s0[i+j]+2*x[i]*x[j]+c;
+			s0[i+j]=(c & mask);
+			c>>=bpe;
+		}
+		s0[i+kx]=c;
+	}
+	mod_(s0,n);
+	copy_(x,s0);
 }
 
 //return x with exactly k leading zero elements
 function bigint_trim(x,k) {
-  var i,y;
-  for (i=x.length; i>0 && !x[i-1]; i--);
-  y=new Array(i+k);
-  copy_(y,x);
-  return y;
+	var i,y;
+	for (i=x.length; i>0 && !x[i-1]; i--);
+	y=new Array(i+k);
+	copy_(y,x);
+	return y;
 }
 
 //do x=x**y mod n, where x,y,n are bigInts and ** is exponentiation.  0**0=1.
 //this is faster when n is odd.  x usually needs to have as many elements as n.
 function powMod_(x,y,n) {
-  var k1,k2,kn,np;
-  if(s7.length!=n.length)
-    s7=dup(n);
+	var k1,k2,kn,np;
+	if(s7.length!=n.length)
+		s7=dup(n);
 
-  //for even modulus, use a simple square-and-multiply algorithm,
-  //rather than using the more complex Montgomery algorithm.
-  if ((n[0]&1)==0) {
-    copy_(s7,x);
-    copyInt_(x,1);
-    while(!equalsInt(y,0)) {
-      if (y[0]&1)
-        multMod_(x,s7,n);
-      divInt_(y,2);
-      squareMod_(s7,n); 
-    }
-    return;
-  }
+	//for even modulus, use a simple square-and-multiply algorithm,
+	//rather than using the more complex Montgomery algorithm.
+	if ((n[0]&1)==0) {
+		copy_(s7,x);
+		copyInt_(x,1);
+		while(!equalsInt(y,0)) {
+			if (y[0]&1)
+				multMod_(x,s7,n);
+			divInt_(y,2);
+			squareMod_(s7,n); 
+		}
+		return;
+	}
 
-  //calculate np from n for the Montgomery multiplications
-  copyInt_(s7,0);
-  for (kn=n.length;kn>0 && !n[kn-1];kn--);
-  np=radix-inverseModInt(modInt(n,radix),radix);
-  s7[kn]=1;
-  multMod_(x ,s7,n);   // x = x * 2**(kn*bp) mod n
+	//calculate np from n for the Montgomery multiplications
+	copyInt_(s7,0);
+	for (kn=n.length;kn>0 && !n[kn-1];kn--);
+	np=radix-inverseModInt(modInt(n,radix),radix);
+	s7[kn]=1;
+	multMod_(x ,s7,n);   // x = x * 2**(kn*bp) mod n
 
-  if (s3.length!=x.length)
-    s3=dup(x);
-  else
-    copy_(s3,x);
+	if (s3.length!=x.length)
+		s3=dup(x);
+	else
+		copy_(s3,x);
 
-  for (k1=y.length-1;k1>0 & !y[k1]; k1--);  //k1=first nonzero element of y
-  if (y[k1]==0) {  //anything to the 0th power is 1
-    copyInt_(x,1);
-    return;
-  }
-  for (k2=1<<(bpe-1);k2 && !(y[k1] & k2); k2>>=1);  //k2=position of first 1 bit in y[k1]
-  for (;;) {
-    if (!(k2>>=1)) {  //look at next bit of y
-      k1--;
-      if (k1<0) {
-        mont_(x,one,n,np);
-        return;
-      }
-      k2=1<<(bpe-1);
-    }    
-    mont_(x,x,n,np);
+	for (k1=y.length-1;k1>0 & !y[k1]; k1--);  //k1=first nonzero element of y
+	if (y[k1]==0) {  //anything to the 0th power is 1
+		copyInt_(x,1);
+		return;
+	}
+	for (k2=1<<(bpe-1);k2 && !(y[k1] & k2); k2>>=1);  //k2=position of first 1 bit in y[k1]
+	for (;;) {
+		if (!(k2>>=1)) {  //look at next bit of y
+			k1--;
+			if (k1<0) {
+				mont_(x,one,n,np);
+				return;
+			}
+			k2=1<<(bpe-1);
+		}    
+		mont_(x,x,n,np);
 
-    if (k2 & y[k1]) //if next bit is a 1
-      mont_(x,s3,n,np);
-  }
+		if (k2 & y[k1]) //if next bit is a 1
+			mont_(x,s3,n,np);
+	}
 }    
 
 //do x=x*y*Ri mod n for bigInts x,y,n, 
@@ -1358,48 +1358,48 @@
 //  n is odd
 //  np = -(n^(-1)) mod radix
 function mont_(x,y,n,np) {
-  var i,j,c,ui,t;
-  var kn=n.length;
-  var ky=y.length;
+	var i,j,c,ui,t;
+	var kn=n.length;
+	var ky=y.length;
 
-  if (sa.length!=kn)
-    sa=new Array(kn);
+	if (sa.length!=kn)
+		sa=new Array(kn);
 
-  for (;kn>0 && n[kn-1]==0;kn--); //ignore leading zeros of n
-  //this function sometimes gives wrong answers when the next line is uncommented
-  //for (;ky>0 && y[ky-1]==0;ky--); //ignore leading zeros of y
+	for (;kn>0 && n[kn-1]==0;kn--); //ignore leading zeros of n
+	//this function sometimes gives wrong answers when the next line is uncommented
+	//for (;ky>0 && y[ky-1]==0;ky--); //ignore leading zeros of y
 
-  copyInt_(sa,0);
+	copyInt_(sa,0);
 
-  //the following loop consumes 95% of the runtime for randTruePrime_() and powMod_() for large keys
-  for (i=0; i<kn; i++) {
-    t=sa[0]+x[i]*y[0];
-    ui=((t & mask) * np) & mask;  //the inner "& mask" is needed on Macintosh MSIE, but not windows MSIE
-    c=(t+ui*n[0]) >> bpe;
-    t=x[i];
+	//the following loop consumes 95% of the runtime for randTruePrime_() and powMod_() for large keys
+	for (i=0; i<kn; i++) {
+		t=sa[0]+x[i]*y[0];
+		ui=((t & mask) * np) & mask;  //the inner "& mask" is needed on Macintosh MSIE, but not windows MSIE
+		c=(t+ui*n[0]) >> bpe;
+		t=x[i];
 
-    //do sa=(sa+x[i]*y+ui*n)/b   where b=2**bpe
-    for (j=1;j<ky;j++) { 
-      c+=sa[j]+t*y[j]+ui*n[j];
-      sa[j-1]=c & mask;
-      c>>=bpe;
-    }    
-    for (;j<kn;j++) { 
-      c+=sa[j]+ui*n[j];
-      sa[j-1]=c & mask;
-      c>>=bpe;
-    }    
-    sa[j-1]=c & mask;
-  }
+		//do sa=(sa+x[i]*y+ui*n)/b   where b=2**bpe
+		for (j=1;j<ky;j++) { 
+			c+=sa[j]+t*y[j]+ui*n[j];
+			sa[j-1]=c & mask;
+			c>>=bpe;
+		}    
+		for (;j<kn;j++) { 
+			c+=sa[j]+ui*n[j];
+			sa[j-1]=c & mask;
+			c>>=bpe;
+		}    
+		sa[j-1]=c & mask;
+	}
 
-  if (!greater(n,sa))
-    sub_(sa,n);
-  copy_(x,sa);
+	if (!greater(n,sa))
+		sub_(sa,n);
+	copy_(x,sa);
 }
 
 
 /* rijndael.js      Rijndael Reference Implementation
-   Copyright (c) 2001 Fritz Schneider
+ 	Copyright (c) 2001 Fritz Schneider
  
  This software is provided as-is, without express or implied warranty.  
  Permission to use, copy, modify, distribute or sell this software, with or
@@ -1410,38 +1410,38 @@
  provided with the application or distribution.
 
 
-   As the above disclaimer notes, you are free to use this code however you
-   want. However, I would request that you send me an email 
-   (fritz /at/ cs /dot/ ucsd /dot/ edu) to say hi if you find this code useful
-   or instructional. Seeing that people are using the code acts as 
-   encouragement for me to continue development. If you *really* want to thank
-   me you can buy the book I wrote with Thomas Powell, _JavaScript:
-   _The_Complete_Reference_ :)
+ 	As the above disclaimer notes, you are free to use this code however you
+ 	want. However, I would request that you send me an email 
+ 	(fritz /at/ cs /dot/ ucsd /dot/ edu) to say hi if you find this code useful
+ 	or instructional. Seeing that people are using the code acts as 
+ 	encouragement for me to continue development. If you *really* want to thank
+ 	me you can buy the book I wrote with Thomas Powell, _JavaScript:
+ 	_The_Complete_Reference_ :)
 
-   This code is an UNOPTIMIZED REFERENCE implementation of Rijndael. 
-   If there is sufficient interest I can write an optimized (word-based, 
-   table-driven) version, although you might want to consider using a 
-   compiled language if speed is critical to your application. As it stands,
-   one run of the monte carlo test (10,000 encryptions) can take up to 
-   several minutes, depending upon your processor. You shouldn't expect more
-   than a few kilobytes per second in throughput.
+ 	This code is an UNOPTIMIZED REFERENCE implementation of Rijndael. 
+ 	If there is sufficient interest I can write an optimized (word-based, 
+ 	table-driven) version, although you might want to consider using a 
+ 	compiled language if speed is critical to your application. As it stands,
+ 	one run of the monte carlo test (10,000 encryptions) can take up to 
+ 	several minutes, depending upon your processor. You shouldn't expect more
+ 	than a few kilobytes per second in throughput.
 
-   Also note that there is very little error checking in these functions. 
-   Doing proper error checking is always a good idea, but the ideal 
-   implementation (using the instanceof operator and exceptions) requires
-   IE5+/NS6+, and I've chosen to implement this code so that it is compatible
-   with IE4/NS4. 
+ 	Also note that there is very little error checking in these functions. 
+ 	Doing proper error checking is always a good idea, but the ideal 
+ 	implementation (using the instanceof operator and exceptions) requires
+ 	IE5+/NS6+, and I've chosen to implement this code so that it is compatible
+ 	with IE4/NS4. 
 
-   And finally, because JavaScript doesn't have an explicit byte/char data 
-   type (although JavaScript 2.0 most likely will), when I refer to "byte" 
-   in this code I generally mean "32 bit integer with value in the interval 
-   [0,255]" which I treat as a byte.
+ 	And finally, because JavaScript doesn't have an explicit byte/char data 
+ 	type (although JavaScript 2.0 most likely will), when I refer to "byte" 
+ 	in this code I generally mean "32 bit integer with value in the interval 
+ 	[0,255]" which I treat as a byte.
 
-   See http://www-cse.ucsd.edu/~fritz/rijndael.html for more documentation
-   of the (very simple) API provided by this code.
+ 	See http://www-cse.ucsd.edu/~fritz/rijndael.html for more documentation
+ 	of the (very simple) API provided by this code.
 
-                                               Fritz Schneider
-                                               fritz at cs.ucsd.edu
+ 																							Fritz Schneider
+ 																							fritz at cs.ucsd.edu
  
 */
 
@@ -1460,8 +1460,8 @@
 
 // The number of rounds for the cipher, indexed by [Nk][Nb]
 var roundsArray = [ ,,,,[,,,,10,, 12,, 14],, 
-                        [,,,,12,, 12,, 14],, 
-                        [,,,,14,, 14,, 14] ];
+												[,,,,12,, 12,, 14],, 
+												[,,,,14,, 14,, 14] ];
 
 // The number of bytes to shift by in shiftRow, indexed by [Nb][row]
 var shiftOffsets = [ ,,,,[,1, 2, 3],,[,1, 2, 3],,[,1, 3, 4] ];
@@ -1487,7 +1487,7 @@
 188, 182, 218,  33,  16, 255, 243, 210, 205,  12,  19, 236,  95, 151,  68,  
 23,  196, 167, 126,  61, 100,  93,  25, 115,  96, 129,  79, 220,  34,  42, 
 144, 136,  70, 238, 184,  20, 222,  94,  11, 219, 224,  50,  58,  10,  73,
-  6,  36,  92, 194, 211, 172,  98, 145, 149, 228, 121, 231, 200,  55, 109, 
+	6,  36,  92, 194, 211, 172,  98, 145, 149, 228, 121, 231, 200,  55, 109, 
 141, 213,  78, 169, 108,  86, 244, 234, 101, 122, 174,   8, 186, 120,  37,  
  46,  28, 166, 180, 198, 232, 221, 116,  31,  75, 189, 139, 138, 112,  62, 
 181, 102,  72,   3, 246,  14,  97,  53,  87, 185, 134, 193,  29, 158, 225,
@@ -1518,13 +1518,13 @@
 
 function str_split(string, chunklen)
 {
-  if(!chunklen) chunklen = 1;
-  ret = new Array();
-  for ( i = 0; i < string.length; i+=chunklen )
-  {
-    ret[ret.length] = string.slice(i, i+chunklen);
-  }
-  return ret;
+	if(!chunklen) chunklen = 1;
+	ret = new Array();
+	for ( i = 0; i < string.length; i+=chunklen )
+	{
+		ret[ret.length] = string.slice(i, i+chunklen);
+	}
+	return ret;
 }
 
 // This method circularly shifts the array left by the number of elements
@@ -1533,9 +1533,9 @@
 // elegant solution, but they require IE5.5+, so I chose to do it manually. 
 
 function cyclicShiftLeft(theArray, positions) {
-  var temp = theArray.slice(0, positions);
-  theArray = theArray.slice(positions).concat(temp);
-  return theArray;
+	var temp = theArray.slice(0, positions);
+	theArray = theArray.slice(positions).concat(temp);
+	return theArray;
 }
 
 // Cipher parameters ... do not change these
@@ -1546,8 +1546,8 @@
 // Multiplies the element "poly" of GF(2^8) by x. See the Rijndael spec.
 
 function xtime(poly) {
-  poly <<= 1;
-  return ((poly & 0x100) ? (poly ^ 0x11B) : (poly));
+	poly <<= 1;
+	return ((poly & 0x100) ? (poly ^ 0x11B) : (poly));
 }
 
 // Multiplies the two elements of GF(2^8) together and returns the result.
@@ -1556,13 +1556,13 @@
 // to the result. x and y should be bytes representing elements of GF(2^8)
 
 function mult_GF256(x, y) {
-  var bit, result = 0;
-  
-  for (bit = 1; bit < 256; bit *= 2, y = xtime(y)) {
-    if (x & bit) 
-      result ^= y;
-  }
-  return result;
+	var bit, result = 0;
+	
+	for (bit = 1; bit < 256; bit *= 2, y = xtime(y)) {
+		if (x & bit) 
+			result ^= y;
+	}
+	return result;
 }
 
 // Performs the substitution step of the cipher. State is the 2d array of
@@ -1571,24 +1571,24 @@
 // substitution (anything else)
 
 function byteSub(state, direction) {
-  var S;
-  if (direction == "encrypt")           // Point S to the SBox we're using
-    S = SBox;
-  else
-    S = SBoxInverse;
-  for (var i = 0; i < 4; i++)           // Substitute for every byte in state
-    for (var j = 0; j < Nb; j++)
-       state[i][j] = S[state[i][j]];
+	var S;
+	if (direction == "encrypt")           // Point S to the SBox we're using
+		S = SBox;
+	else
+		S = SBoxInverse;
+	for (var i = 0; i < 4; i++)           // Substitute for every byte in state
+		for (var j = 0; j < Nb; j++)
+ 			state[i][j] = S[state[i][j]];
 }
 
 // Performs the row shifting step of the cipher.
 
 function shiftRow(state, direction) {
-  for (var i=1; i<4; i++)               // Row 0 never shifts
-    if (direction == "encrypt")
-       state[i] = cyclicShiftLeft(state[i], shiftOffsets[Nb][i]);
-    else
-       state[i] = cyclicShiftLeft(state[i], Nb - shiftOffsets[Nb][i]);
+	for (var i=1; i<4; i++)               // Row 0 never shifts
+		if (direction == "encrypt")
+ 			state[i] = cyclicShiftLeft(state[i], shiftOffsets[Nb][i]);
+		else
+ 			state[i] = cyclicShiftLeft(state[i], Nb - shiftOffsets[Nb][i]);
 
 }
 
@@ -1597,34 +1597,34 @@
 // to greatly increase the speed. 
 
 function mixColumn(state, direction) {
-  var b = [];                            // Result of matrix multiplications
-  for (var j = 0; j < Nb; j++) {         // Go through each column...
-    for (var i = 0; i < 4; i++) {        // and for each row in the column...
-      if (direction == "encrypt")
-        b[i] = mult_GF256(state[i][j], 2) ^          // perform mixing
-               mult_GF256(state[(i+1)%4][j], 3) ^ 
-               state[(i+2)%4][j] ^ 
-               state[(i+3)%4][j];
-      else 
-        b[i] = mult_GF256(state[i][j], 0xE) ^ 
-               mult_GF256(state[(i+1)%4][j], 0xB) ^
-               mult_GF256(state[(i+2)%4][j], 0xD) ^
-               mult_GF256(state[(i+3)%4][j], 9);
-    }
-    for (var i = 0; i < 4; i++)          // Place result back into column
-      state[i][j] = b[i];
-  }
+	var b = [];                            // Result of matrix multiplications
+	for (var j = 0; j < Nb; j++) {         // Go through each column...
+		for (var i = 0; i < 4; i++) {        // and for each row in the column...
+			if (direction == "encrypt")
+				b[i] = mult_GF256(state[i][j], 2) ^          // perform mixing
+ 							mult_GF256(state[(i+1)%4][j], 3) ^ 
+ 							state[(i+2)%4][j] ^ 
+ 							state[(i+3)%4][j];
+			else 
+				b[i] = mult_GF256(state[i][j], 0xE) ^ 
+ 							mult_GF256(state[(i+1)%4][j], 0xB) ^
+ 							mult_GF256(state[(i+2)%4][j], 0xD) ^
+ 							mult_GF256(state[(i+3)%4][j], 9);
+		}
+		for (var i = 0; i < 4; i++)          // Place result back into column
+			state[i][j] = b[i];
+	}
 }
 
 // Adds the current round key to the state information. Straightforward.
 
 function addRoundKey(state, roundKey) {
-  for (var j = 0; j < Nb; j++) {                 // Step through columns...
-    state[0][j] ^= (roundKey[j] & 0xFF);         // and XOR
-    state[1][j] ^= ((roundKey[j]>>8) & 0xFF);
-    state[2][j] ^= ((roundKey[j]>>16) & 0xFF);
-    state[3][j] ^= ((roundKey[j]>>24) & 0xFF);
-  }
+	for (var j = 0; j < Nb; j++) {                 // Step through columns...
+		state[0][j] ^= (roundKey[j] & 0xFF);         // and XOR
+		state[1][j] ^= ((roundKey[j]>>8) & 0xFF);
+		state[2][j] ^= ((roundKey[j]>>16) & 0xFF);
+		state[3][j] ^= ((roundKey[j]>>24) & 0xFF);
+	}
 }
 
 // This function creates the expanded key from the input (128/192/256-bit)
@@ -1633,63 +1633,63 @@
 // make up the expanded key.
 
 function keyExpansion(key) {
-  var expandedKey = new Array();
-  var temp;
+	var expandedKey = new Array();
+	var temp;
 
-  // in case the key size or parameters were changed...
-  Nk = keySizeInBits / 32;                   
-  Nb = blockSizeInBits / 32;
-  Nr = roundsArray[Nk][Nb];
+	// in case the key size or parameters were changed...
+	Nk = keySizeInBits / 32;                   
+	Nb = blockSizeInBits / 32;
+	Nr = roundsArray[Nk][Nb];
 
-  for (var j=0; j < Nk; j++)     // Fill in input key first
-    expandedKey[j] = 
-      (key[4*j]) | (key[4*j+1]<<8) | (key[4*j+2]<<16) | (key[4*j+3]<<24);
+	for (var j=0; j < Nk; j++)     // Fill in input key first
+		expandedKey[j] = 
+			(key[4*j]) | (key[4*j+1]<<8) | (key[4*j+2]<<16) | (key[4*j+3]<<24);
 
-  // Now walk down the rest of the array filling in expanded key bytes as
-  // per Rijndael's spec
-  for (j = Nk; j < Nb * (Nr + 1); j++) {    // For each word of expanded key
-    temp = expandedKey[j - 1];
-    if (j % Nk == 0) 
-      temp = ( (SBox[(temp>>8) & 0xFF]) |
-               (SBox[(temp>>16) & 0xFF]<<8) |
-               (SBox[(temp>>24) & 0xFF]<<16) |
-               (SBox[temp & 0xFF]<<24) ) ^ Rcon[Math.floor(j / Nk) - 1];
-    else if (Nk > 6 && j % Nk == 4)
-      temp = (SBox[(temp>>24) & 0xFF]<<24) |
-             (SBox[(temp>>16) & 0xFF]<<16) |
-             (SBox[(temp>>8) & 0xFF]<<8) |
-             (SBox[temp & 0xFF]);
-    expandedKey[j] = expandedKey[j-Nk] ^ temp;
-  }
-  return expandedKey;
+	// Now walk down the rest of the array filling in expanded key bytes as
+	// per Rijndael's spec
+	for (j = Nk; j < Nb * (Nr + 1); j++) {    // For each word of expanded key
+		temp = expandedKey[j - 1];
+		if (j % Nk == 0) 
+			temp = ( (SBox[(temp>>8) & 0xFF]) |
+ 							(SBox[(temp>>16) & 0xFF]<<8) |
+ 							(SBox[(temp>>24) & 0xFF]<<16) |
+ 							(SBox[temp & 0xFF]<<24) ) ^ Rcon[Math.floor(j / Nk) - 1];
+		else if (Nk > 6 && j % Nk == 4)
+			temp = (SBox[(temp>>24) & 0xFF]<<24) |
+ 						(SBox[(temp>>16) & 0xFF]<<16) |
+ 						(SBox[(temp>>8) & 0xFF]<<8) |
+ 						(SBox[temp & 0xFF]);
+		expandedKey[j] = expandedKey[j-Nk] ^ temp;
+	}
+	return expandedKey;
 }
 
 // Rijndael's round functions... 
 
 function Round(state, roundKey) {
-  byteSub(state, "encrypt");
-  shiftRow(state, "encrypt");
-  mixColumn(state, "encrypt");
-  addRoundKey(state, roundKey);
+	byteSub(state, "encrypt");
+	shiftRow(state, "encrypt");
+	mixColumn(state, "encrypt");
+	addRoundKey(state, roundKey);
 }
 
 function InverseRound(state, roundKey) {
-  addRoundKey(state, roundKey);
-  mixColumn(state, "decrypt");
-  shiftRow(state, "decrypt");
-  byteSub(state, "decrypt");
+	addRoundKey(state, roundKey);
+	mixColumn(state, "decrypt");
+	shiftRow(state, "decrypt");
+	byteSub(state, "decrypt");
 }
 
 function FinalRound(state, roundKey) {
-  byteSub(state, "encrypt");
-  shiftRow(state, "encrypt");
-  addRoundKey(state, roundKey);
+	byteSub(state, "encrypt");
+	shiftRow(state, "encrypt");
+	addRoundKey(state, roundKey);
 }
 
 function InverseFinalRound(state, roundKey){
-  addRoundKey(state, roundKey);
-  shiftRow(state, "decrypt");
-  byteSub(state, "decrypt");  
+	addRoundKey(state, roundKey);
+	shiftRow(state, "decrypt");
+	byteSub(state, "decrypt");  
 }
 
 // encrypt is the basic encryption function. It takes parameters
@@ -1698,18 +1698,18 @@
 // keyExpansion(). The ciphertext block is returned as an array of bytes.
 
 function encrypt(block, expandedKey) {
-  var i;  
-  if (!block || block.length*8 != blockSizeInBits)
-     return; 
-  if (!expandedKey)
-     return;
+	var i;  
+	if (!block || block.length*8 != blockSizeInBits)
+ 		return; 
+	if (!expandedKey)
+ 		return;
 
-  block = packBytes(block);
-  addRoundKey(block, expandedKey);
-  for (i=1; i<Nr; i++) 
-    Round(block, expandedKey.slice(Nb*i, Nb*(i+1)));
-  FinalRound(block, expandedKey.slice(Nb*Nr)); 
-  return unpackBytes(block);
+	block = packBytes(block);
+	addRoundKey(block, expandedKey);
+	for (i=1; i<Nr; i++) 
+		Round(block, expandedKey.slice(Nb*i, Nb*(i+1)));
+	FinalRound(block, expandedKey.slice(Nb*Nr)); 
+	return unpackBytes(block);
 }
 
 // decrypt is the basic decryption function. It takes parameters
@@ -1718,18 +1718,18 @@
 // keyExpansion(). The decrypted block is returned as an array of bytes.
 
 function decrypt(block, expandedKey) {
-  var i;
-  if (!block || block.length*8 != blockSizeInBits)
-     return;
-  if (!expandedKey)
-     return;
+	var i;
+	if (!block || block.length*8 != blockSizeInBits)
+ 		return;
+	if (!expandedKey)
+ 		return;
 
-  block = packBytes(block);
-  InverseFinalRound(block, expandedKey.slice(Nb*Nr)); 
-  for (i = Nr - 1; i>0; i--) 
-    InverseRound(block, expandedKey.slice(Nb*i, Nb*(i+1)));
-  addRoundKey(block, expandedKey);
-  return unpackBytes(block);
+	block = packBytes(block);
+	InverseFinalRound(block, expandedKey.slice(Nb*Nr)); 
+	for (i = Nr - 1; i>0; i--) 
+		InverseRound(block, expandedKey.slice(Nb*i, Nb*(i+1)));
+	addRoundKey(block, expandedKey);
+	return unpackBytes(block);
 }
 
 // This function packs an array of bytes into the four row form defined by
@@ -1738,19 +1738,19 @@
 // column 0, row 0 to 3). This function returns a 2d array.
 
 function packBytes(octets) {
-  var state = new Array();
-  if (!octets || octets.length % 4)
-    return;
+	var state = new Array();
+	if (!octets || octets.length % 4)
+		return;
 
-  state[0] = new Array();  state[1] = new Array(); 
-  state[2] = new Array();  state[3] = new Array();
-  for (var j=0; j<octets.length; j+= 4) {
-     state[0][j/4] = octets[j];
-     state[1][j/4] = octets[j+1];
-     state[2][j/4] = octets[j+2];
-     state[3][j/4] = octets[j+3];
-  }
-  return state;  
+	state[0] = new Array();  state[1] = new Array(); 
+	state[2] = new Array();  state[3] = new Array();
+	for (var j=0; j<octets.length; j+= 4) {
+ 		state[0][j/4] = octets[j];
+ 		state[1][j/4] = octets[j+1];
+ 		state[2][j/4] = octets[j+2];
+ 		state[3][j/4] = octets[j+3];
+	}
+	return state;  
 }
 
 // This function unpacks an array of bytes from the four row format preferred
@@ -1759,14 +1759,14 @@
 // This function returns a 1d array of bytes.
 
 function unpackBytes(packed) {
-  var result = new Array();
-  for (var j=0; j<packed[0].length; j++) {
-    result[result.length] = packed[0][j];
-    result[result.length] = packed[1][j];
-    result[result.length] = packed[2][j];
-    result[result.length] = packed[3][j];
-  }
-  return result;
+	var result = new Array();
+	for (var j=0; j<packed[0].length; j++) {
+		result[result.length] = packed[0][j];
+		result[result.length] = packed[1][j];
+		result[result.length] = packed[2][j];
+		result[result.length] = packed[3][j];
+	}
+	return result;
 }
 
 // This function takes a prospective plaintext (string or array of bytes)
@@ -1777,23 +1777,23 @@
 // chose to use the heuristic below. 
 
 function formatPlaintext(plaintext) {
-  var bpb = blockSizeInBits / 8;               // bytes per block
-  var i;
+	var bpb = blockSizeInBits / 8;               // bytes per block
+	var i;
 
-  // if primitive string or String instance
-  if (typeof plaintext == "string" || plaintext.split) {
-    // alert('AUUGH you idiot it\'s NOT A STRING ITS A '+typeof(plaintext)+'!!!');
-    // return false;
-    plaintext = plaintext.split("");
-    // Unicode issues here (ignoring high byte)
-    for (i=0; i<plaintext.length; i++)
-      plaintext[i] = plaintext[i].charCodeAt(0) & 0xFF;
-  } 
+	// if primitive string or String instance
+	if (typeof plaintext == "string" || plaintext.split) {
+		// alert('AUUGH you idiot it\'s NOT A STRING ITS A '+typeof(plaintext)+'!!!');
+		// return false;
+		plaintext = plaintext.split("");
+		// Unicode issues here (ignoring high byte)
+		for (i=0; i<plaintext.length; i++)
+			plaintext[i] = plaintext[i].charCodeAt(0) & 0xFF;
+	} 
 
-  for (i = bpb - (plaintext.length % bpb); i > 0 && i < bpb; i--) 
-    plaintext[plaintext.length] = 0;
-  
-  return plaintext;
+	for (i = bpb - (plaintext.length % bpb); i > 0 && i < bpb; i--) 
+		plaintext[plaintext.length] = 0;
+	
+	return plaintext;
 }
 
 // Returns an array containing "howMany" random bytes. YOU SHOULD CHANGE THIS
@@ -1801,11 +1801,11 @@
 // APPLICATION.
 
 function getRandomBytes(howMany) {
-  var i;
-  var bytes = new Array();
-  for (i=0; i<howMany; i++)
-    bytes[i] = Math.round(Math.random()*255);
-  return bytes;
+	var i;
+	var bytes = new Array();
+	for (i=0; i<howMany; i++)
+		bytes[i] = Math.round(Math.random()*255);
+	return bytes;
 }
 
 // rijndaelEncrypt(plaintext, key, mode)
@@ -1823,43 +1823,43 @@
 // something that returns truly random bits.
 
 function rijndaelEncrypt(plaintext, key, mode) {
-  var expandedKey, i, aBlock;
-  var bpb = blockSizeInBits / 8;          // bytes per block
-  var ct;                                 // ciphertext
+	var expandedKey, i, aBlock;
+	var bpb = blockSizeInBits / 8;          // bytes per block
+	var ct;                                 // ciphertext
 
-  if (typeof plaintext != 'object' || typeof key != 'object')
-  {
-    alert( 'Invalid params\nplaintext: '+typeof(plaintext)+'\nkey: '+typeof(key) );
-    return false;
-  }
-  if (key.length*8 == keySizeInBits+8)
-    key.length = keySizeInBits / 8;
-  if (key.length*8 != keySizeInBits)
-  {
-    alert( 'Key length is bad!\nLength: '+key.length+'\nExpected: '+keySizeInBits / 8 );
-    return false;
-  }
-  if (mode == "CBC")
-    ct = getRandomBytes(bpb);             // get IV
-  else {
-    mode = "ECB";
-    ct = new Array();
-  }
+	if (typeof plaintext != 'object' || typeof key != 'object')
+	{
+		alert( 'Invalid params\nplaintext: '+typeof(plaintext)+'\nkey: '+typeof(key) );
+		return false;
+	}
+	if (key.length*8 == keySizeInBits+8)
+		key.length = keySizeInBits / 8;
+	if (key.length*8 != keySizeInBits)
+	{
+		alert( 'Key length is bad!\nLength: '+key.length+'\nExpected: '+keySizeInBits / 8 );
+		return false;
+	}
+	if (mode == "CBC")
+		ct = getRandomBytes(bpb);             // get IV
+	else {
+		mode = "ECB";
+		ct = new Array();
+	}
 
-  // convert plaintext to byte array and pad with zeros if necessary. 
-  plaintext = formatPlaintext(plaintext);
+	// convert plaintext to byte array and pad with zeros if necessary. 
+	plaintext = formatPlaintext(plaintext);
 
-  expandedKey = keyExpansion(key);
-  
-  for (var block=0; block<plaintext.length / bpb; block++) {
-    aBlock = plaintext.slice(block*bpb, (block+1)*bpb);
-    if (mode == "CBC")
-      for (var i=0; i<bpb; i++) 
-        aBlock[i] ^= ct[block*bpb + i];
-    ct = ct.concat(encrypt(aBlock, expandedKey));
-  }
+	expandedKey = keyExpansion(key);
+	
+	for (var block=0; block<plaintext.length / bpb; block++) {
+		aBlock = plaintext.slice(block*bpb, (block+1)*bpb);
+		if (mode == "CBC")
+			for (var i=0; i<bpb; i++) 
+				aBlock[i] ^= ct[block*bpb + i];
+		ct = ct.concat(encrypt(aBlock, expandedKey));
+	}
 
-  return ct;
+	return ct;
 }
 
 // rijndaelDecrypt(ciphertext, key, mode)
@@ -1874,37 +1874,37 @@
 // to a string of characters, you can use byteArrayToString().
 
 function rijndaelDecrypt(ciphertext, key, mode) {
-  var expandedKey;
-  var bpb = blockSizeInBits / 8;          // bytes per block
-  var pt = new Array();                   // plaintext array
-  var aBlock;                             // a decrypted block
-  var block;                              // current block number
+	var expandedKey;
+	var bpb = blockSizeInBits / 8;          // bytes per block
+	var pt = new Array();                   // plaintext array
+	var aBlock;                             // a decrypted block
+	var block;                              // current block number
 
-  if (!ciphertext || !key || typeof ciphertext == "string")
-    return;
-  if (key.length*8 != keySizeInBits)
-    return; 
-  if (!mode)
-    mode = "ECB";                         // assume ECB if mode omitted
+	if (!ciphertext || !key || typeof ciphertext == "string")
+		return;
+	if (key.length*8 != keySizeInBits)
+		return; 
+	if (!mode)
+		mode = "ECB";                         // assume ECB if mode omitted
 
-  expandedKey = keyExpansion(key);
+	expandedKey = keyExpansion(key);
  
-  // work backwards to accomodate CBC mode 
-  for (block=(ciphertext.length / bpb)-1; block>0; block--) {
-    aBlock = 
-     decrypt(ciphertext.slice(block*bpb,(block+1)*bpb), expandedKey);
-    if (mode == "CBC") 
-      for (var i=0; i<bpb; i++) 
-        pt[(block-1)*bpb + i] = aBlock[i] ^ ciphertext[(block-1)*bpb + i];
-    else 
-      pt = aBlock.concat(pt);
-  }
+	// work backwards to accomodate CBC mode 
+	for (block=(ciphertext.length / bpb)-1; block>0; block--) {
+		aBlock = 
+ 		decrypt(ciphertext.slice(block*bpb,(block+1)*bpb), expandedKey);
+		if (mode == "CBC") 
+			for (var i=0; i<bpb; i++) 
+				pt[(block-1)*bpb + i] = aBlock[i] ^ ciphertext[(block-1)*bpb + i];
+		else 
+			pt = aBlock.concat(pt);
+	}
 
-  // do last block if ECB (skips the IV in CBC)
-  if (mode == "ECB")
-    pt = decrypt(ciphertext.slice(0, bpb), expandedKey).concat(pt);
+	// do last block if ECB (skips the IV in CBC)
+	if (mode == "ECB")
+		pt = decrypt(ciphertext.slice(0, bpb), expandedKey).concat(pt);
 
-  return pt;
+	return pt;
 }
 
 // This method takes a byte array (byteArray) and converts it to a string by
@@ -1916,11 +1916,11 @@
 // values. Roll your own function for more robust functionality :)
 
 function byteArrayToString(byteArray) {
-  var result = "";
-  for ( var i=0; i < byteArray.length; i++ )
-    if (byteArray[i] != 0) 
-      result += '%' + byteArray[i].toString(16);
-  return decodeURIComponent(result);
+	var result = "";
+	for ( var i=0; i < byteArray.length; i++ )
+		if (byteArray[i] != 0) 
+			result += '%' + byteArray[i].toString(16);
+	return decodeURIComponent(result);
 }
 
 // This function takes an array of bytes (byteArray) and converts them
@@ -1930,13 +1930,13 @@
 // string.
 
 function byteArrayToHex(byteArray) {
-  var result = "";
-  if (!byteArray)
-    return;
-  for (var i=0; i<byteArray.length; i++)
-    result += ((byteArray[i]<16) ? "0" : "") + byteArray[i].toString(16);
+	var result = "";
+	if (!byteArray)
+		return;
+	for (var i=0; i<byteArray.length; i++)
+		result += ((byteArray[i]<16) ? "0" : "") + byteArray[i].toString(16);
 
-  return result;
+	return result;
 }
 
 // This function converts a string containing hexadecimal digits to an 
@@ -1945,84 +1945,84 @@
 // function returns an array. 
 
 function hexToByteArray(hexString) {
-  /*
-  var byteArray = [];
-  if (hexString.length % 2)             // must have even length
-    return;
-  if (hexString.indexOf("0x") == 0 || hexString.indexOf("0X") == 0)
-    hexString = hexString.substring(2);
-  for (var i = 0; i<hexString.length; i += 2) 
-    byteArray[Math.floor(i/2)] = parseInt(hexString.slice(i, i+2), 16);
-  return byteArray;
-  */
-  var bytes = new Array();
-  hexString = str_split(hexString, 2);
-  //alert(hexString.toString());
-  //return false;
-  for( var i in hexString )
-  {
-    bytes[bytes.length] = parseInt(hexString[i], 16);
-  }
-  //alert(bytes.toString());
-  return bytes;
+	/*
+	var byteArray = [];
+	if (hexString.length % 2)             // must have even length
+		return;
+	if (hexString.indexOf("0x") == 0 || hexString.indexOf("0X") == 0)
+		hexString = hexString.substring(2);
+	for (var i = 0; i<hexString.length; i += 2) 
+		byteArray[Math.floor(i/2)] = parseInt(hexString.slice(i, i+2), 16);
+	return byteArray;
+	*/
+	var bytes = new Array();
+	hexString = str_split(hexString, 2);
+	//alert(hexString.toString());
+	//return false;
+	for( var i in hexString )
+	{
+		bytes[bytes.length] = parseInt(hexString[i], 16);
+	}
+	//alert(bytes.toString());
+	return bytes;
 }
 
 function stringToByteArray(text)
 {
-  // Modified for Enano 2009-02-16 to be Unicode-safe
-  var result = new Array();
-  text = encodeURIComponent(text);
-  for ( var i = 0; i < text.length; i++ )
-  {
-    var ch = text.charCodeAt(i);
-    var a = false;
-    if ( ch == 37 ) // "%"
-    {
-      var hexch = text.substr(i, 3);
-      if ( hexch.match(/^%[a-f0-9][a-f0-9]$/i) )
-      {
-        result[result.length] = (unescape(hexch)).charCodeAt(0);
-        a = true;
-        i += 2;
-      }
-    }
-    if ( !a )
-    {
-      result[result.length] = ch;
-    }
-  }
-  return result;
+	// Modified for Enano 2009-02-16 to be Unicode-safe
+	var result = new Array();
+	text = encodeURIComponent(text);
+	for ( var i = 0; i < text.length; i++ )
+	{
+		var ch = text.charCodeAt(i);
+		var a = false;
+		if ( ch == 37 ) // "%"
+		{
+			var hexch = text.substr(i, 3);
+			if ( hexch.match(/^%[a-f0-9][a-f0-9]$/i) )
+			{
+				result[result.length] = (unescape(hexch)).charCodeAt(0);
+				a = true;
+				i += 2;
+			}
+		}
+		if ( !a )
+		{
+			result[result.length] = ch;
+		}
+	}
+	return result;
 }
 
 function aes_self_test()
 {
-  //
-  // Encryption test
-  //
-  
-  var str = '';
-  for(i=0;i<keySizeInBits/4;i++)
-  {
-    str+='0';
-  }
-  str = hexToByteArray(str);
-  var ct  = rijndaelEncrypt(str, str, 'ECB');
-  ct      = byteArrayToHex(ct);
-  var v;
-  switch(keySizeInBits)
-  {
-    // These test vectors are for 128-bit block size.
-    case 128:
-      v = '66e94bd4ef8a2c3b884cfa59ca342b2e';
-      break;
-    case 192:
-      v = 'aae06992acbf52a3e8f4a96ec9300bd7aae06992acbf52a3e8f4a96ec9300bd7';
-      break;
-    case 256:
-      v = 'dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087';
-      break;
-  }
-  return ( ct == v && md5_vm_test() );
+	//
+	// Encryption test
+	//
+	
+	var str = '';
+	for(i=0;i<keySizeInBits/4;i++)
+	{
+		str+='0';
+	}
+	str = hexToByteArray(str);
+	var ct  = rijndaelEncrypt(str, str, 'ECB');
+	ct      = byteArrayToHex(ct);
+	var v;
+	switch(keySizeInBits)
+	{
+		// These test vectors are for 128-bit block size.
+		case 128:
+			v = '66e94bd4ef8a2c3b884cfa59ca342b2e';
+			break;
+		case 192:
+			v = 'aae06992acbf52a3e8f4a96ec9300bd7aae06992acbf52a3e8f4a96ec9300bd7';
+			break;
+		case 256:
+			v = 'dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087';
+			break;
+	}
+	return ( ct == v && md5_vm_test() );
 }
 
 /*
@@ -2035,21 +2035,21 @@
 // EnanoMath layer: Leemon (frontend to BigInt library by Leemon Baird)
 
 EnanoMathLayers.Leemon = {
-  Base: 10,
-  PowMod: function(a, b, c)
-  {
-    a = str2bigInt(a, this.Base);
-    b = str2bigInt(b, this.Base);
-    c = str2bigInt(c, this.Base);
-    var result = powMod(a, b, c);
-    result = bigInt2str(result, this.Base);
-    return result;
-  },
-  RandomInt: function(bits)
-  {
-    var result = randBigInt(bits);
-    return bigInt2str(result, this.Base);
-  }
+	Base: 10,
+	PowMod: function(a, b, c)
+	{
+		a = str2bigInt(a, this.Base);
+		b = str2bigInt(b, this.Base);
+		c = str2bigInt(c, this.Base);
+		var result = powMod(a, b, c);
+		result = bigInt2str(result, this.Base);
+		return result;
+	},
+	RandomInt: function(bits)
+	{
+		var result = randBigInt(bits);
+		return bigInt2str(result, this.Base);
+	}
 }
 
 var EnanoMath = EnanoMathLayers.Leemon;
@@ -2072,7 +2072,7 @@
 
 function dh_gen_private()
 {
-  return EnanoMath.RandomInt(256);
+	return EnanoMath.RandomInt(256);
 }
 
 /**
@@ -2083,7 +2083,7 @@
 
 function dh_gen_public(b)
 {
-  return EnanoMath.PowMod(dh_g, b, dh_prime);
+	return EnanoMath.PowMod(dh_g, b, dh_prime);
 }
 
 /**
@@ -2095,7 +2095,7 @@
 
 function dh_gen_shared_secret(b, A)
 {
-  return EnanoMath.PowMod(A, b, dh_prime);
+	return EnanoMath.PowMod(A, b, dh_prime);
 }
 
 /* A JavaScript implementation of the Secure Hash Algorithm, SHA-256
@@ -2111,13 +2111,13 @@
 are permitted provided that the following conditions are met:
 
  * Redistributions of source code must retain the above copyright notice, this
-   list of conditions and the following disclaimer.
+ 	list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
+ 	this list of conditions and the following disclaimer in the documentation
+ 	and/or other materials provided with the distribution.
  * Neither the name of the <ORGANIZATION> nor the names of its contributors may
-   be used to endorse or promote products derived from this software without
-   specific prior written permission.
+ 	be used to endorse or promote products derived from this software without
+ 	specific prior written permission.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -2132,9 +2132,9 @@
 */
 var chrsz = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode  */
 function safe_add (x, y) {
-  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
-  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
-  return (msw << 16) | (lsw & 0xFFFF);
+	var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+	var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+	return (msw << 16) | (lsw & 0xFFFF);
 }
 function S (X, n) {return ( X >>> n ) | (X << (32 - n));}
 function R (X, n) {return ( X >>> n );}
@@ -2145,42 +2145,42 @@
 function Gamma0256(x) {return (S(x, 7) ^ S(x, 18) ^ R(x, 3));}
 function Gamma1256(x) {return (S(x, 17) ^ S(x, 19) ^ R(x, 10));}
 function core_sha256 (m, l) {
-    var K = new Array(0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2);
-    var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
-    var W = new Array(64);
-    var a, b, c, d, e, f, g, h, i, j;
-    var T1, T2;
-    /* append padding */
-    m[l >> 5] |= 0x80 << (24 - l % 32);
-    m[((l + 64 >> 9) << 4) + 15] = l;
-    for ( var i = 0; i<m.length; i+=16 ) {
-        a = HASH[0]; b = HASH[1]; c = HASH[2]; d = HASH[3]; e = HASH[4]; f = HASH[5]; g = HASH[6]; h = HASH[7];
-        for ( var j = 0; j<64; j++) {
-            if (j < 16) W[j] = m[j + i];
-            else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
-            T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
-            T2 = safe_add(Sigma0256(a), Maj(a, b, c));
-            h = g; g = f; f = e; e = safe_add(d, T1); d = c; c = b; b = a; a = safe_add(T1, T2);
-        }
-        HASH[0] = safe_add(a, HASH[0]); HASH[1] = safe_add(b, HASH[1]); HASH[2] = safe_add(c, HASH[2]); HASH[3] = safe_add(d, HASH[3]); HASH[4] = safe_add(e, HASH[4]); HASH[5] = safe_add(f, HASH[5]); HASH[6] = safe_add(g, HASH[6]); HASH[7] = safe_add(h, HASH[7]);
-    }
-    return HASH;
+		var K = new Array(0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2);
+		var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
+		var W = new Array(64);
+		var a, b, c, d, e, f, g, h, i, j;
+		var T1, T2;
+		/* append padding */
+		m[l >> 5] |= 0x80 << (24 - l % 32);
+		m[((l + 64 >> 9) << 4) + 15] = l;
+		for ( var i = 0; i<m.length; i+=16 ) {
+				a = HASH[0]; b = HASH[1]; c = HASH[2]; d = HASH[3]; e = HASH[4]; f = HASH[5]; g = HASH[6]; h = HASH[7];
+				for ( var j = 0; j<64; j++) {
+						if (j < 16) W[j] = m[j + i];
+						else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
+						T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
+						T2 = safe_add(Sigma0256(a), Maj(a, b, c));
+						h = g; g = f; f = e; e = safe_add(d, T1); d = c; c = b; b = a; a = safe_add(T1, T2);
+				}
+				HASH[0] = safe_add(a, HASH[0]); HASH[1] = safe_add(b, HASH[1]); HASH[2] = safe_add(c, HASH[2]); HASH[3] = safe_add(d, HASH[3]); HASH[4] = safe_add(e, HASH[4]); HASH[5] = safe_add(f, HASH[5]); HASH[6] = safe_add(g, HASH[6]); HASH[7] = safe_add(h, HASH[7]);
+		}
+		return HASH;
 }
 function str2binb (str) {
-  var bin = Array();
-  var mask = (1 << chrsz) - 1;
-  for(var i = 0; i < str.length * chrsz; i += chrsz)
-    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
-  return bin;
+	var bin = Array();
+	var mask = (1 << chrsz) - 1;
+	for(var i = 0; i < str.length * chrsz; i += chrsz)
+		bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
+	return bin;
 }
 function binb2hex (binarray) {
-  var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
-  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
-  var str = "";
-  for (var i = 0; i < binarray.length * 4; i++) {
-    str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
-  }
-  return str;
+	var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
+	var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+	var str = "";
+	for (var i = 0; i < binarray.length * 4; i++) {
+		str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
+	}
+	return str;
 }
 function hex_sha256(s){return binb2hex(core_sha256(str2binb(s),s.length * chrsz));}
 
@@ -2196,13 +2196,13 @@
 function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
 function md5_vm_test() { return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; }
 function core_md5(x, len) { x[len >> 5] |= 0x80 << ((len) % 32); x[(((len + 64) >>> 9) << 4) + 14] = len; var a =  1732584193; var b = -271733879; var c = -1732584194; var d =  271733878; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
-         a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);c = md5_ff(c, d, a, b, x[i+10], 17, -42063);b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
-         c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
-         a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
-         c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
-         a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
-         c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
-         a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return Array(a, b, c, d); }
+ 				a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);c = md5_ff(c, d, a, b, x[i+10], 17, -42063);b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+ 				c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+ 				a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+ 				c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+ 				a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+ 				c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+ 				a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return Array(a, b, c, d); }
 function md5_cmn(q, a, b, x, s, t) { return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); }
 function md5_ff(a, b, c, d, x, s, t) { return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); }
 function md5_gg(a, b, c, d, x, s, t) { return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); }
--- a/includes/clientside/static/dropdown.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/dropdown.js	Sun Mar 28 23:10:46 2010 -0400
@@ -34,221 +34,221 @@
 // Adds the jBox CSS to the HTML header. Called on window onload.
 var jBoxInit = function()
 {
-  setTimeout('jBoxBatchSetup();', 200);
+	setTimeout('jBoxBatchSetup();', 200);
 }
 addOnloadHook(jBoxInit);
 
 // Initializes each menu.
 function jBoxBatchSetup()
 {
-  if ( KILL_SWITCH )
-    return false;
-  var menus = document.getElementsByClassName('div', 'menu_nojs');
-  if ( menus.length > 0 )
-  {
-    for ( var i in menus )
-    {
-      if ( typeof(menus[i]) != 'object')
-        continue; // toJSONString() compatibility
-      jBoxSetup(menus[i]);
-    }
-  }
+	if ( KILL_SWITCH )
+		return false;
+	var menus = document.getElementsByClassName('div', 'menu_nojs');
+	if ( menus.length > 0 )
+	{
+		for ( var i in menus )
+		{
+			if ( typeof(menus[i]) != 'object')
+				continue; // toJSONString() compatibility
+			jBoxSetup(menus[i]);
+		}
+	}
 }
 
 // Initializes a div with a jBox menu in it.
 function jBoxSetup(obj)
 {
-  $dynano(obj).addClass('menu');
-  removeTextNodes(obj);
-  
-  for ( var i = 0; i < obj.childNodes.length; i++ )
-  {
-    /* normally this would be done in about 2 lines of code, but javascript is so picky..... */
-    if ( obj.childNodes[i] )
-    {
-      if ( obj.childNodes[i].tagName )
-      {
-        if ( obj.childNodes[i].tagName == 'A' )
-        {
-          // if ( is_Safari ) alert('It\'s an A: '+obj);
-          if ( obj.childNodes[i].nextSibling )
-          {
-            // alert("Next sibling: " + obj.childNodes[i].nextSibling);
-            if ( obj.childNodes[i].nextSibling.tagName )
-            {
-              if ( obj.childNodes[i].nextSibling.tagName == 'UL' || ( obj.childNodes[i].nextSibling.tagName.toLowerCase() == 'div' && obj.childNodes[i].nextSibling.className == 'submenu' ) )
-              {
-                // Calculate height
-                var ul = obj.childNodes[i].nextSibling;
-                domObjChangeOpac(0, ul);
-                ul.style.display = 'block';
-                ul.style.zIndex = getHighestZ() + 2;
-                var links = ul.getElementsByTagName('a');
-                for ( var j = 0; j < links.length; j++ )
-                {
-                  links[j].onmouseup = function()
-                  {
-                    var ul = this;
-                    while ( ul.tagName != 'UL' && ul.tagName != 'DIV' && ul.tagName != 'BODY' )
-                      ul = ul.parentNode;
-                    if ( ul.tagName == 'BODY' )
-                      return false;
-                    jBoxHideMenu(ul.previousSibling, ul);
-                  }
-                }
-                var dim = fetch_dimensions(ul);
-                if ( !ul.id )
-                  ul.id = 'jBoxmenuobj_' + Math.floor(Math.random() * 10000000);
-                jBoxMenuHeights[ul.id] = parseInt(dim['h']) - 2; // subtract 2px for border width
-                
-                if ( dim['w'] + $dynano(ul).Left() > getWidth() || $dynano(ul).hasClass('jbox_right') )
-                {
-                  $dynano(ul).addClass('jbox_right');
-                  ul.jbox_width = $dynano(ul).Width();
-                }
-                
-                ul.style.display = 'none';
-                domObjChangeOpac(100, ul);
-                
-                // Setup events
-                obj.childNodes[i].onmouseover = function()  { jBoxOverHandler(this); };
-                obj.childNodes[i].onmouseout = function(e)  { jBoxOutHandler(this, e); };
-                obj.childNodes[i].nextSibling.onmouseout = function(e)  { jBoxOutHandler(this, e); };
-                if ( is_iPhone )
-                {
-                  obj.childNodes[i].onclick = function()  { jBoxOverHandler(this); return false; };
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-  }
+	$dynano(obj).addClass('menu');
+	removeTextNodes(obj);
+	
+	for ( var i = 0; i < obj.childNodes.length; i++ )
+	{
+		/* normally this would be done in about 2 lines of code, but javascript is so picky..... */
+		if ( obj.childNodes[i] )
+		{
+			if ( obj.childNodes[i].tagName )
+			{
+				if ( obj.childNodes[i].tagName == 'A' )
+				{
+					// if ( is_Safari ) alert('It\'s an A: '+obj);
+					if ( obj.childNodes[i].nextSibling )
+					{
+						// alert("Next sibling: " + obj.childNodes[i].nextSibling);
+						if ( obj.childNodes[i].nextSibling.tagName )
+						{
+							if ( obj.childNodes[i].nextSibling.tagName == 'UL' || ( obj.childNodes[i].nextSibling.tagName.toLowerCase() == 'div' && obj.childNodes[i].nextSibling.className == 'submenu' ) )
+							{
+								// Calculate height
+								var ul = obj.childNodes[i].nextSibling;
+								domObjChangeOpac(0, ul);
+								ul.style.display = 'block';
+								ul.style.zIndex = getHighestZ() + 2;
+								var links = ul.getElementsByTagName('a');
+								for ( var j = 0; j < links.length; j++ )
+								{
+									links[j].onmouseup = function()
+									{
+										var ul = this;
+										while ( ul.tagName != 'UL' && ul.tagName != 'DIV' && ul.tagName != 'BODY' )
+											ul = ul.parentNode;
+										if ( ul.tagName == 'BODY' )
+											return false;
+										jBoxHideMenu(ul.previousSibling, ul);
+									}
+								}
+								var dim = fetch_dimensions(ul);
+								if ( !ul.id )
+									ul.id = 'jBoxmenuobj_' + Math.floor(Math.random() * 10000000);
+								jBoxMenuHeights[ul.id] = parseInt(dim['h']) - 2; // subtract 2px for border width
+								
+								if ( dim['w'] + $dynano(ul).Left() > getWidth() || $dynano(ul).hasClass('jbox_right') )
+								{
+									$dynano(ul).addClass('jbox_right');
+									ul.jbox_width = $dynano(ul).Width();
+								}
+								
+								ul.style.display = 'none';
+								domObjChangeOpac(100, ul);
+								
+								// Setup events
+								obj.childNodes[i].onmouseover = function()  { jBoxOverHandler(this); };
+								obj.childNodes[i].onmouseout = function(e)  { jBoxOutHandler(this, e); };
+								obj.childNodes[i].nextSibling.onmouseout = function(e)  { jBoxOutHandler(this, e); };
+								if ( is_iPhone )
+								{
+									obj.childNodes[i].onclick = function()  { jBoxOverHandler(this); return false; };
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
 }
 
 // Called when user hovers mouse over a submenu
 function jBoxOverHandler(obj)
 {
-  // if ( is_Safari )
-  //   alert('Safari and over');
-  // Random ID used to track the object to perform on
-  var seed = Math.floor(Math.random() * 1000000);
-  jBoxObjCache[seed] = obj;
-  
-  // Sleep for a (little more than a tenth of a) second to see if the user really wants the menu to expand
-  setTimeout('if(isOverObj(jBoxObjCache['+seed+'], false, false)) jBoxOverHandlerBin(jBoxObjCache['+seed+']);', 150);
+	// if ( is_Safari )
+	//   alert('Safari and over');
+	// Random ID used to track the object to perform on
+	var seed = Math.floor(Math.random() * 1000000);
+	jBoxObjCache[seed] = obj;
+	
+	// Sleep for a (little more than a tenth of a) second to see if the user really wants the menu to expand
+	setTimeout('if(isOverObj(jBoxObjCache['+seed+'], false, false)) jBoxOverHandlerBin(jBoxObjCache['+seed+']);', 150);
 }
 
 // Displays a menu.
 function jBoxOverHandlerBin(obj)
 {
-  var others = obj.parentNode.getElementsByTagName('ul');
-  for ( var i in others )
-  {
-    if(typeof(others[i]) == 'object')
-    {
-      others[i].style.display = 'none';
-      $dynano(others[i].previousSibling).rmClass('liteselected');
-    }
-  }
-  var others = obj.parentNode.getElementsByTagName('div');
-  for ( var i in others )
-  {
-    if(typeof(others[i]) == 'object')
-    {
-      if ( others[i].className == 'submenu' )
-      {
-        others[i].style.display = 'none';
-        $dynano(others[i].previousSibling).rmClass('liteselected');
-      }
-    }
-  }
-  if(obj.nextSibling.tagName.toLowerCase() == 'ul' || ( obj.nextSibling.tagName.toLowerCase() == 'div' && obj.nextSibling.className == 'submenu' ))
-  {
-    $dynano(obj).addClass('liteselected');
-    //obj.className = 'liteselected';
-    var ul = obj.nextSibling;
-    var dim = fetch_dimensions(obj);
-    var off = fetch_offset(obj);
-    var dimh = parseInt(dim['h']);
-    var offtop = parseInt(off['top']);
-    var top = dimh + offtop;
-    if ( $dynano(ul).hasClass('jbox_right') )
-    {
-      left = $dynano(obj).Left() + $dynano(obj).Width() - ul.jbox_width; // ( link left + link width ) - ul width
-    }
-    else
-    {
-      left = off['left'];
-    }
-    if ( jBox_slide_enable )
-    {
-      domObjChangeOpac(0, ul);
-    }
-    ul.style.left = left + 'px';
-    ul.style.top = top + 'px';
-    ul.style.clip = 'rect(auto,auto,auto,auto)';
-    ul.style.overflow = 'visible';
-    ul.style.display = 'block';
-    if ( jBox_slide_enable )
-    {
-      slideOut(ul);
-    }
-    else
-    {
-      domObjChangeOpac(100, ul);
-    }
-  }
+	var others = obj.parentNode.getElementsByTagName('ul');
+	for ( var i in others )
+	{
+		if(typeof(others[i]) == 'object')
+		{
+			others[i].style.display = 'none';
+			$dynano(others[i].previousSibling).rmClass('liteselected');
+		}
+	}
+	var others = obj.parentNode.getElementsByTagName('div');
+	for ( var i in others )
+	{
+		if(typeof(others[i]) == 'object')
+		{
+			if ( others[i].className == 'submenu' )
+			{
+				others[i].style.display = 'none';
+				$dynano(others[i].previousSibling).rmClass('liteselected');
+			}
+		}
+	}
+	if(obj.nextSibling.tagName.toLowerCase() == 'ul' || ( obj.nextSibling.tagName.toLowerCase() == 'div' && obj.nextSibling.className == 'submenu' ))
+	{
+		$dynano(obj).addClass('liteselected');
+		//obj.className = 'liteselected';
+		var ul = obj.nextSibling;
+		var dim = fetch_dimensions(obj);
+		var off = fetch_offset(obj);
+		var dimh = parseInt(dim['h']);
+		var offtop = parseInt(off['top']);
+		var top = dimh + offtop;
+		if ( $dynano(ul).hasClass('jbox_right') )
+		{
+			left = $dynano(obj).Left() + $dynano(obj).Width() - ul.jbox_width; // ( link left + link width ) - ul width
+		}
+		else
+		{
+			left = off['left'];
+		}
+		if ( jBox_slide_enable )
+		{
+			domObjChangeOpac(0, ul);
+		}
+		ul.style.left = left + 'px';
+		ul.style.top = top + 'px';
+		ul.style.clip = 'rect(auto,auto,auto,auto)';
+		ul.style.overflow = 'visible';
+		ul.style.display = 'block';
+		if ( jBox_slide_enable )
+		{
+			slideOut(ul);
+		}
+		else
+		{
+			domObjChangeOpac(100, ul);
+		}
+	}
 }
 
 function jBoxOutHandler(obj, event)
 {
-  var seed = Math.floor(Math.random() * 1000000);
-  var seed2 = Math.floor(Math.random() * 1000000);
-  jBoxObjCache[seed] = obj;
-  jBoxObjCache[seed2] = event;
-  setTimeout('jBoxOutHandlerBin(jBoxObjCache['+seed+'], jBoxObjCache['+seed2+']);', 750);
+	var seed = Math.floor(Math.random() * 1000000);
+	var seed2 = Math.floor(Math.random() * 1000000);
+	jBoxObjCache[seed] = obj;
+	jBoxObjCache[seed2] = event;
+	setTimeout('jBoxOutHandlerBin(jBoxObjCache['+seed+'], jBoxObjCache['+seed2+']);', 750);
 }
 
 function jBoxOutHandlerBin(obj, event)
 {
-  var caller = obj.tagName.toLowerCase();
-  if(caller == 'a')
-  {
-    a = obj;
-    ul = obj.nextSibling;
-  }
-  else if(caller == 'ul' || caller == 'div')
-  {
-    a = obj.previousSibling;
-    ul = obj;
-  }
-  else
-  {
-    return false;
-  }
-  
-  if (!isOverObj(a, false, event) && !isOverObj(ul, true, event))
-  {
-    jBoxHideMenu(a, ul);
-  }
-  
-  return true;
+	var caller = obj.tagName.toLowerCase();
+	if(caller == 'a')
+	{
+		a = obj;
+		ul = obj.nextSibling;
+	}
+	else if(caller == 'ul' || caller == 'div')
+	{
+		a = obj.previousSibling;
+		ul = obj;
+	}
+	else
+	{
+		return false;
+	}
+	
+	if (!isOverObj(a, false, event) && !isOverObj(ul, true, event))
+	{
+		jBoxHideMenu(a, ul);
+	}
+	
+	return true;
 }
 
 function jBoxHideMenu(a, ul)
 {
-  $dynano(a).rmClass('liteselected');
-    
-  if ( jBox_slide_enable )
-  {
-    slideIn(ul);
-  }
-  else
-  {
-    ul.style.display = 'none';
-  }
+	$dynano(a).rmClass('liteselected');
+		
+	if ( jBox_slide_enable )
+	{
+		slideIn(ul);
+	}
+	else
+	{
+		ul.style.display = 'none';
+	}
 }
 
 // Slide an element downwards until it is at full height.
@@ -258,306 +258,306 @@
 
 function slideOut(obj)
 {
-  if ( jBoxSlideBlocker[obj.id] )
-    return false;
-  
-  jBoxSlideBlocker[obj.id] = true;
-  
-  if ( slide_speed == -1 )
-  {
-    obj.style.display = 'block';
-    return false;
-  }
-  
-  var currentheight = 0;
-  var targetheight = jBoxMenuHeights[obj.id];
-  var inertiabase = inertia_base;
-  var inertiainc = inertia_inc;
-  slideStep(obj, 0);
-  domObjChangeOpac(100, obj);
-  obj.style.overflow = 'hidden';
-  
-  // Don't edit past here
-  var timercnt = 0;
-  
-  var seed = Math.floor(Math.random() * 1000000);
-  sliderobj[seed] = obj;
-  
-  var framecnt = 0;
-  
-  while(true)
-  {
-    framecnt++;
-    timercnt += ( 100 - slide_speed );
-    inertiabase += inertiainc;
-    currentheight += inertiabase;
-    if ( currentheight > targetheight )
-      currentheight = targetheight;
-    setTimeout('slideStep(sliderobj['+seed+'], '+currentheight+', '+targetheight+');', timercnt);
-    if ( currentheight >= targetheight )
-      break;
-  }
-  timercnt = timercnt + ( 100 - slide_speed );
-  setTimeout('jBoxSlideBlocker[sliderobj['+seed+'].id] = false;', timercnt);
-  var opacstep = jBox_opacity / framecnt;
-  var opac = 0;
-  var timerstep = 0;
-  domObjChangeOpac(0, obj);
-  while(true)
-  {
-    timerstep += ( 100 - slide_speed );
-    opac += opacstep;
-    setTimeout('domObjChangeOpac('+opac+', sliderobj['+seed+']);', timerstep);
-    if ( opac >= jBox_opacity )
-      break;
-  }
+	if ( jBoxSlideBlocker[obj.id] )
+		return false;
+	
+	jBoxSlideBlocker[obj.id] = true;
+	
+	if ( slide_speed == -1 )
+	{
+		obj.style.display = 'block';
+		return false;
+	}
+	
+	var currentheight = 0;
+	var targetheight = jBoxMenuHeights[obj.id];
+	var inertiabase = inertia_base;
+	var inertiainc = inertia_inc;
+	slideStep(obj, 0);
+	domObjChangeOpac(100, obj);
+	obj.style.overflow = 'hidden';
+	
+	// Don't edit past here
+	var timercnt = 0;
+	
+	var seed = Math.floor(Math.random() * 1000000);
+	sliderobj[seed] = obj;
+	
+	var framecnt = 0;
+	
+	while(true)
+	{
+		framecnt++;
+		timercnt += ( 100 - slide_speed );
+		inertiabase += inertiainc;
+		currentheight += inertiabase;
+		if ( currentheight > targetheight )
+			currentheight = targetheight;
+		setTimeout('slideStep(sliderobj['+seed+'], '+currentheight+', '+targetheight+');', timercnt);
+		if ( currentheight >= targetheight )
+			break;
+	}
+	timercnt = timercnt + ( 100 - slide_speed );
+	setTimeout('jBoxSlideBlocker[sliderobj['+seed+'].id] = false;', timercnt);
+	var opacstep = jBox_opacity / framecnt;
+	var opac = 0;
+	var timerstep = 0;
+	domObjChangeOpac(0, obj);
+	while(true)
+	{
+		timerstep += ( 100 - slide_speed );
+		opac += opacstep;
+		setTimeout('domObjChangeOpac('+opac+', sliderobj['+seed+']);', timerstep);
+		if ( opac >= jBox_opacity )
+			break;
+	}
 }
 
 function slideIn(obj)
 {
-  if ( obj.style.display != 'block' )
-    return false;
-  
-  if ( jBoxSlideBlocker[obj.id] )
-    return false;
-  
-  jBoxSlideBlocker[obj.id] = true;
-  
-  var targetheight = 0;
-  var dim = fetch_dimensions(obj);
-  var currentheight = jBoxMenuHeights[obj.id];
-  var origheight = currentheight;
-  var inertiabase = inertia_base;
-  var inertiainc = inertia_inc;
-  domObjChangeOpac(100, obj);
-  obj.style.overflow = 'hidden';
-  
-  // Don't edit past here
-  var timercnt = 0;
-  
-  var seed = Math.floor(Math.random() * 1000000);
-  sliderobj[seed] = obj;
-  
-  var framecnt = 0;
-  
-  for(var j = 0;j<100;j++) // while(true)
-  {
-    framecnt++;
-    timercnt = timercnt + ( 100 - slide_speed );
-    inertiabase = inertiabase + inertiainc;
-    currentheight = currentheight - inertiabase;
-    if ( currentheight < targetheight )
-      currentheight = targetheight;
-    setTimeout('slideStep(sliderobj['+seed+'], '+currentheight+');', timercnt);
-    if ( currentheight <= targetheight )
-      break;
-  }
-  timercnt += ( 100 - slide_speed );
-  setTimeout('sliderobj['+seed+'].style.display="none";sliderobj['+seed+'].style.height="'+origheight+'px";jBoxSlideBlocker[sliderobj['+seed+'].id] = false;', timercnt);
-  
-  var opacstep = jBox_opacity / framecnt;
-  var opac = jBox_opacity;
-  var timerstep = 0;
-  domObjChangeOpac(100, obj);
-  while(true)
-  {
-    timerstep += ( 100 - slide_speed );
-    opac -= opacstep;
-    setTimeout('domObjChangeOpac('+opac+', sliderobj['+seed+']);', timerstep);
-    if ( opac <= 0 )
-      break;
-  }
-  
+	if ( obj.style.display != 'block' )
+		return false;
+	
+	if ( jBoxSlideBlocker[obj.id] )
+		return false;
+	
+	jBoxSlideBlocker[obj.id] = true;
+	
+	var targetheight = 0;
+	var dim = fetch_dimensions(obj);
+	var currentheight = jBoxMenuHeights[obj.id];
+	var origheight = currentheight;
+	var inertiabase = inertia_base;
+	var inertiainc = inertia_inc;
+	domObjChangeOpac(100, obj);
+	obj.style.overflow = 'hidden';
+	
+	// Don't edit past here
+	var timercnt = 0;
+	
+	var seed = Math.floor(Math.random() * 1000000);
+	sliderobj[seed] = obj;
+	
+	var framecnt = 0;
+	
+	for(var j = 0;j<100;j++) // while(true)
+	{
+		framecnt++;
+		timercnt = timercnt + ( 100 - slide_speed );
+		inertiabase = inertiabase + inertiainc;
+		currentheight = currentheight - inertiabase;
+		if ( currentheight < targetheight )
+			currentheight = targetheight;
+		setTimeout('slideStep(sliderobj['+seed+'], '+currentheight+');', timercnt);
+		if ( currentheight <= targetheight )
+			break;
+	}
+	timercnt += ( 100 - slide_speed );
+	setTimeout('sliderobj['+seed+'].style.display="none";sliderobj['+seed+'].style.height="'+origheight+'px";jBoxSlideBlocker[sliderobj['+seed+'].id] = false;', timercnt);
+	
+	var opacstep = jBox_opacity / framecnt;
+	var opac = jBox_opacity;
+	var timerstep = 0;
+	domObjChangeOpac(100, obj);
+	while(true)
+	{
+		timerstep += ( 100 - slide_speed );
+		opac -= opacstep;
+		setTimeout('domObjChangeOpac('+opac+', sliderobj['+seed+']);', timerstep);
+		if ( opac <= 0 )
+			break;
+	}
+	
 }
 
 function slideStep(obj, height, maxheight)
 {
-  obj.style.height = height + 'px';
-  //obj.style.clip = 'rect(3px,auto,'+maxheight+'px,auto)';
-  obj.style.overflow = 'hidden';
-  //obj.style.clip = 'rect('+height+'px,0px,'+maxheight+'px,auto);';
+	obj.style.height = height + 'px';
+	//obj.style.clip = 'rect(3px,auto,'+maxheight+'px,auto)';
+	obj.style.overflow = 'hidden';
+	//obj.style.clip = 'rect('+height+'px,0px,'+maxheight+'px,auto);';
 }
 
 function isOverObj(obj, bias, event)
 {
-  var fieldUL = new Object();
-  var dim = fetch_dimensions(obj);
-  var off = fetch_offset(obj);
-  fieldUL['top'] = off['top'];
-  fieldUL['left'] = off['left'];
-  fieldUL['right'] = off['left'] + dim['w'];
-  fieldUL['bottom'] = off['top'] + dim['h'];
-  
-  var mouseY_local = mouseY + getScrollOffset();
-  
-  // document.getElementById('debug').innerHTML = '<br />Mouse: x: '+mouseX+', y:' + mouseY + '<br />' + document.getElementById('debug').innerHTML;
-  
-  if(bias)
-  {
-    if ( ( mouseX < fieldUL['left'] + 2 || mouseX > fieldUL['right']  - 5 ) ||
-         ( mouseY_local < fieldUL['top']  - 2 || mouseY_local > fieldUL['bottom'] - 2 ) )
-    {
-       return false;
-    }
-  }
-  else
-  {
-    if ( ( mouseX < fieldUL['left'] || mouseX > fieldUL['right']  ) ||
-         ( mouseY_local < fieldUL['top']  || mouseY_local > fieldUL['bottom'] ) )
-       return false;
-  }
-     
-  return true;
+	var fieldUL = new Object();
+	var dim = fetch_dimensions(obj);
+	var off = fetch_offset(obj);
+	fieldUL['top'] = off['top'];
+	fieldUL['left'] = off['left'];
+	fieldUL['right'] = off['left'] + dim['w'];
+	fieldUL['bottom'] = off['top'] + dim['h'];
+	
+	var mouseY_local = mouseY + getScrollOffset();
+	
+	// document.getElementById('debug').innerHTML = '<br />Mouse: x: '+mouseX+', y:' + mouseY + '<br />' + document.getElementById('debug').innerHTML;
+	
+	if(bias)
+	{
+		if ( ( mouseX < fieldUL['left'] + 2 || mouseX > fieldUL['right']  - 5 ) ||
+ 				( mouseY_local < fieldUL['top']  - 2 || mouseY_local > fieldUL['bottom'] - 2 ) )
+		{
+ 			return false;
+		}
+	}
+	else
+	{
+		if ( ( mouseX < fieldUL['left'] || mouseX > fieldUL['right']  ) ||
+ 				( mouseY_local < fieldUL['top']  || mouseY_local > fieldUL['bottom'] ) )
+ 			return false;
+	}
+ 		
+	return true;
 }
 
 function jBoxGarbageCollection(e)
 {
-  setMousePos(e);
-  var menus = document.getElementsByClassName('div', 'menu');
-  if ( menus.length > 0 )
-  {
-    for ( var i in menus )
-    {
-      if ( typeof(menus[i]) != 'object')
-        continue; // toJSONString() compatibility
-      var uls = menus[i].getElementsByTagName('ul');
-      if ( uls.length > 0 )
-      {
-        for ( var j = 0; j < uls.length; j++ )
-        {
-          if ( !isOverObj(uls[j], false, e) )
-          {
-            $dynano(uls[j].previousSibling).rmClass('liteselected');
-            //uls[j].style.display = 'none';
-            slideIn(uls[j]);
-          }
-        }
-      }
-      var uls = getElementsByClassName(menus[i], 'divs', 'submenu');
-      if ( uls.length > 0 )
-      {
-        for ( var j = 0; j < uls.length; j++ )
-        {
-          if ( !isOverObj(uls[j], false, e) )
-          {
-            $dynano(uls[j].previousSibling).rmClass('liteselected');
-            //uls[j].style.display = 'none';
-            slideIn(uls[j]);
-          }
-        }
-      }
-    }
-  }
+	setMousePos(e);
+	var menus = document.getElementsByClassName('div', 'menu');
+	if ( menus.length > 0 )
+	{
+		for ( var i in menus )
+		{
+			if ( typeof(menus[i]) != 'object')
+				continue; // toJSONString() compatibility
+			var uls = menus[i].getElementsByTagName('ul');
+			if ( uls.length > 0 )
+			{
+				for ( var j = 0; j < uls.length; j++ )
+				{
+					if ( !isOverObj(uls[j], false, e) )
+					{
+						$dynano(uls[j].previousSibling).rmClass('liteselected');
+						//uls[j].style.display = 'none';
+						slideIn(uls[j]);
+					}
+				}
+			}
+			var uls = getElementsByClassName(menus[i], 'divs', 'submenu');
+			if ( uls.length > 0 )
+			{
+				for ( var j = 0; j < uls.length; j++ )
+				{
+					if ( !isOverObj(uls[j], false, e) )
+					{
+						$dynano(uls[j].previousSibling).rmClass('liteselected');
+						//uls[j].style.display = 'none';
+						slideIn(uls[j]);
+					}
+				}
+			}
+		}
+	}
 }
 
 document.onclick = jBoxGarbageCollection;
 
 var getElementsByClassName = function(parent, type, cls) {
-  if(!type)
-    type = '*';
-  ret = new Array();
-  if ( !parent )
-    return ret;
-  el = parent.getElementsByTagName(type);
-  for ( var i = 0; i < el.length; i++ )
-  {
-    if ( typeof(el[i]) != 'object')
-      continue; // toJSONString() compatibility
-    if(el[i].className)
-    {
-      if(el[i].className.indexOf(' ') > 0)
-      {
-        classes = el[i].className.split(' ');
-      }
-      else
-      {
-        classes = new Array();
-        classes.push(el[i].className);
-      }
-      if ( in_array(cls, classes) )
-        ret.push(el[i]);
-    }
-  }
-  return ret;
+	if(!type)
+		type = '*';
+	ret = new Array();
+	if ( !parent )
+		return ret;
+	el = parent.getElementsByTagName(type);
+	for ( var i = 0; i < el.length; i++ )
+	{
+		if ( typeof(el[i]) != 'object')
+			continue; // toJSONString() compatibility
+		if(el[i].className)
+		{
+			if(el[i].className.indexOf(' ') > 0)
+			{
+				classes = el[i].className.split(' ');
+			}
+			else
+			{
+				classes = new Array();
+				classes.push(el[i].className);
+			}
+			if ( in_array(cls, classes) )
+				ret.push(el[i]);
+		}
+	}
+	return ret;
 }
 
 document.getElementsByClassName = function(type, cls) {
-  return getElementsByClassName(document, type, cls);
+	return getElementsByClassName(document, type, cls);
 }
 
 function setMousePos(event)
 {
-  if(IE)
-  {
-    if(!event)
-    {
-      event = window.event;
-    }
-    clX = event.clientX;
-    if ( document.body )
-      sL  = document.body.scrollLeft;
-    else
-      sL  = 0;
-    mouseX = clX + sL;
-    mouseY = event.clientY + ( document.body ? document.body.scrollTop : 0 );
-    return;
-  }
-  if( typeof(event.clientX) == 'number' )
-  {
-    mouseX = event.clientX;
-    mouseY = event.clientY;
-    return;
-  }
-  else if( typeof(event.layerX) == 'number' )
-  {
-    mouseX = event.layerX;
-    mouseY = event.layerY;
-    return;
-  }
-  else if( typeof(event.offsetX) == 'number' )
-  {
-    mouseX = event.offsetX;
-    mouseY = event.offsetY;
-    return;
-  }
-  else if( typeof(event.screenX) == 'number' )
-  {
-    mouseX = event.screenX;
-    mouseY = event.screenY;
-    return;
-  }
-  else if( typeof(event.x) == 'number' )
-  {
-    mouseX = event.x;
-    mouseY = event.y;
-    return;
-  }
+	if(IE)
+	{
+		if(!event)
+		{
+			event = window.event;
+		}
+		clX = event.clientX;
+		if ( document.body )
+			sL  = document.body.scrollLeft;
+		else
+			sL  = 0;
+		mouseX = clX + sL;
+		mouseY = event.clientY + ( document.body ? document.body.scrollTop : 0 );
+		return;
+	}
+	if( typeof(event.clientX) == 'number' )
+	{
+		mouseX = event.clientX;
+		mouseY = event.clientY;
+		return;
+	}
+	else if( typeof(event.layerX) == 'number' )
+	{
+		mouseX = event.layerX;
+		mouseY = event.layerY;
+		return;
+	}
+	else if( typeof(event.offsetX) == 'number' )
+	{
+		mouseX = event.offsetX;
+		mouseY = event.offsetY;
+		return;
+	}
+	else if( typeof(event.screenX) == 'number' )
+	{
+		mouseX = event.screenX;
+		mouseY = event.screenY;
+		return;
+	}
+	else if( typeof(event.x) == 'number' )
+	{
+		mouseX = event.x;
+		mouseY = event.y;
+		return;
+	}
 }
 
 document.onmousemove = function(e)
 {
-  setMousePos(e);
+	setMousePos(e);
 };
 
 function removeTextNodes(obj)
 {
-  if(obj)
-  {
-    if(typeof(obj.tagName) != 'string' || ( String(obj) == '[object Text]' && is_Safari ) )
-    {
-      if ( ( obj.nodeType == 3 && obj.data.match(/^([\s]*)$/ig) ) ) //  || ( typeof(obj.innerHTML) == undefined && is_Safari ) ) 
-      {
-        obj.parentNode.removeChild(obj);
-        return;
-      }
-    }
-    if(obj.firstChild)
-    {
-      for(var i = 0; i < obj.childNodes.length; i++)
-      {
-        removeTextNodes(obj.childNodes[i]);
-      }
-    }
-  }
+	if(obj)
+	{
+		if(typeof(obj.tagName) != 'string' || ( String(obj) == '[object Text]' && is_Safari ) )
+		{
+			if ( ( obj.nodeType == 3 && obj.data.match(/^([\s]*)$/ig) ) ) //  || ( typeof(obj.innerHTML) == undefined && is_Safari ) ) 
+			{
+				obj.parentNode.removeChild(obj);
+				return;
+			}
+		}
+		if(obj.firstChild)
+		{
+			for(var i = 0; i < obj.childNodes.length; i++)
+			{
+				removeTextNodes(obj.childNodes[i]);
+			}
+		}
+	}
 }
 
--- a/includes/clientside/static/dynano.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/dynano.js	Sun Mar 28 23:10:46 2010 -0400
@@ -2,196 +2,196 @@
 
 var $dynano = function(id)
 {
-  return new DNobj(id);
+	return new DNobj(id);
 }
 function DNobj(id)
 {
-  if ( id == undefined )
-  {
-    return {};
-  }
-  this.object = ( typeof(id) == 'object' ) ? id : document.getElementById(id);
-  if ( !this.object )
-  {
-    console.warn('Dynano: requested object is bad. id parameter follows.');
-    console.debug(id);
-    this.object = false;
-    return this;
-  }
-  if ( this.object.Dynano )
-  {
-    return this.object.Dynano;
-  }
-  this.object.Dynano = this;
-  
-  this.height = __DNObjGetHeight(this.object);
-  this.width = __DNObjGetWidth(this.object);
-  
-  if ( this.object.tagName == 'TEXTAREA' && ( typeof(tinyMCE) == 'object' || typeof(tinyMCE_GZ) == 'object' ) )
-  {
-    this.object.dnIsMCE = 'no';
-    this.switchToMCE = DN_switchToMCE;
-    this.destroyMCE = DN_destroyMCE;
-    this.getContent = DN_mceFetchContent;
-    this.setContent = DN_mceSetContent;
-    this.makeSwitchable = DN_makeSwitchableTA;
-    this.isMCE = DN_isMCE;
-  }
+	if ( id == undefined )
+	{
+		return {};
+	}
+	this.object = ( typeof(id) == 'object' ) ? id : document.getElementById(id);
+	if ( !this.object )
+	{
+		console.warn('Dynano: requested object is bad. id parameter follows.');
+		console.debug(id);
+		this.object = false;
+		return this;
+	}
+	if ( this.object.Dynano )
+	{
+		return this.object.Dynano;
+	}
+	this.object.Dynano = this;
+	
+	this.height = __DNObjGetHeight(this.object);
+	this.width = __DNObjGetWidth(this.object);
+	
+	if ( this.object.tagName == 'TEXTAREA' && ( typeof(tinyMCE) == 'object' || typeof(tinyMCE_GZ) == 'object' ) )
+	{
+		this.object.dnIsMCE = 'no';
+		this.switchToMCE = DN_switchToMCE;
+		this.destroyMCE = DN_destroyMCE;
+		this.getContent = DN_mceFetchContent;
+		this.setContent = DN_mceSetContent;
+		this.makeSwitchable = DN_makeSwitchableTA;
+		this.isMCE = DN_isMCE;
+	}
 }
 function __DNObjGetHeight(o) {
-  return o.offsetHeight;
+	return o.offsetHeight;
 }
 
 function __DNObjGetWidth(o) {
-  return o.offsetWidth;
+	return o.offsetWidth;
 }
 
 function addClass(obj, clsname)
 {
-  var cnt = obj.className;
-  var space = ( (cnt + '').length > 0 ) ? ' ' : '';
-  var cls = cnt + space + clsname;
-  obj.className = cls;
+	var cnt = obj.className;
+	var space = ( (cnt + '').length > 0 ) ? ' ' : '';
+	var cls = cnt + space + clsname;
+	obj.className = cls;
 }
 
 function rmClass(obj, clsname)
 {
-  var cnt = obj.className;
-  if ( cnt == clsname )
-  {
-    obj.className = '';
-  }
-  else
-  {
-    cnt = cnt.replace(clsname, '');
-    cnt = trim(cnt);
-    obj.className = cnt;
-  }
+	var cnt = obj.className;
+	if ( cnt == clsname )
+	{
+		obj.className = '';
+	}
+	else
+	{
+		cnt = cnt.replace(clsname, '');
+		cnt = trim(cnt);
+		obj.className = cnt;
+	}
 }
 
 function hasClass(obj, clsname)
 {
-  var cnt = obj.className;
-  if ( !cnt )
-    return false;
-  if ( cnt == clsname )
-    return true;
-  cnt = cnt.split(' ');
-  
-  for ( var i in cnt )
-    if ( cnt[i] == clsname )
-      return true;
-    
-  return false;
+	var cnt = obj.className;
+	if ( !cnt )
+		return false;
+	if ( cnt == clsname )
+		return true;
+	cnt = cnt.split(' ');
+	
+	for ( var i in cnt )
+		if ( cnt[i] == clsname )
+			return true;
+		
+	return false;
 }
 function __DNObjGetLeft(obj) {
-  var left_offset = obj.offsetLeft;
-  while ((obj = obj.offsetParent) != null) {
-    left_offset += obj.offsetLeft;
-  }
-  return left_offset;
+	var left_offset = obj.offsetLeft;
+	while ((obj = obj.offsetParent) != null) {
+		left_offset += obj.offsetLeft;
+	}
+	return left_offset;
 }
 
 function __DNObjGetTop(obj) {
-  var left_offset = obj.offsetTop;
-  while ((obj = obj.offsetParent) != null) {
-    left_offset += obj.offsetTop;
-  }
-  return left_offset;
+	var left_offset = obj.offsetTop;
+	while ((obj = obj.offsetParent) != null) {
+		left_offset += obj.offsetTop;
+	}
+	return left_offset;
 }
 
 function DN_switchToMCE(performWikiTransform)
 {
-  if ( !this.object.id )
-    this.object.id = 'textarea_' + Math.floor(Math.random() * 1000000);
-  if ( !this.object.name )
-    this.object.name = 'textarea_' + Math.floor(Math.random() * 1000000);
-  // Updated for TinyMCE 3.x
-  if ( performWikiTransform )
-  {
-    this.object.value = DN_WikitextToXHTML(this.object.value);
-  }
-  // If tinyMCE init hasn't been called yet, do it now.
-  if ( !tinymce_initted )
-  {
-    console.info('$dynano().switchToMCE(): doing "exact"-type MCE init');
-    enano_tinymce_options.mode = 'exact';
-    enano_tinymce_options.elements = this.object.id;
-    initTinyMCE();
-    this.object.dnIsMCE = 'yes';
-    return true;
-  }
-  else
-  {
-    console.info('$dynano().switchToMCE(): tinyMCE already loaded, calling mceAddControl');
-    tinymce.EditorManager.execCommand("mceAddControl", true, this.object.id);
-    this.object.dnIsMCE = 'yes';
-  }
-  return this;
+	if ( !this.object.id )
+		this.object.id = 'textarea_' + Math.floor(Math.random() * 1000000);
+	if ( !this.object.name )
+		this.object.name = 'textarea_' + Math.floor(Math.random() * 1000000);
+	// Updated for TinyMCE 3.x
+	if ( performWikiTransform )
+	{
+		this.object.value = DN_WikitextToXHTML(this.object.value);
+	}
+	// If tinyMCE init hasn't been called yet, do it now.
+	if ( !tinymce_initted )
+	{
+		console.info('$dynano().switchToMCE(): doing "exact"-type MCE init');
+		enano_tinymce_options.mode = 'exact';
+		enano_tinymce_options.elements = this.object.id;
+		initTinyMCE();
+		this.object.dnIsMCE = 'yes';
+		return true;
+	}
+	else
+	{
+		console.info('$dynano().switchToMCE(): tinyMCE already loaded, calling mceAddControl');
+		tinymce.EditorManager.execCommand("mceAddControl", true, this.object.id);
+		this.object.dnIsMCE = 'yes';
+	}
+	return this;
 }
 
 function DN_destroyMCE(performWikiTransform)
 {
-  //if ( !this.object.dn_is_mce )
-  //  return this;
-  if ( this.object.id && window.tinymce )
-  {
-    // TinyMCE 2.x
-    // tinymce.EditorManager.removeMCEControl(this.object.name);
-    // TinyMCE 3.x
-    var ed = tinymce.EditorManager.getInstanceById(this.object.id);
-    if ( ed )
-    {
-      if ( !tinymce.EditorManager.execCommand("mceRemoveEditor", false, this.object.id) )
-        alert('could not destroy editor');
-      if ( performWikiTransform )
-      {
-        this.object.value = DN_XHTMLToWikitext(this.object.value);
-      }
-    }
-  }
-  this.object.dnIsMCE = 'no';
-  return this;
+	//if ( !this.object.dn_is_mce )
+	//  return this;
+	if ( this.object.id && window.tinymce )
+	{
+		// TinyMCE 2.x
+		// tinymce.EditorManager.removeMCEControl(this.object.name);
+		// TinyMCE 3.x
+		var ed = tinymce.EditorManager.getInstanceById(this.object.id);
+		if ( ed )
+		{
+			if ( !tinymce.EditorManager.execCommand("mceRemoveEditor", false, this.object.id) )
+				alert('could not destroy editor');
+			if ( performWikiTransform )
+			{
+				this.object.value = DN_XHTMLToWikitext(this.object.value);
+			}
+		}
+	}
+	this.object.dnIsMCE = 'no';
+	return this;
 }
 
 function DN_isMCE()
 {
-  return ( this.object.dnIsMCE == 'yes' );
+	return ( this.object.dnIsMCE == 'yes' );
 }
 
 function DN_mceFetchContent()
 {
-  if ( this.object.name )
-  {
-    var text = this.object.value;
-    if ( tinymce.EditorManager.get(this.object.id) )
-    {
-      var editor = tinymce.EditorManager.get(this.object.id);
-      text = editor.getContent();
-    }
-    return text;
-  }
-  else
-  {
-    return this.object.value;
-  }
+	if ( this.object.name )
+	{
+		var text = this.object.value;
+		if ( tinymce.EditorManager.get(this.object.id) )
+		{
+			var editor = tinymce.EditorManager.get(this.object.id);
+			text = editor.getContent();
+		}
+		return text;
+	}
+	else
+	{
+		return this.object.value;
+	}
 }
 
 function DN_mceSetContent(text)
 {
-  if ( this.object.name )
-  {
-    this.object.value = text;
-    if ( tinymce.EditorManager.get(this.object.id) )
-    {
-      var editor = tinymce.EditorManager.get(this.object.id);
-      editor.setContent(text);
-    }
-  }
-  else
-  {
-    this.object.value = text;
-  }
+	if ( this.object.name )
+	{
+		this.object.value = text;
+		if ( tinymce.EditorManager.get(this.object.id) )
+		{
+			var editor = tinymce.EditorManager.get(this.object.id);
+			editor.setContent(text);
+		}
+	}
+	else
+	{
+		this.object.value = text;
+	}
 }
 
 var P_BOTTOM = 1;
@@ -199,137 +199,137 @@
 
 function DN_makeSwitchableTA(pos)
 {
-  if ( this.toggler )
-    return false;
-  
-  if ( !pos )
-    pos = P_BOTTOM;
-  
-  load_component('l10n');
-  var cookiename = 'enano_editor_mode';
-  
-  var toggler = document.createElement('div');
-  toggler.dynano = this;
-  this.toggler = toggler;
-  
-  if ( !this.object.id )
-    this.object.id = 'dynano_auto_' + Math.floor(Math.random() * 1000000);
-  
-  toggler.s_mode_text = $lang.get('editor_btn_wikitext');
-  toggler.s_mode_graphical = $lang.get('editor_btn_graphical');
-  
-  toggler.set_text = function()
-  {
-    if ( this.dynano.object.dnIsMCE == 'yes' )
-      this.dynano.destroyMCE();
-    
-    this.innerHTML = '';
-    this.appendChild(document.createTextNode(this.s_mode_text + ' | '));
-    
-    var link = document.createElement('a');
-    link.href = '#';
-    link.onclick = function()
-    {
-      this.parentNode.set_graphical();
-      return false;
-    }
-    link.appendChild(document.createTextNode(this.s_mode_graphical));
-    this.appendChild(link);
-    
-    createCookie('enano_editor_mode', 'text', 365);
-  }
-  
-  toggler.set_graphical = function()
-  {
-    this.dynano.switchToMCE();
-    this.innerHTML = '';
-    
-    var link = document.createElement('a');
-    link.href = '#';
-    link.onclick = function()
-    {
-      this.parentNode.set_text();
-      return false;
-    }
-    link.appendChild(document.createTextNode(this.s_mode_text));
-    this.appendChild(link);
-    
-    this.appendChild(document.createTextNode(' | ' + this.s_mode_graphical));
-    createCookie('enano_editor_mode', 'tinymce', 365);
-  }
-  
-  toggler.style.styleFloat = 'right';
-  toggler.style.cssFloat = 'right';
-  if ( pos == P_BOTTOM )
-  {
-    insertAfter(this.object.parentNode, toggler, this.object);
-  }
-  else
-  {
-    this.object.parentNode.insertBefore(toggler, this.object);
-  }
-  
-  if ( readCookie(cookiename) == 'tinymce' )
-  {
-    toggler.set_graphical();
-  }
-  else
-  {
-    toggler.set_text();
-  }
+	if ( this.toggler )
+		return false;
+	
+	if ( !pos )
+		pos = P_BOTTOM;
+	
+	load_component('l10n');
+	var cookiename = 'enano_editor_mode';
+	
+	var toggler = document.createElement('div');
+	toggler.dynano = this;
+	this.toggler = toggler;
+	
+	if ( !this.object.id )
+		this.object.id = 'dynano_auto_' + Math.floor(Math.random() * 1000000);
+	
+	toggler.s_mode_text = $lang.get('editor_btn_wikitext');
+	toggler.s_mode_graphical = $lang.get('editor_btn_graphical');
+	
+	toggler.set_text = function()
+	{
+		if ( this.dynano.object.dnIsMCE == 'yes' )
+			this.dynano.destroyMCE();
+		
+		this.innerHTML = '';
+		this.appendChild(document.createTextNode(this.s_mode_text + ' | '));
+		
+		var link = document.createElement('a');
+		link.href = '#';
+		link.onclick = function()
+		{
+			this.parentNode.set_graphical();
+			return false;
+		}
+		link.appendChild(document.createTextNode(this.s_mode_graphical));
+		this.appendChild(link);
+		
+		createCookie('enano_editor_mode', 'text', 365);
+	}
+	
+	toggler.set_graphical = function()
+	{
+		this.dynano.switchToMCE();
+		this.innerHTML = '';
+		
+		var link = document.createElement('a');
+		link.href = '#';
+		link.onclick = function()
+		{
+			this.parentNode.set_text();
+			return false;
+		}
+		link.appendChild(document.createTextNode(this.s_mode_text));
+		this.appendChild(link);
+		
+		this.appendChild(document.createTextNode(' | ' + this.s_mode_graphical));
+		createCookie('enano_editor_mode', 'tinymce', 365);
+	}
+	
+	toggler.style.styleFloat = 'right';
+	toggler.style.cssFloat = 'right';
+	if ( pos == P_BOTTOM )
+	{
+		insertAfter(this.object.parentNode, toggler, this.object);
+	}
+	else
+	{
+		this.object.parentNode.insertBefore(toggler, this.object);
+	}
+	
+	if ( readCookie(cookiename) == 'tinymce' )
+	{
+		toggler.set_graphical();
+	}
+	else
+	{
+		toggler.set_text();
+	}
 }
 
 function DN_WikitextToXHTML(text)
 {
-  return DN_AjaxGetTransformedText(text, 'xhtml');
+	return DN_AjaxGetTransformedText(text, 'xhtml');
 }
 
 function DN_XHTMLToWikitext(text)
 {
-  return DN_AjaxGetTransformedText(text, 'wikitext');
+	return DN_AjaxGetTransformedText(text, 'wikitext');
 }
 
 // AJAX to the server to transform text
 function DN_AjaxGetTransformedText(text, to)
 {
-  // get an XHR instance
-  var ajax = ajaxMakeXHR();
-  
-  var uri = stdAjaxPrefix + '&_mode=transform&to=' + to;
-  var parms = 'text=' + ajaxEscape(text);
-  try
-  {
-    ajax.open('POST', uri, false);
-    ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
-    // Setting Content-length in Safari triggers a warning
-    if ( !is_Safari )
-    {
-      ajax.setRequestHeader("Content-length", parms.length);
-    }
-    ajax.send(parms);
-    // async request, so if status != 200 at this point then we're screwed
-    if ( ajax.readyState == 4 && ajax.status == 200 )
-    {
-      var response = String(ajax.responseText + '');
-      if ( !check_json_response(response) )
-      {
-        handle_invalid_json(response);
-        return text;
-      }
-      response = parseJSON(response);
-      if ( response.mode == 'error' )
-      {
-        alert(response.error);
-        return text;
-      }
-      return response.text;
-    }
-  }
-  catch(e)
-  {
-    console.warn('DN_AjaxGetTransformedText: XHR failed');
-  }
-  return text;
+	// get an XHR instance
+	var ajax = ajaxMakeXHR();
+	
+	var uri = stdAjaxPrefix + '&_mode=transform&to=' + to;
+	var parms = 'text=' + ajaxEscape(text);
+	try
+	{
+		ajax.open('POST', uri, false);
+		ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+		// Setting Content-length in Safari triggers a warning
+		if ( !is_Safari )
+		{
+			ajax.setRequestHeader("Content-length", parms.length);
+		}
+		ajax.send(parms);
+		// async request, so if status != 200 at this point then we're screwed
+		if ( ajax.readyState == 4 && ajax.status == 200 )
+		{
+			var response = String(ajax.responseText + '');
+			if ( !check_json_response(response) )
+			{
+				handle_invalid_json(response);
+				return text;
+			}
+			response = parseJSON(response);
+			if ( response.mode == 'error' )
+			{
+				alert(response.error);
+				return text;
+			}
+			return response.text;
+		}
+	}
+	catch(e)
+	{
+		console.warn('DN_AjaxGetTransformedText: XHR failed');
+	}
+	return text;
 }
 
 DNobj.prototype.addClass = function(clsname) { addClass(this.object, clsname); return this; };
--- a/includes/clientside/static/editor.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/editor.js	Sun Mar 28 23:10:46 2010 -0400
@@ -9,1167 +9,1167 @@
 
 window.ajaxEditor = function(revid)
 {
-  if ( KILL_SWITCH )
-    return true;
-  if ( editor_open )
-    return true;
-  load_component(['l10n', 'template-compiler', 'messagebox', 'fadefilter', 'flyin', 'toolbar']);
-  selectButtonMinor('edit');
-  selectButtonMajor('article');
-  setAjaxLoading();
-  
-  var rev_id_uri = ( revid ) ? '&revid=' + revid : '';
-  ajaxGet(stdAjaxPrefix + '&_mode=getsource' + rev_id_uri, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        unsetAjaxLoading();
-        
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(response);
-          return false;
-        }
-        
-        response = parseJSON(response);
-        if ( response.mode == 'error' )
-        {
-          unselectAllButtonsMinor();
-          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
-          return false;
-        }
-        
-        if ( !response.auth_view_source )
-        {
-          unselectAllButtonsMinor();
-          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_access_denied_title'), $lang.get('editor_err_access_denied_body'));
-          return false;
-        }
-        
-        // do we need to enter a captcha before saving the page?
-        var captcha_hash = ( response.require_captcha ) ? response.captcha_id : false;
-        
-        ajaxBuildEditor((!response.auth_edit), response.time, response.allow_wysiwyg, captcha_hash, response.revid, response.undo_info, response);
-      }
-    });
+	if ( KILL_SWITCH )
+		return true;
+	if ( editor_open )
+		return true;
+	load_component(['l10n', 'template-compiler', 'messagebox', 'fadefilter', 'flyin', 'toolbar']);
+	selectButtonMinor('edit');
+	selectButtonMajor('article');
+	setAjaxLoading();
+	
+	var rev_id_uri = ( revid ) ? '&revid=' + revid : '';
+	ajaxGet(stdAjaxPrefix + '&_mode=getsource' + rev_id_uri, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				unsetAjaxLoading();
+				
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(response);
+					return false;
+				}
+				
+				response = parseJSON(response);
+				if ( response.mode == 'error' )
+				{
+					unselectAllButtonsMinor();
+					new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
+					return false;
+				}
+				
+				if ( !response.auth_view_source )
+				{
+					unselectAllButtonsMinor();
+					new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_access_denied_title'), $lang.get('editor_err_access_denied_body'));
+					return false;
+				}
+				
+				// do we need to enter a captcha before saving the page?
+				var captcha_hash = ( response.require_captcha ) ? response.captcha_id : false;
+				
+				ajaxBuildEditor((!response.auth_edit), response.time, response.allow_wysiwyg, captcha_hash, response.revid, response.undo_info, response);
+			}
+		});
 }
 
 window.ajaxBuildEditor = function(readonly, timestamp, allow_wysiwyg, captcha_hash, revid, undo_info, response)
 {
-  // Set flags
-  // We don't want the fancy confirmation framework to trigger if the user is only viewing the page source
-  if ( !readonly )
-  {
-    editor_open = true;
-    disableUnload();
-  }
-  
-  // Destroy existing contents of page container
-  if ( editor_use_modal_window )
-  {
-    darken(true, 70, 'enano_editor_darkener');
-    // Build a div with 80% width, centered, and 10px from the top of the window
-    var edcon = document.createElement('div');
-    edcon.style.position = 'absolute';
-    edcon.style.backgroundColor = '#FFFFFF';
-    edcon.style.padding = '10px';
-    edcon.style.width = '80%';
-    edcon.style.zIndex = getHighestZ() + 1;
-    edcon.id = 'ajaxEditContainerModal';
-    
-    // Positioning
-    var top = getScrollOffset() + 10;
-    var left = ( getWidth() / 10 ) - 10; // 10% of window width on either side - 10px for padding = perfect centering effect
-    edcon.style.top = String(top) + 'px';
-    edcon.style.left = String(left) + 'px';
-    var body = document.getElementsByTagName('body')[0];
-    
-    // Set opacity to 0
-    domObjChangeOpac(0, edcon);
-    body.appendChild(edcon);
-  }
-  else
-  {
-    var edcon = document.getElementById('ajaxEditContainer');
-    for ( var i = edcon.childNodes.length - 1; i >= 0; i-- )
-    {
-      edcon.removeChild(edcon.childNodes[i]);
-    }
-  }
-  
-  var content = response.src;
-  
-  //
-  // BUILD EDITOR
-  //
-  
-  var heading = document.createElement('h3');
-  heading.style.cssFloat = 'left';
-  heading.style.styleFloat = 'left';
-  heading.style.marginTop = '0px';
-  heading.style.marginBottom = '0px';
-  heading.appendChild(document.createTextNode($lang.get('editor_msg_editor_heading')));
-  
-  // Plaintext/wikitext toggler
-  // Only build the editor if using TinyMCE is allowed. THIS IS WEAK
-  // AND CANNOT BE MADE ANY STRONGER.
-  
-  if ( allow_wysiwyg )
-  {
-    var toggler = document.createElement('p');
-    toggler.style.marginLeft = '0';
-    toggler.style.textAlign = 'right';
-    
-    var span_wiki = document.createElement('span');
-    var span_mce  = document.createElement('span');
-    span_wiki.id  = 'enano_edit_btn_pt';
-    span_mce.id   = 'enano_edit_btn_mce';
-    
-    // to-wikitext button
-    var a = document.createElement('a');
-    a.href = '#';
-    a.className = 'abutton image abutton_green';
-    a.appendChild(gen_sprite(scriptPath + '/images/editor/sprite.png', 16, 16, 0, 96));
-    a.appendChild(document.createTextNode(' ' + $lang.get('editor_btn_wikitext')));
-    span_wiki.appendChild(a);
-    toggler.appendChild(span_wiki);
-    
-    // to-HTML button
-    var a = document.createElement('a');
-    a.href = '#';
-    a.className = 'abutton image abutton_blue';
-    a.appendChild(gen_sprite(scriptPath + '/images/editor/sprite.png', 16, 16, 0, 112));
-    a.appendChild(document.createTextNode(' ' + $lang.get('editor_btn_graphical')));
-    span_mce.appendChild(a);
-    toggler.appendChild(span_mce);
-    
-    if ( response.page_format == 'xhtml' )
-    {
-      // Current selection is TinyMCE - make span_wiki have the link and span_mce be plaintext
-      span_mce.style.display = 'none';
-    }
-    else
-    {
-      // Current selection is wikitext - set span_wiki to plaintext and span_mce to link
-      span_wiki.style.display = 'none';
-    }
-  }
-  
-  // Form (to allow submits from MCE to trigger a real save)
-  var form = document.createElement('form');
-  form.action = 'javascript:void(0);';
-  form.onsubmit = function()
-  {
-    ajaxEditorSave();
-    return false;
-  }
-  
-  // Draft notice
-  if ( response.have_draft && !readonly )
-  {
-    var dn = document.createElement('div');
-    dn.className = 'warning-box';
-    dn.id = 'ajax_edit_draft_notice';
-    dn.innerHTML = '<b>' + $lang.get('editor_msg_have_draft_title') + '</b><br />';
-    dn.innerHTML += $lang.get('editor_msg_have_draft_body', { author: response.draft_author, time: response.draft_time });
-  }
-  
-  // Old-revision notice
-  if ( revid > 0 )
-  {
-    var oldrev_box = document.createElement('div');
-    oldrev_box.className = 'usermessage';
-    oldrev_box.appendChild(document.createTextNode($lang.get('editor_msg_editing_old_revision')));
-  }
-  
-  // Preview holder
-  var preview_anchor = document.createElement('a');
-  preview_anchor.name = 'ajax_preview';
-  preview_anchor.id = 'ajax_preview';
-  var preview_container = document.createElement('div');
-  preview_container.id = 'enano_editor_preview';
-  preview_container.style.clear = 'left';
-  
-  // Textarea containing the content
-  var ta_wrapper = document.createElement('div');
-  ta_wrapper.style.margin = '10px 0';
-  // ta_wrapper.style.clear = 'both';
-  var textarea = document.createElement('textarea');
-  ta_wrapper.appendChild(textarea);
-  
-  textarea.id = 'ajaxEditArea';
-  textarea.rows = '20';
-  textarea.cols = '60';
-  textarea.style.width = '98.7%';
-  
-  // Revision metadata controls
-  var tblholder = document.createElement('div');
-  tblholder.className = 'tblholder';
-  var metatable = document.createElement('table');
-  metatable.setAttribute('border', '0');
-  metatable.setAttribute('cellspacing', '1');
-  metatable.setAttribute('cellpadding', '4');
-  
-  if ( readonly )
-  {
-    // Close Viewer button
-    var toolbar = '';
-    var head = new templateParser(response.toolbar_templates.toolbar_start);
-    var button = new templateParser(response.toolbar_templates.toolbar_button);
-    var tail = new templateParser(response.toolbar_templates.toolbar_end);
-    
-    toolbar += head.run();
-    
-    button.assign_bool({
-        show_title: true
-      });
-    
-    // Button: close
-    button.assign_vars({
-        TITLE: $lang.get('editor_btn_closeviewer'),
-        IMAGE: editor_img_path + '/discard.gif',
-        SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 16),
-        FLAGS: 'href="#" onclick="ajaxReset(true); return false;"'
-      });
-    toolbar += button.run();
-    toolbar += tail.run();
-  }
-  else
-  {
-    // First row: edit summary
-    var tr1 = document.createElement('tr');
-    var td1_1 = document.createElement('td');
-    var td1_2 = document.createElement('td');
-    td1_1.className = 'row2';
-    td1_2.className = 'row1';
-    td1_2.style.width = '70%';
-    td1_1.appendChild(document.createTextNode($lang.get('editor_lbl_edit_summary')));
-    td1_1.appendChild(document.createElement('br'));
-    var small = document.createElement('small');
-    small.appendChild(document.createTextNode($lang.get('editor_lbl_edit_summary_explain')));
-    td1_1.appendChild(small);
-    
-    var field_es = document.createElement('input');
-    field_es.id = 'enano_editor_field_summary';
-    field_es.type = 'text';
-    field_es.size = '40';
-    field_es.style.width = '96%';
-    
-    if ( revid > 0 )
-    {
-      undo_info.last_rev_id = revid;
-      field_es.value = $lang.get('editor_reversion_edit_summary', undo_info);
-    }
-    
-    td1_2.appendChild(field_es);
-    
-    tr1.appendChild(td1_1);
-    tr1.appendChild(td1_2);
-    
-    // Second row: minor edit
-    var tr2 = document.createElement('tr');
-    var td2_1 = document.createElement('td');
-    var td2_2 = document.createElement('td');
-    td2_1.className = 'row2';
-    td2_2.className = 'row1';
-    td2_1.appendChild(document.createTextNode($lang.get('editor_lbl_minor_edit')));
-    td2_1.appendChild(document.createElement('br'));
-    var small = document.createElement('small');
-    small.appendChild(document.createTextNode($lang.get('editor_lbl_minor_edit_explain')));
-    td2_1.appendChild(small);
-    
-    var label = document.createElement('label');
-    var field_mi = document.createElement('input');
-    field_mi.id = 'enano_editor_field_minor';
-    field_mi.type = 'checkbox';
-    label.appendChild(field_mi);
-    label.appendChild(document.createTextNode(' '));
-    label.appendChild(document.createTextNode($lang.get('editor_lbl_minor_edit_field')));
-    td2_2.appendChild(label);
-    
-    tr2.appendChild(td2_1);
-    tr2.appendChild(td2_2);
-    
-    if ( captcha_hash )
-    {
-      // generate captcha field (effectively third row)
-      var tr4 = document.createElement('tr');
-      var td4_1 = document.createElement('td');
-      var td4_2 = document.createElement('td');
-      td4_1.className = 'row2';
-      td4_2.className = 'row1';
-      
-      td4_1.appendChild(document.createTextNode($lang.get('editor_lbl_field_captcha')));
-      td4_1.appendChild(document.createElement('br'));
-      var small2 = document.createElement('small');
-      small2.appendChild(document.createTextNode($lang.get('editor_msg_captcha_pleaseenter')));
-      small2.appendChild(document.createElement('br'));
-      small2.appendChild(document.createElement('br'));
-      small2.appendChild(document.createTextNode($lang.get('editor_msg_captcha_blind')));
-      td4_1.appendChild(small2);
-      
-      var img = document.createElement('img');
-      img.src = makeUrlNS('Special', 'Captcha/' + captcha_hash);
-      img.setAttribute('enano:captcha_hash', captcha_hash);
-      img.id = 'enano_editor_captcha_img';
-      img.onclick = function()
-      {
-        this.src = makeUrlNS('Special', 'Captcha/' + this.getAttribute('enano:captcha_hash') + '/' + Math.floor(Math.random() * 100000));
-      }
-      img.style.cursor = 'pointer';
-      td4_2.appendChild(img);
-      td4_2.appendChild(document.createElement('br'));
-      td4_2.appendChild(document.createTextNode($lang.get('editor_lbl_field_captcha_code') + ' '));
-      var input = document.createElement('input');
-      input.type = 'text';
-      input.id = 'enano_editor_field_captcha';
-      input.setAttribute('enano:captcha_hash', captcha_hash);
-      input.size = '9';
-      td4_2.appendChild(input);
-      
-      tr4.appendChild(td4_1);
-      tr4.appendChild(td4_2);
-    }
-    
-    // Third row: controls
-    
-    var toolbar = '';
-    var head = new templateParser(response.toolbar_templates.toolbar_start);
-    var button = new templateParser(response.toolbar_templates.toolbar_button);
-    var label = new templateParser(response.toolbar_templates.toolbar_label);
-    var tail = new templateParser(response.toolbar_templates.toolbar_end);
-    
-    button.assign_bool({
-        show_title: true
-      });
-    
-    toolbar += head.run();
-    
-    // Button: Save
-    button.assign_vars({
-        TITLE: $lang.get('editor_btn_save'),
-        IMAGE: editor_img_path + '/save.gif',
-        SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 64),
-        FLAGS: 'href="#" onclick="ajaxEditorSave(); return false;"'
-      });
-    toolbar += button.run();
-    
-    // Button: preview
-    button.assign_vars({
-        TITLE: $lang.get('editor_btn_preview'),
-        IMAGE: editor_img_path + '/preview.gif',
-        SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 32),
-        FLAGS: 'href="#" onclick="ajaxEditorGenPreview(); return false;"'
-      });
-    toolbar += button.run();
-    
-    // Button: revert
-    button.assign_vars({
-        TITLE: $lang.get('editor_btn_revert'),
-          IMAGE: editor_img_path + '/revert.gif',
-          SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 48),
-        FLAGS: 'href="#" onclick="ajaxEditorRevertToLatest(); return false;"'
-      });
-    toolbar += button.run();
-    
-    // Button: diff
-    button.assign_vars({
-        TITLE: $lang.get('editor_btn_diff'),
-        IMAGE: editor_img_path + '/diff.gif',
-        SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 0),
-        FLAGS: 'href="#" onclick="ajaxEditorShowDiffs(); return false;"'
-      });
-    toolbar += button.run();
-    
-    // Button: cancel
-    button.assign_vars({
-        TITLE: $lang.get('editor_btn_cancel'),
-        IMAGE: editor_img_path + '/discard.gif',
-        SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 16),
-        FLAGS: 'href="#" onclick="ajaxEditorCancel(); return false;"'
-      });
-    toolbar += button.run();
-    
-    // Separator
-    label.assign_vars({
-        TITLE: ' '
-      });
-    toolbar += label.run();
-    
-    // Button: Save draft
-    button.assign_vars({
-        TITLE: $lang.get('editor_btn_savedraft'),
-        IMAGE: editor_img_path + '/savedraft.gif',
-        SPRITE: false,
-        FLAGS: 'href="#" onclick="ajaxPerformAutosave(); return false;" id="ajax_edit_savedraft_btn"'
-      });
-    toolbar += button.run();
-    
-    toolbar += tail.run();
-    
-    metatable.appendChild(tr1);
-    metatable.appendChild(tr2);
-    if ( captcha_hash )
-    {
-      metatable.appendChild(tr4);
-    }
-    // metatable.appendChild(tr3);
-  }
-  tblholder.appendChild(metatable);
-  
-  // Edit disclaimer/notice
-  if ( response.edit_notice )
-  {
-    var en_div = document.createElement('div');
-    en_div.innerHTML = response.edit_notice;
-    en_div.className = 'usermessage';
-    en_div.style.margin = '10px 0 0 0';
-  }
-  
-  // Put it all together...
-  form.appendChild(heading);
-  if ( allow_wysiwyg )
-    form.appendChild(toggler);
-  
-  if ( dn )
-    form.appendChild(dn);
-  
-  if ( oldrev_box )
-    form.appendChild(oldrev_box);
-  
-  form.appendChild(preview_anchor);
-  form.appendChild(preview_container);
-  form.appendChild(ta_wrapper);
-  if ( !readonly )
-    form.appendChild(tblholder);
-  form.innerHTML += '<div style="margin: 10px 0 0 0;">' + toolbar + '</div>';
-  edcon.appendChild(form);
-  
-  if ( response.edit_notice && !readonly )
-  {
-    edcon.appendChild(en_div);
-  }
-  
-  // more textarea attribs/init
-  var textarea = document.getElementById('ajaxEditArea');
-  textarea.as_last_save = 0;
-  textarea.content_orig = content;
-  textarea.used_draft = false;
-  textarea.onkeyup = function()
-  {
-    if ( this.needReset )
-    {
-      var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
-      var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
-      img.src = editor_img_path + '/savedraft.gif';
-      lbl.innerHTML = $lang.get('editor_btn_savedraft');
-    }
-    if ( window.AutosaveTimeoutObj )
-      clearTimeout(window.AutosaveTimeoutObj);
-    window.AutosaveTimeoutObj = setTimeout('ajaxAutosaveDraft();', ( AUTOSAVE_TIMEOUT * 1000 ));
-  }
-  
-  if ( readonly )
-  {
-    textarea.className = 'mce_readonly';
-    textarea.setAttribute('readonly', 'readonly');
-  }
-  
-  $dynano('ajaxEditArea').object.focus();
-  $dynano('ajaxEditArea').object._edTimestamp = timestamp;
-  $dynano('ajaxEditArea').setContent(content);
-  
-  // If the editor preference is tinymce, switch the editor to TinyMCE now
-  if ( response.page_format == 'xhtml' && allow_wysiwyg )
-  {
-    $dynano('ajaxEditArea').switchToMCE(false);
-  }
-  
-  if ( allow_wysiwyg )
-  {
-    var a = document.getElementById('enano_edit_btn_pt').getElementsByTagName('a')[0];
-    a.onclick = function() {
-      ajaxSetEditorPlain();
-      return false;
-    };
-    var a = document.getElementById('enano_edit_btn_mce').getElementsByTagName('a')[0];
-    a.onclick = function() {
-      ajaxSetEditorMCE();
-      return false;
-    };
-  }
-  
-  // if we're using the modal window, fade it in
-  if ( editor_use_modal_window )
-  {
-    domOpacity(edcon, 0, 100, 500);
-  }
-  
-  // Autosave every 5 minutes           (m  *  s  *  ms)
-  setInterval('ajaxPerformAutosave();', ( 5 * 60 * 1000 ));
+	// Set flags
+	// We don't want the fancy confirmation framework to trigger if the user is only viewing the page source
+	if ( !readonly )
+	{
+		editor_open = true;
+		disableUnload();
+	}
+	
+	// Destroy existing contents of page container
+	if ( editor_use_modal_window )
+	{
+		darken(true, 70, 'enano_editor_darkener');
+		// Build a div with 80% width, centered, and 10px from the top of the window
+		var edcon = document.createElement('div');
+		edcon.style.position = 'absolute';
+		edcon.style.backgroundColor = '#FFFFFF';
+		edcon.style.padding = '10px';
+		edcon.style.width = '80%';
+		edcon.style.zIndex = getHighestZ() + 1;
+		edcon.id = 'ajaxEditContainerModal';
+		
+		// Positioning
+		var top = getScrollOffset() + 10;
+		var left = ( getWidth() / 10 ) - 10; // 10% of window width on either side - 10px for padding = perfect centering effect
+		edcon.style.top = String(top) + 'px';
+		edcon.style.left = String(left) + 'px';
+		var body = document.getElementsByTagName('body')[0];
+		
+		// Set opacity to 0
+		domObjChangeOpac(0, edcon);
+		body.appendChild(edcon);
+	}
+	else
+	{
+		var edcon = document.getElementById('ajaxEditContainer');
+		for ( var i = edcon.childNodes.length - 1; i >= 0; i-- )
+		{
+			edcon.removeChild(edcon.childNodes[i]);
+		}
+	}
+	
+	var content = response.src;
+	
+	//
+	// BUILD EDITOR
+	//
+	
+	var heading = document.createElement('h3');
+	heading.style.cssFloat = 'left';
+	heading.style.styleFloat = 'left';
+	heading.style.marginTop = '0px';
+	heading.style.marginBottom = '0px';
+	heading.appendChild(document.createTextNode($lang.get('editor_msg_editor_heading')));
+	
+	// Plaintext/wikitext toggler
+	// Only build the editor if using TinyMCE is allowed. THIS IS WEAK
+	// AND CANNOT BE MADE ANY STRONGER.
+	
+	if ( allow_wysiwyg )
+	{
+		var toggler = document.createElement('p');
+		toggler.style.marginLeft = '0';
+		toggler.style.textAlign = 'right';
+		
+		var span_wiki = document.createElement('span');
+		var span_mce  = document.createElement('span');
+		span_wiki.id  = 'enano_edit_btn_pt';
+		span_mce.id   = 'enano_edit_btn_mce';
+		
+		// to-wikitext button
+		var a = document.createElement('a');
+		a.href = '#';
+		a.className = 'abutton image abutton_green';
+		a.appendChild(gen_sprite(scriptPath + '/images/editor/sprite.png', 16, 16, 0, 96));
+		a.appendChild(document.createTextNode(' ' + $lang.get('editor_btn_wikitext')));
+		span_wiki.appendChild(a);
+		toggler.appendChild(span_wiki);
+		
+		// to-HTML button
+		var a = document.createElement('a');
+		a.href = '#';
+		a.className = 'abutton image abutton_blue';
+		a.appendChild(gen_sprite(scriptPath + '/images/editor/sprite.png', 16, 16, 0, 112));
+		a.appendChild(document.createTextNode(' ' + $lang.get('editor_btn_graphical')));
+		span_mce.appendChild(a);
+		toggler.appendChild(span_mce);
+		
+		if ( response.page_format == 'xhtml' )
+		{
+			// Current selection is TinyMCE - make span_wiki have the link and span_mce be plaintext
+			span_mce.style.display = 'none';
+		}
+		else
+		{
+			// Current selection is wikitext - set span_wiki to plaintext and span_mce to link
+			span_wiki.style.display = 'none';
+		}
+	}
+	
+	// Form (to allow submits from MCE to trigger a real save)
+	var form = document.createElement('form');
+	form.action = 'javascript:void(0);';
+	form.onsubmit = function()
+	{
+		ajaxEditorSave();
+		return false;
+	}
+	
+	// Draft notice
+	if ( response.have_draft && !readonly )
+	{
+		var dn = document.createElement('div');
+		dn.className = 'warning-box';
+		dn.id = 'ajax_edit_draft_notice';
+		dn.innerHTML = '<b>' + $lang.get('editor_msg_have_draft_title') + '</b><br />';
+		dn.innerHTML += $lang.get('editor_msg_have_draft_body', { author: response.draft_author, time: response.draft_time });
+	}
+	
+	// Old-revision notice
+	if ( revid > 0 )
+	{
+		var oldrev_box = document.createElement('div');
+		oldrev_box.className = 'usermessage';
+		oldrev_box.appendChild(document.createTextNode($lang.get('editor_msg_editing_old_revision')));
+	}
+	
+	// Preview holder
+	var preview_anchor = document.createElement('a');
+	preview_anchor.name = 'ajax_preview';
+	preview_anchor.id = 'ajax_preview';
+	var preview_container = document.createElement('div');
+	preview_container.id = 'enano_editor_preview';
+	preview_container.style.clear = 'left';
+	
+	// Textarea containing the content
+	var ta_wrapper = document.createElement('div');
+	ta_wrapper.style.margin = '10px 0';
+	// ta_wrapper.style.clear = 'both';
+	var textarea = document.createElement('textarea');
+	ta_wrapper.appendChild(textarea);
+	
+	textarea.id = 'ajaxEditArea';
+	textarea.rows = '20';
+	textarea.cols = '60';
+	textarea.style.width = '98.7%';
+	
+	// Revision metadata controls
+	var tblholder = document.createElement('div');
+	tblholder.className = 'tblholder';
+	var metatable = document.createElement('table');
+	metatable.setAttribute('border', '0');
+	metatable.setAttribute('cellspacing', '1');
+	metatable.setAttribute('cellpadding', '4');
+	
+	if ( readonly )
+	{
+		// Close Viewer button
+		var toolbar = '';
+		var head = new templateParser(response.toolbar_templates.toolbar_start);
+		var button = new templateParser(response.toolbar_templates.toolbar_button);
+		var tail = new templateParser(response.toolbar_templates.toolbar_end);
+		
+		toolbar += head.run();
+		
+		button.assign_bool({
+				show_title: true
+			});
+		
+		// Button: close
+		button.assign_vars({
+				TITLE: $lang.get('editor_btn_closeviewer'),
+				IMAGE: editor_img_path + '/discard.gif',
+				SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 16),
+				FLAGS: 'href="#" onclick="ajaxReset(true); return false;"'
+			});
+		toolbar += button.run();
+		toolbar += tail.run();
+	}
+	else
+	{
+		// First row: edit summary
+		var tr1 = document.createElement('tr');
+		var td1_1 = document.createElement('td');
+		var td1_2 = document.createElement('td');
+		td1_1.className = 'row2';
+		td1_2.className = 'row1';
+		td1_2.style.width = '70%';
+		td1_1.appendChild(document.createTextNode($lang.get('editor_lbl_edit_summary')));
+		td1_1.appendChild(document.createElement('br'));
+		var small = document.createElement('small');
+		small.appendChild(document.createTextNode($lang.get('editor_lbl_edit_summary_explain')));
+		td1_1.appendChild(small);
+		
+		var field_es = document.createElement('input');
+		field_es.id = 'enano_editor_field_summary';
+		field_es.type = 'text';
+		field_es.size = '40';
+		field_es.style.width = '96%';
+		
+		if ( revid > 0 )
+		{
+			undo_info.last_rev_id = revid;
+			field_es.value = $lang.get('editor_reversion_edit_summary', undo_info);
+		}
+		
+		td1_2.appendChild(field_es);
+		
+		tr1.appendChild(td1_1);
+		tr1.appendChild(td1_2);
+		
+		// Second row: minor edit
+		var tr2 = document.createElement('tr');
+		var td2_1 = document.createElement('td');
+		var td2_2 = document.createElement('td');
+		td2_1.className = 'row2';
+		td2_2.className = 'row1';
+		td2_1.appendChild(document.createTextNode($lang.get('editor_lbl_minor_edit')));
+		td2_1.appendChild(document.createElement('br'));
+		var small = document.createElement('small');
+		small.appendChild(document.createTextNode($lang.get('editor_lbl_minor_edit_explain')));
+		td2_1.appendChild(small);
+		
+		var label = document.createElement('label');
+		var field_mi = document.createElement('input');
+		field_mi.id = 'enano_editor_field_minor';
+		field_mi.type = 'checkbox';
+		label.appendChild(field_mi);
+		label.appendChild(document.createTextNode(' '));
+		label.appendChild(document.createTextNode($lang.get('editor_lbl_minor_edit_field')));
+		td2_2.appendChild(label);
+		
+		tr2.appendChild(td2_1);
+		tr2.appendChild(td2_2);
+		
+		if ( captcha_hash )
+		{
+			// generate captcha field (effectively third row)
+			var tr4 = document.createElement('tr');
+			var td4_1 = document.createElement('td');
+			var td4_2 = document.createElement('td');
+			td4_1.className = 'row2';
+			td4_2.className = 'row1';
+			
+			td4_1.appendChild(document.createTextNode($lang.get('editor_lbl_field_captcha')));
+			td4_1.appendChild(document.createElement('br'));
+			var small2 = document.createElement('small');
+			small2.appendChild(document.createTextNode($lang.get('editor_msg_captcha_pleaseenter')));
+			small2.appendChild(document.createElement('br'));
+			small2.appendChild(document.createElement('br'));
+			small2.appendChild(document.createTextNode($lang.get('editor_msg_captcha_blind')));
+			td4_1.appendChild(small2);
+			
+			var img = document.createElement('img');
+			img.src = makeUrlNS('Special', 'Captcha/' + captcha_hash);
+			img.setAttribute('enano:captcha_hash', captcha_hash);
+			img.id = 'enano_editor_captcha_img';
+			img.onclick = function()
+			{
+				this.src = makeUrlNS('Special', 'Captcha/' + this.getAttribute('enano:captcha_hash') + '/' + Math.floor(Math.random() * 100000));
+			}
+			img.style.cursor = 'pointer';
+			td4_2.appendChild(img);
+			td4_2.appendChild(document.createElement('br'));
+			td4_2.appendChild(document.createTextNode($lang.get('editor_lbl_field_captcha_code') + ' '));
+			var input = document.createElement('input');
+			input.type = 'text';
+			input.id = 'enano_editor_field_captcha';
+			input.setAttribute('enano:captcha_hash', captcha_hash);
+			input.size = '9';
+			td4_2.appendChild(input);
+			
+			tr4.appendChild(td4_1);
+			tr4.appendChild(td4_2);
+		}
+		
+		// Third row: controls
+		
+		var toolbar = '';
+		var head = new templateParser(response.toolbar_templates.toolbar_start);
+		var button = new templateParser(response.toolbar_templates.toolbar_button);
+		var label = new templateParser(response.toolbar_templates.toolbar_label);
+		var tail = new templateParser(response.toolbar_templates.toolbar_end);
+		
+		button.assign_bool({
+				show_title: true
+			});
+		
+		toolbar += head.run();
+		
+		// Button: Save
+		button.assign_vars({
+				TITLE: $lang.get('editor_btn_save'),
+				IMAGE: editor_img_path + '/save.gif',
+				SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 64),
+				FLAGS: 'href="#" onclick="ajaxEditorSave(); return false;"'
+			});
+		toolbar += button.run();
+		
+		// Button: preview
+		button.assign_vars({
+				TITLE: $lang.get('editor_btn_preview'),
+				IMAGE: editor_img_path + '/preview.gif',
+				SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 32),
+				FLAGS: 'href="#" onclick="ajaxEditorGenPreview(); return false;"'
+			});
+		toolbar += button.run();
+		
+		// Button: revert
+		button.assign_vars({
+				TITLE: $lang.get('editor_btn_revert'),
+					IMAGE: editor_img_path + '/revert.gif',
+					SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 48),
+				FLAGS: 'href="#" onclick="ajaxEditorRevertToLatest(); return false;"'
+			});
+		toolbar += button.run();
+		
+		// Button: diff
+		button.assign_vars({
+				TITLE: $lang.get('editor_btn_diff'),
+				IMAGE: editor_img_path + '/diff.gif',
+				SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 0),
+				FLAGS: 'href="#" onclick="ajaxEditorShowDiffs(); return false;"'
+			});
+		toolbar += button.run();
+		
+		// Button: cancel
+		button.assign_vars({
+				TITLE: $lang.get('editor_btn_cancel'),
+				IMAGE: editor_img_path + '/discard.gif',
+				SPRITE: gen_sprite_html(editor_img_path + '/sprite.png', 16, 16, 0, 16),
+				FLAGS: 'href="#" onclick="ajaxEditorCancel(); return false;"'
+			});
+		toolbar += button.run();
+		
+		// Separator
+		label.assign_vars({
+				TITLE: ' '
+			});
+		toolbar += label.run();
+		
+		// Button: Save draft
+		button.assign_vars({
+				TITLE: $lang.get('editor_btn_savedraft'),
+				IMAGE: editor_img_path + '/savedraft.gif',
+				SPRITE: false,
+				FLAGS: 'href="#" onclick="ajaxPerformAutosave(); return false;" id="ajax_edit_savedraft_btn"'
+			});
+		toolbar += button.run();
+		
+		toolbar += tail.run();
+		
+		metatable.appendChild(tr1);
+		metatable.appendChild(tr2);
+		if ( captcha_hash )
+		{
+			metatable.appendChild(tr4);
+		}
+		// metatable.appendChild(tr3);
+	}
+	tblholder.appendChild(metatable);
+	
+	// Edit disclaimer/notice
+	if ( response.edit_notice )
+	{
+		var en_div = document.createElement('div');
+		en_div.innerHTML = response.edit_notice;
+		en_div.className = 'usermessage';
+		en_div.style.margin = '10px 0 0 0';
+	}
+	
+	// Put it all together...
+	form.appendChild(heading);
+	if ( allow_wysiwyg )
+		form.appendChild(toggler);
+	
+	if ( dn )
+		form.appendChild(dn);
+	
+	if ( oldrev_box )
+		form.appendChild(oldrev_box);
+	
+	form.appendChild(preview_anchor);
+	form.appendChild(preview_container);
+	form.appendChild(ta_wrapper);
+	if ( !readonly )
+		form.appendChild(tblholder);
+	form.innerHTML += '<div style="margin: 10px 0 0 0;">' + toolbar + '</div>';
+	edcon.appendChild(form);
+	
+	if ( response.edit_notice && !readonly )
+	{
+		edcon.appendChild(en_div);
+	}
+	
+	// more textarea attribs/init
+	var textarea = document.getElementById('ajaxEditArea');
+	textarea.as_last_save = 0;
+	textarea.content_orig = content;
+	textarea.used_draft = false;
+	textarea.onkeyup = function()
+	{
+		if ( this.needReset )
+		{
+			var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
+			var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
+			img.src = editor_img_path + '/savedraft.gif';
+			lbl.innerHTML = $lang.get('editor_btn_savedraft');
+		}
+		if ( window.AutosaveTimeoutObj )
+			clearTimeout(window.AutosaveTimeoutObj);
+		window.AutosaveTimeoutObj = setTimeout('ajaxAutosaveDraft();', ( AUTOSAVE_TIMEOUT * 1000 ));
+	}
+	
+	if ( readonly )
+	{
+		textarea.className = 'mce_readonly';
+		textarea.setAttribute('readonly', 'readonly');
+	}
+	
+	$dynano('ajaxEditArea').object.focus();
+	$dynano('ajaxEditArea').object._edTimestamp = timestamp;
+	$dynano('ajaxEditArea').setContent(content);
+	
+	// If the editor preference is tinymce, switch the editor to TinyMCE now
+	if ( response.page_format == 'xhtml' && allow_wysiwyg )
+	{
+		$dynano('ajaxEditArea').switchToMCE(false);
+	}
+	
+	if ( allow_wysiwyg )
+	{
+		var a = document.getElementById('enano_edit_btn_pt').getElementsByTagName('a')[0];
+		a.onclick = function() {
+			ajaxSetEditorPlain();
+			return false;
+		};
+		var a = document.getElementById('enano_edit_btn_mce').getElementsByTagName('a')[0];
+		a.onclick = function() {
+			ajaxSetEditorMCE();
+			return false;
+		};
+	}
+	
+	// if we're using the modal window, fade it in
+	if ( editor_use_modal_window )
+	{
+		domOpacity(edcon, 0, 100, 500);
+	}
+	
+	// Autosave every 5 minutes           (m  *  s  *  ms)
+	setInterval('ajaxPerformAutosave();', ( 5 * 60 * 1000 ));
 }
 
 window.ajaxEditorDestroyModalWindow = function()
 {
-  if ( editor_use_modal_window )
-  {
-    var edcon = document.getElementById('ajaxEditContainerModal');
-    var body = document.getElementsByTagName('body')[0];
-    if ( edcon )
-    {
-      body.removeChild(edcon);
-      enlighten(true, 'enano_editor_darkener');
-    }
-  }
+	if ( editor_use_modal_window )
+	{
+		var edcon = document.getElementById('ajaxEditContainerModal');
+		var body = document.getElementsByTagName('body')[0];
+		if ( edcon )
+		{
+			body.removeChild(edcon);
+			enlighten(true, 'enano_editor_darkener');
+		}
+	}
 }
 
 window.ajaxEditorSave = function(is_draft, text_override)
 {
-  if ( !is_draft )
-  {
-    ajaxSetEditorLoading();
-  }
-  if ( is_draft && editor_save_lock )
-    return false;
-  else
-    editor_save_lock = true;
-  
-  var ta_content = ( text_override ) ? text_override : $dynano('ajaxEditArea').getContent();
-  
-  if ( !is_draft && ( ta_content == '' || ta_content == '<p></p>' || ta_content == '<p>&nbsp;</p>' ) )
-  {
-    new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_no_text_title'), $lang.get('editor_err_no_text_body'));
-    ajaxUnSetEditorLoading();
-    return false;
-  }
-  
-  if ( is_draft )
-  {
-    // ajaxSetEditorLoading();
-    var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
-    var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
-    img.src = cdnPath + '/images/loading.gif';
-    var d = new Date();
-    var m = String(d.getMinutes());
-    if ( m.length < 2 )
-      m = '0' + m;
-    var time = d.getHours() + ':' + m;
-    lbl.innerHTML = $lang.get('editor_msg_draft_saving');
-  }
-  
-  var edit_summ = $dynano('enano_editor_field_summary').object.value;
-  if ( !edit_summ )
-    edit_summ = '';
-  var is_minor = ( $dynano('enano_editor_field_minor').object.checked ) ? 1 : 0;
-  var timestamp = $dynano('ajaxEditArea').object._edTimestamp;
-  var used_draft = $dynano('ajaxEditArea').object.used_draft;
-  
-  var json_packet = {
-    src: ta_content,
-    summary: edit_summ,
-    minor_edit: is_minor,
-    time: timestamp,
-    draft: ( is_draft == true ),
-    format: ( $dynano('ajaxEditArea').isMCE() ) ? 'xhtml' : 'wikitext',
-    used_draft: used_draft
-  };
-  
-  // Do we need to add captcha info?
-  if ( document.getElementById('enano_editor_field_captcha') && !is_draft )
-  {
-    var captcha_field = document.getElementById('enano_editor_field_captcha');
-    if ( captcha_field.value == '' )
-    {
-      new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_need_captcha_title'), $lang.get('editor_err_need_captcha_body'));
-      ajaxUnSetEditorLoading();
-      return false;
-    }
-    json_packet.captcha_code = captcha_field.value;
-    json_packet.captcha_id = captcha_field.getAttribute('enano:captcha_hash');
-  }
-  
-  json_packet = ajaxEscape(toJSONString(json_packet));
-  ajaxPost(stdAjaxPrefix + '&_mode=savepage_json', 'r=' + json_packet, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        ajaxUnSetEditorLoading();
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(response);
-          return false;
-        }
-        
-        response = parseJSON(response);
-        // This will only be used if there was a lower-level error.
-        if ( response.mode == 'error' )
-        {
-          editor_save_lock = false;
-          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
-          return false;
-        }
-        // This will be used if the PageProcessor generated errors (usually security/permissions related)
-        if ( response.mode == 'errors' )
-        {
-          editor_save_lock = false;
-          // This will be true if the user entered a captcha code incorrectly, thus
-          // invalidating the code and requiring a new image to be generated.
-          if ( response.new_captcha )
-          {
-            // Generate the new captcha field
-            var img = document.getElementById('enano_editor_captcha_img');
-            var input = document.getElementById('enano_editor_field_captcha');
-            if ( img && input )
-            {
-              img._captchaHash = response.new_captcha;
-              input._captchaHash = response.new_captcha;
-              img.src = makeUrlNS('Special', 'Captcha/' + response.new_captcha);
-              input.value = '';
-            }
-          }
-          var errors = '<ul><li>' + implode('</li><li>', response.errors) + '</li></ul>';
-          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_save_title'), $lang.get('editor_err_save_body') + errors);
-          return false;
-        }
-        // If someone else got to the page first, warn the user
-        if ( response.mode == 'obsolete' )
-        {
-          editor_save_lock = false;
-          // Update the local timestamp to allow override
-          $dynano('ajaxEditArea').object._edTimestamp = response.time;
-          new MessageBox(MB_OK | MB_ICONEXCLAMATION, $lang.get('editor_err_obsolete_title'), $lang.get('editor_err_obsolete_body', { author: response.author, timestamp: response.date_string, page_url: makeUrl(title, false, true) }));
-          return false;
-        }
-        if ( response.mode == 'success' )
-        {
-          if ( response.is_draft )
-          {
-            document.getElementById('ajaxEditArea').used_draft = true;
-            document.getElementById('ajaxEditArea').needReset = true;
-            var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
-            var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
-            if ( response.is_draft == 'delete' )
-            {
-              img.src = scriptPath + '/images/editor/savedraft.gif';
-              lbl.innerHTML = $lang.get('editor_btn_savedraft');
-              
-              var dn = $dynano('ajax_edit_draft_notice').object;
-              if ( dn )
-              {
-                dn.parentNode.removeChild(dn);
-              }
-            }
-            else
-            {
-              img.src = scriptPath + '/images/mini-info.png';
-              var d = new Date();
-              var m = String(d.getMinutes());
-              if ( m.length < 2 )
-                m = '0' + m;
-              var time = d.getHours() + ':' + m;
-              lbl.innerHTML = $lang.get('editor_msg_draft_saved', { time: time });
-            }
-            editor_save_lock = false;
-          }
-          else
-          {
-            // The save was successful; reset flags and make another request for the new page content
-            setAjaxLoading();
-            editor_open = false;
-            editor_save_lock = false;
-            enableUnload();
-            $dynano('ajaxEditArea').destroyMCE(false);
-            changeOpac(0, 'ajaxEditContainer');
-            ajaxGet(stdAjaxPrefix + '&_mode=getpage&noheaders', function(ajax)
-              {
-                if ( ajax.readyState == 4 && ajax.status == 200 )
-                {
-                  unsetAjaxLoading();
-                  selectButtonMajor('article');
-                  unselectAllButtonsMinor();
-                  
-                  ajaxEditorDestroyModalWindow();
-                  document.getElementById('ajaxEditContainer').innerHTML = '<div class="usermessage">' + $lang.get('editor_msg_saved') + '</div>' + ajax.responseText;
-                  // if we're on a userpage, call the onload function to rebuild the tabs
-                  if ( typeof(userpage_onload) == 'function' )
-                  {
-                    window.userpage_blocks = [];
-                    userpage_onload();
-                  }
-                  opacity('ajaxEditContainer', 0, 100, 1000);
-                }
-              });
-          }
-        }
-      }
-    }, true);
+	if ( !is_draft )
+	{
+		ajaxSetEditorLoading();
+	}
+	if ( is_draft && editor_save_lock )
+		return false;
+	else
+		editor_save_lock = true;
+	
+	var ta_content = ( text_override ) ? text_override : $dynano('ajaxEditArea').getContent();
+	
+	if ( !is_draft && ( ta_content == '' || ta_content == '<p></p>' || ta_content == '<p>&nbsp;</p>' ) )
+	{
+		new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_no_text_title'), $lang.get('editor_err_no_text_body'));
+		ajaxUnSetEditorLoading();
+		return false;
+	}
+	
+	if ( is_draft )
+	{
+		// ajaxSetEditorLoading();
+		var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
+		var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
+		img.src = cdnPath + '/images/loading.gif';
+		var d = new Date();
+		var m = String(d.getMinutes());
+		if ( m.length < 2 )
+			m = '0' + m;
+		var time = d.getHours() + ':' + m;
+		lbl.innerHTML = $lang.get('editor_msg_draft_saving');
+	}
+	
+	var edit_summ = $dynano('enano_editor_field_summary').object.value;
+	if ( !edit_summ )
+		edit_summ = '';
+	var is_minor = ( $dynano('enano_editor_field_minor').object.checked ) ? 1 : 0;
+	var timestamp = $dynano('ajaxEditArea').object._edTimestamp;
+	var used_draft = $dynano('ajaxEditArea').object.used_draft;
+	
+	var json_packet = {
+		src: ta_content,
+		summary: edit_summ,
+		minor_edit: is_minor,
+		time: timestamp,
+		draft: ( is_draft == true ),
+		format: ( $dynano('ajaxEditArea').isMCE() ) ? 'xhtml' : 'wikitext',
+		used_draft: used_draft
+	};
+	
+	// Do we need to add captcha info?
+	if ( document.getElementById('enano_editor_field_captcha') && !is_draft )
+	{
+		var captcha_field = document.getElementById('enano_editor_field_captcha');
+		if ( captcha_field.value == '' )
+		{
+			new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('editor_err_need_captcha_title'), $lang.get('editor_err_need_captcha_body'));
+			ajaxUnSetEditorLoading();
+			return false;
+		}
+		json_packet.captcha_code = captcha_field.value;
+		json_packet.captcha_id = captcha_field.getAttribute('enano:captcha_hash');
+	}
+	
+	json_packet = ajaxEscape(toJSONString(json_packet));
+	ajaxPost(stdAjaxPrefix + '&_mode=savepage_json', 'r=' + json_packet, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				ajaxUnSetEditorLoading();
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(response);
+					return false;
+				}
+				
+				response = parseJSON(response);
+				// This will only be used if there was a lower-level error.
+				if ( response.mode == 'error' )
+				{
+					editor_save_lock = false;
+					new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
+					return false;
+				}
+				// This will be used if the PageProcessor generated errors (usually security/permissions related)
+				if ( response.mode == 'errors' )
+				{
+					editor_save_lock = false;
+					// This will be true if the user entered a captcha code incorrectly, thus
+					// invalidating the code and requiring a new image to be generated.
+					if ( response.new_captcha )
+					{
+						// Generate the new captcha field
+						var img = document.getElementById('enano_editor_captcha_img');
+						var input = document.getElementById('enano_editor_field_captcha');
+						if ( img && input )
+						{
+							img._captchaHash = response.new_captcha;
+							input._captchaHash = response.new_captcha;
+							img.src = makeUrlNS('Special', 'Captcha/' + response.new_captcha);
+							input.value = '';
+						}
+					}
+					var errors = '<ul><li>' + implode('</li><li>', response.errors) + '</li></ul>';
+					new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_save_title'), $lang.get('editor_err_save_body') + errors);
+					return false;
+				}
+				// If someone else got to the page first, warn the user
+				if ( response.mode == 'obsolete' )
+				{
+					editor_save_lock = false;
+					// Update the local timestamp to allow override
+					$dynano('ajaxEditArea').object._edTimestamp = response.time;
+					new MessageBox(MB_OK | MB_ICONEXCLAMATION, $lang.get('editor_err_obsolete_title'), $lang.get('editor_err_obsolete_body', { author: response.author, timestamp: response.date_string, page_url: makeUrl(title, false, true) }));
+					return false;
+				}
+				if ( response.mode == 'success' )
+				{
+					if ( response.is_draft )
+					{
+						document.getElementById('ajaxEditArea').used_draft = true;
+						document.getElementById('ajaxEditArea').needReset = true;
+						var img = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('img')[0];
+						var lbl = $dynano('ajax_edit_savedraft_btn').object.getElementsByTagName('span')[0];
+						if ( response.is_draft == 'delete' )
+						{
+							img.src = scriptPath + '/images/editor/savedraft.gif';
+							lbl.innerHTML = $lang.get('editor_btn_savedraft');
+							
+							var dn = $dynano('ajax_edit_draft_notice').object;
+							if ( dn )
+							{
+								dn.parentNode.removeChild(dn);
+							}
+						}
+						else
+						{
+							img.src = scriptPath + '/images/mini-info.png';
+							var d = new Date();
+							var m = String(d.getMinutes());
+							if ( m.length < 2 )
+								m = '0' + m;
+							var time = d.getHours() + ':' + m;
+							lbl.innerHTML = $lang.get('editor_msg_draft_saved', { time: time });
+						}
+						editor_save_lock = false;
+					}
+					else
+					{
+						// The save was successful; reset flags and make another request for the new page content
+						setAjaxLoading();
+						editor_open = false;
+						editor_save_lock = false;
+						enableUnload();
+						$dynano('ajaxEditArea').destroyMCE(false);
+						changeOpac(0, 'ajaxEditContainer');
+						ajaxGet(stdAjaxPrefix + '&_mode=getpage&noheaders', function(ajax)
+							{
+								if ( ajax.readyState == 4 && ajax.status == 200 )
+								{
+									unsetAjaxLoading();
+									selectButtonMajor('article');
+									unselectAllButtonsMinor();
+									
+									ajaxEditorDestroyModalWindow();
+									document.getElementById('ajaxEditContainer').innerHTML = '<div class="usermessage">' + $lang.get('editor_msg_saved') + '</div>' + ajax.responseText;
+									// if we're on a userpage, call the onload function to rebuild the tabs
+									if ( typeof(userpage_onload) == 'function' )
+									{
+										window.userpage_blocks = [];
+										userpage_onload();
+									}
+									opacity('ajaxEditContainer', 0, 100, 1000);
+								}
+							});
+					}
+				}
+			}
+		}, true);
 }
 
 // Delete the draft (this is a massive server-side hack)
 window.ajaxEditorDeleteDraft = function()
 {
-  miniPromptMessage({
-      title: $lang.get('editor_msg_confirm_delete_draft_title'),
-      message: $lang.get('editor_msg_confirm_delete_draft_body'),
-      buttons: [
-          {
-            text: $lang.get('editor_btn_delete_draft'),
-            color: 'red',
-            style: {
-              fontWeight: 'bold'
-            },
-            onclick: function() {
-              ajaxEditorDeleteDraftReal();
-              miniPromptDestroy(this);
-            }
-          },
-          {
-            text: $lang.get('etc_cancel'),
-            onclick: function() {
-              miniPromptDestroy(this);
-            }
-          }
-        ]
-    });
+	miniPromptMessage({
+			title: $lang.get('editor_msg_confirm_delete_draft_title'),
+			message: $lang.get('editor_msg_confirm_delete_draft_body'),
+			buttons: [
+					{
+						text: $lang.get('editor_btn_delete_draft'),
+						color: 'red',
+						style: {
+							fontWeight: 'bold'
+						},
+						onclick: function() {
+							ajaxEditorDeleteDraftReal();
+							miniPromptDestroy(this);
+						}
+					},
+					{
+						text: $lang.get('etc_cancel'),
+						onclick: function() {
+							miniPromptDestroy(this);
+						}
+					}
+				]
+		});
 }
 
 window.ajaxEditorDeleteDraftReal = function()
 {
-  return ajaxEditorSave(true, -1);
+	return ajaxEditorSave(true, -1);
 }
 
 window.ajaxEditorGenPreview = function()
 {
-  ajaxSetEditorLoading();
-  var ta_content = $dynano('ajaxEditArea').getContent();
-  ta_content = ajaxEscape(ta_content);
-  if ( $dynano('enano_editor_preview').object.innerHTML != '' )
-  {
-    opacity('enano_editor_preview', 100, 0, 500);
-  }
-  ajaxPost(stdAjaxPrefix + '&_mode=preview', 'text=' + ta_content, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        ajaxUnSetEditorLoading();
-        changeOpac(0, 'enano_editor_preview');
-        $dynano('enano_editor_preview').object.innerHTML = ajax.responseText;
-        window.location.hash = '#ajax_preview';
-        opacity('enano_editor_preview', 0, 100, 500);
-      }
-    }, true);
+	ajaxSetEditorLoading();
+	var ta_content = $dynano('ajaxEditArea').getContent();
+	ta_content = ajaxEscape(ta_content);
+	if ( $dynano('enano_editor_preview').object.innerHTML != '' )
+	{
+		opacity('enano_editor_preview', 100, 0, 500);
+	}
+	ajaxPost(stdAjaxPrefix + '&_mode=preview', 'text=' + ta_content, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				ajaxUnSetEditorLoading();
+				changeOpac(0, 'enano_editor_preview');
+				$dynano('enano_editor_preview').object.innerHTML = ajax.responseText;
+				window.location.hash = '#ajax_preview';
+				opacity('enano_editor_preview', 0, 100, 500);
+			}
+		}, true);
 }
 
 window.ajaxEditorRevertToLatest = function()
 {
-  miniPromptMessage({
-      title: $lang.get('editor_msg_revert_confirm_title'),
-      message: $lang.get('editor_msg_revert_confirm_body'),
-      buttons: [
-        {
-          text: $lang.get('editor_btn_revert_confirm'),
-          color: 'red',
-          sprite: [ editor_img_path + '/sprite.png', 16, 16, 0, 48 ],
-          style: {
-            fontWeight: 'bold'
-          },
-          onclick: function()
-          {
-            ajaxEditorRevertToLatestReal();
-            miniPromptDestroy(this);
-            return false;
-          }
-        },
-        {
-          text: $lang.get('etc_cancel'),
-          onclick: function()
-          {
-            miniPromptDestroy(this);
-            return false;
-          }
-        }
-      ]
-    });
+	miniPromptMessage({
+			title: $lang.get('editor_msg_revert_confirm_title'),
+			message: $lang.get('editor_msg_revert_confirm_body'),
+			buttons: [
+				{
+					text: $lang.get('editor_btn_revert_confirm'),
+					color: 'red',
+					sprite: [ editor_img_path + '/sprite.png', 16, 16, 0, 48 ],
+					style: {
+						fontWeight: 'bold'
+					},
+					onclick: function()
+					{
+						ajaxEditorRevertToLatestReal();
+						miniPromptDestroy(this);
+						return false;
+					}
+				},
+				{
+					text: $lang.get('etc_cancel'),
+					onclick: function()
+					{
+						miniPromptDestroy(this);
+						return false;
+					}
+				}
+			]
+		});
 }
 
 window.ajaxEditorRevertToLatestReal = function()
 {
-  ajaxSetEditorLoading();
-  ajaxGet(stdAjaxPrefix + '&_mode=getsource', function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        ajaxUnSetEditorLoading();
-        
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(response);
-          return false;
-        }
-        
-        response = parseJSON(response);
-        if ( response.mode == 'error' )
-        {
-          unselectAllButtonsMinor();
-          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
-          return false;
-        }
-        
-        if ( !response.auth_view_source )
-        {
-          unselectAllButtonsMinor();
-          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_access_denied_title'), $lang.get('editor_err_access_denied_body'));
-          return false;
-        }
-        
-        setTimeout(function()
-          {
-            editor_convert_if_needed(response.page_format);
-            $dynano('ajaxEditArea').setContent(response.src);
-          }, aclDisableTransitionFX ? 10 : 750);
-      }
-    }, true);
+	ajaxSetEditorLoading();
+	ajaxGet(stdAjaxPrefix + '&_mode=getsource', function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				ajaxUnSetEditorLoading();
+				
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(response);
+					return false;
+				}
+				
+				response = parseJSON(response);
+				if ( response.mode == 'error' )
+				{
+					unselectAllButtonsMinor();
+					new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
+					return false;
+				}
+				
+				if ( !response.auth_view_source )
+				{
+					unselectAllButtonsMinor();
+					new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_access_denied_title'), $lang.get('editor_err_access_denied_body'));
+					return false;
+				}
+				
+				setTimeout(function()
+					{
+						editor_convert_if_needed(response.page_format);
+						$dynano('ajaxEditArea').setContent(response.src);
+					}, aclDisableTransitionFX ? 10 : 750);
+			}
+		}, true);
 }
 
 window.ajaxEditorShowDiffs = function()
 {
-  ajaxSetEditorLoading();
-  var ta_content = $dynano('ajaxEditArea').getContent();
-  ta_content = ajaxEscape(ta_content);
-  if ( $dynano('enano_editor_preview').object.innerHTML != '' )
-  {
-    opacity('enano_editor_preview', 100, 0, 500);
-  }
-  ajaxPost(stdAjaxPrefix + '&_mode=diff_cur', 'text=' + ta_content, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        ajaxUnSetEditorLoading();
-        changeOpac(0, 'enano_editor_preview');
-        $dynano('enano_editor_preview').object.innerHTML = ajax.responseText;
-        window.location.hash = '#ajax_preview';
-        opacity('enano_editor_preview', 0, 100, 500);
-      }
-    }, true);
+	ajaxSetEditorLoading();
+	var ta_content = $dynano('ajaxEditArea').getContent();
+	ta_content = ajaxEscape(ta_content);
+	if ( $dynano('enano_editor_preview').object.innerHTML != '' )
+	{
+		opacity('enano_editor_preview', 100, 0, 500);
+	}
+	ajaxPost(stdAjaxPrefix + '&_mode=diff_cur', 'text=' + ta_content, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				ajaxUnSetEditorLoading();
+				changeOpac(0, 'enano_editor_preview');
+				$dynano('enano_editor_preview').object.innerHTML = ajax.responseText;
+				window.location.hash = '#ajax_preview';
+				opacity('enano_editor_preview', 0, 100, 500);
+			}
+		}, true);
 }
 
 window.ajaxEditorCancel = function()
 {
-  miniPromptMessage({
-      title: $lang.get('editor_msg_cancel_confirm_title'),
-      message: $lang.get('editor_msg_cancel_confirm_body'),
-      buttons: [
-        {
-          text: $lang.get('editor_btn_cancel_confirm'),
-          color: 'red',
-          sprite: [ editor_img_path + '/sprite.png', 16, 16, 0, 16 ],
-          style: {
-            fontWeight: 'bold'
-          },
-          onclick: function()
-          {
-            setAjaxLoading();
-            ajaxEditorDestroyModalWindow();
-            editor_open = false;
-            enableUnload();
-            $dynano('ajaxEditArea').destroyMCE(false);
-            ajaxReset();
-            miniPromptDestroy(this);
-            return false;
-          }
-        },
-        {
-          text: $lang.get('editor_btn_cancel_cancel'),
-          onclick: function()
-          {
-            miniPromptDestroy(this);
-            return false;
-          }
-        }
-      ]
-    });
+	miniPromptMessage({
+			title: $lang.get('editor_msg_cancel_confirm_title'),
+			message: $lang.get('editor_msg_cancel_confirm_body'),
+			buttons: [
+				{
+					text: $lang.get('editor_btn_cancel_confirm'),
+					color: 'red',
+					sprite: [ editor_img_path + '/sprite.png', 16, 16, 0, 16 ],
+					style: {
+						fontWeight: 'bold'
+					},
+					onclick: function()
+					{
+						setAjaxLoading();
+						ajaxEditorDestroyModalWindow();
+						editor_open = false;
+						enableUnload();
+						$dynano('ajaxEditArea').destroyMCE(false);
+						ajaxReset();
+						miniPromptDestroy(this);
+						return false;
+					}
+				},
+				{
+					text: $lang.get('editor_btn_cancel_cancel'),
+					onclick: function()
+					{
+						miniPromptDestroy(this);
+						return false;
+					}
+				}
+			]
+		});
 }
 
 window.ajaxSetEditorMCE = function(confirmed)
 {
-  if ( editor_loading )
-    return false;
-  
-  if ( !confirmed )
-  {
-    miniPromptMessage({
-        title: $lang.get('editor_msg_convert_confirm_title'),
-        message: $lang.get('editor_msg_convert_confirm_body'),
-        buttons: [
-          {
-            color: 'blue',
-            text: $lang.get('editor_btn_graphical'),
-            style: {
-              fontWeight: 'bold'
-            },
-            sprite: [ editor_img_path + '/sprite.png', 16, 16, 0, 112 ],
-            onclick: function()
-            {
-              ajaxSetEditorMCE(true);
-              miniPromptDestroy(this);
-              return false;
-            }
-          },
-          {
-            text: $lang.get('etc_cancel'),
-            onclick: function()
-            {
-              miniPromptDestroy(this);
-              return false;
-            }
-          }
-        ]
-      });
-    return false;
-  }
-  
-  // Clear out existing buttons
-  var span_wiki = $dynano('enano_edit_btn_pt').object;
-  var span_mce  = $dynano('enano_edit_btn_mce').object;
-  span_wiki.style.display = 'inline';
-  span_mce.style.display = 'none';
-  
-  // Swap editor
-  $dynano('ajaxEditArea').switchToMCE(true);
+	if ( editor_loading )
+		return false;
+	
+	if ( !confirmed )
+	{
+		miniPromptMessage({
+				title: $lang.get('editor_msg_convert_confirm_title'),
+				message: $lang.get('editor_msg_convert_confirm_body'),
+				buttons: [
+					{
+						color: 'blue',
+						text: $lang.get('editor_btn_graphical'),
+						style: {
+							fontWeight: 'bold'
+						},
+						sprite: [ editor_img_path + '/sprite.png', 16, 16, 0, 112 ],
+						onclick: function()
+						{
+							ajaxSetEditorMCE(true);
+							miniPromptDestroy(this);
+							return false;
+						}
+					},
+					{
+						text: $lang.get('etc_cancel'),
+						onclick: function()
+						{
+							miniPromptDestroy(this);
+							return false;
+						}
+					}
+				]
+			});
+		return false;
+	}
+	
+	// Clear out existing buttons
+	var span_wiki = $dynano('enano_edit_btn_pt').object;
+	var span_mce  = $dynano('enano_edit_btn_mce').object;
+	span_wiki.style.display = 'inline';
+	span_mce.style.display = 'none';
+	
+	// Swap editor
+	$dynano('ajaxEditArea').switchToMCE(true);
 }
 
 window.ajaxSetEditorPlain = function(confirmed)
 {
-  if ( editor_loading )
-    return false;
-  
-  if ( !confirmed )
-  {
-    miniPromptMessage({
-        title: $lang.get('editor_msg_convert_confirm_title'),
-        message: $lang.get('editor_msg_convert_confirm_body'),
-        buttons: [
-          {
-            color: 'green',
-            text: $lang.get('editor_btn_wikitext'),
-            style: {
-              fontWeight: 'bold'
-            },
-            sprite: [ editor_img_path + '/sprite.png', 16, 16, 0, 96 ],
-            onclick: function()
-            {
-              ajaxSetEditorPlain(true);
-              miniPromptDestroy(this);
-              return false;
-            }
-          },
-          {
-            text: $lang.get('etc_cancel'),
-            onclick: function()
-            {
-              miniPromptDestroy(this);
-              return false;
-            }
-          }
-        ]
-      });
-    return false;
-  }
-  
-  // Clear out existing buttons
-  var span_wiki = $dynano('enano_edit_btn_pt').object;
-  var span_mce  = $dynano('enano_edit_btn_mce').object;
-  span_wiki.style.display = 'none';
-  span_mce.style.display = 'inline';
-  
-  // Swap editor
-  $dynano('ajaxEditArea').destroyMCE(true);
+	if ( editor_loading )
+		return false;
+	
+	if ( !confirmed )
+	{
+		miniPromptMessage({
+				title: $lang.get('editor_msg_convert_confirm_title'),
+				message: $lang.get('editor_msg_convert_confirm_body'),
+				buttons: [
+					{
+						color: 'green',
+						text: $lang.get('editor_btn_wikitext'),
+						style: {
+							fontWeight: 'bold'
+						},
+						sprite: [ editor_img_path + '/sprite.png', 16, 16, 0, 96 ],
+						onclick: function()
+						{
+							ajaxSetEditorPlain(true);
+							miniPromptDestroy(this);
+							return false;
+						}
+					},
+					{
+						text: $lang.get('etc_cancel'),
+						onclick: function()
+						{
+							miniPromptDestroy(this);
+							return false;
+						}
+					}
+				]
+			});
+		return false;
+	}
+	
+	// Clear out existing buttons
+	var span_wiki = $dynano('enano_edit_btn_pt').object;
+	var span_mce  = $dynano('enano_edit_btn_mce').object;
+	span_wiki.style.display = 'none';
+	span_mce.style.display = 'inline';
+	
+	// Swap editor
+	$dynano('ajaxEditArea').destroyMCE(true);
 }
 
 var editor_loading = false;
 
 window.ajaxSetEditorLoading = function()
 {
-  var ed = false;
-  if ( window.tinyMCE )
-  {
-    ed = tinyMCE.get('ajaxEditArea');
-  }
-  editor_loading = true;
-  if ( ed )
-  {
-    ed.setProgressState(1);
-  }
-  else
-  {
-    ed = document.getElementById('ajaxEditArea');
-    var blackout = document.createElement('div');
-    blackout.style.position = 'absolute';
-    blackout.style.top = $dynano('ajaxEditArea').Top() + 'px';
-    blackout.style.left = $dynano('ajaxEditArea').Left() + 'px';
-    blackout.style.width = $dynano('ajaxEditArea').Width() + 'px';
-    blackout.style.height = $dynano('ajaxEditArea').Height() + 'px';
-    blackout.style.backgroundColor = '#FFFFFF';
-    domObjChangeOpac(60, blackout);
-    blackout.style.backgroundImage = 'url(' + scriptPath + '/includes/clientside/tinymce/themes/advanced/skins/default/img/progress.gif)';
-    blackout.style.backgroundPosition = 'center center';
-    blackout.style.backgroundRepeat = 'no-repeat';
-    blackout.id = 'enano_editor_blackout';
-    blackout.style.zIndex = getHighestZ() + 2;
-    
-    var body = document.getElementsByTagName('body')[0];
-    body.appendChild(blackout);
-  }
+	var ed = false;
+	if ( window.tinyMCE )
+	{
+		ed = tinyMCE.get('ajaxEditArea');
+	}
+	editor_loading = true;
+	if ( ed )
+	{
+		ed.setProgressState(1);
+	}
+	else
+	{
+		ed = document.getElementById('ajaxEditArea');
+		var blackout = document.createElement('div');
+		blackout.style.position = 'absolute';
+		blackout.style.top = $dynano('ajaxEditArea').Top() + 'px';
+		blackout.style.left = $dynano('ajaxEditArea').Left() + 'px';
+		blackout.style.width = $dynano('ajaxEditArea').Width() + 'px';
+		blackout.style.height = $dynano('ajaxEditArea').Height() + 'px';
+		blackout.style.backgroundColor = '#FFFFFF';
+		domObjChangeOpac(60, blackout);
+		blackout.style.backgroundImage = 'url(' + scriptPath + '/includes/clientside/tinymce/themes/advanced/skins/default/img/progress.gif)';
+		blackout.style.backgroundPosition = 'center center';
+		blackout.style.backgroundRepeat = 'no-repeat';
+		blackout.id = 'enano_editor_blackout';
+		blackout.style.zIndex = getHighestZ() + 2;
+		
+		var body = document.getElementsByTagName('body')[0];
+		body.appendChild(blackout);
+	}
 }
 
 window.ajaxUnSetEditorLoading = function()
 {
-  editor_loading = false;
-  var ed = false;
-  if ( window.tinyMCE )
-  {
-    ed = tinyMCE.get('ajaxEditArea');
-  }
-  if ( ed )
-  {
-    ed.setProgressState(0);
-  }
-  else
-  {
-    var blackout = document.getElementById('enano_editor_blackout');
-    var body = document.getElementsByTagName('body')[0];
-    if ( !blackout )
-      return false;
-    body.removeChild(blackout);
-  }
+	editor_loading = false;
+	var ed = false;
+	if ( window.tinyMCE )
+	{
+		ed = tinyMCE.get('ajaxEditArea');
+	}
+	if ( ed )
+	{
+		ed.setProgressState(0);
+	}
+	else
+	{
+		var blackout = document.getElementById('enano_editor_blackout');
+		var body = document.getElementsByTagName('body')[0];
+		if ( !blackout )
+			return false;
+		body.removeChild(blackout);
+	}
 }
 
 window.ajaxAutosaveDraft = function()
 {
-  var aed = document.getElementById('ajaxEditArea');
-  if ( !aed )
-    return false;
-  var last_save = aed.as_last_save;
-  var now = unix_time();
-  if ( ( last_save + 120 ) < now && aed.value != aed.content_orig )
-  {
-    ajaxPerformAutosave();
-  }
+	var aed = document.getElementById('ajaxEditArea');
+	if ( !aed )
+		return false;
+	var last_save = aed.as_last_save;
+	var now = unix_time();
+	if ( ( last_save + 120 ) < now && aed.value != aed.content_orig )
+	{
+		ajaxPerformAutosave();
+	}
 }
 
 window.ajaxPerformAutosave = function()
 {
-  var aed = document.getElementById('ajaxEditArea');
-  if ( !aed )
-    return false;
-  var now = unix_time();
-  aed.as_last_save = now;
-  
-  var ta_content = $dynano('ajaxEditArea').getContent();
-  
-  if ( ta_content == '' || ta_content == '<p></p>' || ta_content == '<p>&nbsp;</p>' )
-  {
-    return false;
-  }
-  
-  ajaxEditorSave(true);
+	var aed = document.getElementById('ajaxEditArea');
+	if ( !aed )
+		return false;
+	var now = unix_time();
+	aed.as_last_save = now;
+	
+	var ta_content = $dynano('ajaxEditArea').getContent();
+	
+	if ( ta_content == '' || ta_content == '<p></p>' || ta_content == '<p>&nbsp;</p>' )
+	{
+		return false;
+	}
+	
+	ajaxEditorSave(true);
 }
 
 window.ajaxEditorUseDraft = function()
 {
-  var aed = document.getElementById('ajaxEditArea');
-  if ( !aed )
-    return false;
-  ajaxSetEditorLoading();
-  ajaxGet(stdAjaxPrefix + '&_mode=getsource&get_draft=1', function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        ajaxUnSetEditorLoading();
-        
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(response);
-          return false;
-        }
-        
-        response = parseJSON(response);
-        if ( response.mode == 'error' )
-        {
-          unselectAllButtonsMinor();
-          new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
-          return false;
-        }
-        
-        editor_convert_if_needed(response.page_format);
-        
-        $dynano('ajaxEditArea').setContent(response.src);
-        $dynano('ajaxEditArea').object.used_draft = true;
-        
-        var es = document.getElementById('enano_editor_field_summary');
-        if ( es.value == '' )
-        {
-          es.value = response.edit_summary;
-        }
-        
-        var dn = $dynano('ajax_edit_draft_notice').object;
-        dn.parentNode.removeChild(dn);
-      }
-    }, true);
+	var aed = document.getElementById('ajaxEditArea');
+	if ( !aed )
+		return false;
+	ajaxSetEditorLoading();
+	ajaxGet(stdAjaxPrefix + '&_mode=getsource&get_draft=1', function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				ajaxUnSetEditorLoading();
+				
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(response);
+					return false;
+				}
+				
+				response = parseJSON(response);
+				if ( response.mode == 'error' )
+				{
+					unselectAllButtonsMinor();
+					new MessageBox(MB_OK | MB_ICONSTOP, $lang.get('editor_err_server'), response.error);
+					return false;
+				}
+				
+				editor_convert_if_needed(response.page_format);
+				
+				$dynano('ajaxEditArea').setContent(response.src);
+				$dynano('ajaxEditArea').object.used_draft = true;
+				
+				var es = document.getElementById('enano_editor_field_summary');
+				if ( es.value == '' )
+				{
+					es.value = response.edit_summary;
+				}
+				
+				var dn = $dynano('ajax_edit_draft_notice').object;
+				dn.parentNode.removeChild(dn);
+			}
+		}, true);
 }
 
 window.editor_convert_if_needed = function(targetformat, noticetitle, noticebody)
 {
-  // Do we need to change the format?
-  var need_to_mce = ( targetformat == 'xhtml' && !$dynano('ajaxEditArea').isMCE() );
-  var need_to_wkt = ( targetformat == 'wikitext' && $dynano('ajaxEditArea').isMCE() );
-  if ( need_to_mce )
-  {
-    $dynano('ajaxEditArea').setContent('');
-    $dynano('ajaxEditArea').switchToMCE(false);
-    
-    // Clear out existing buttons
-    var span_wiki = $dynano('enano_edit_btn_pt').object;
-    var span_mce  = $dynano('enano_edit_btn_mce').object;
-    span_wiki.style.display = 'inline';
-    span_mce.style.display = 'none';
-  }
-  else if ( need_to_wkt )
-  {
-    $dynano('ajaxEditArea').setContent('');
-    $dynano('ajaxEditArea').destroyMCE(false);
-    
-    // Clear out existing buttons
-    var span_wiki = $dynano('enano_edit_btn_pt').object;
-    var span_mce  = $dynano('enano_edit_btn_mce').object;
-    span_wiki.style.display = 'none';
-    span_mce.style.display = 'inline';
-  }
-  if ( need_to_mce || need_to_wkt )
-  {
-    // explain the conversion
-    if ( !noticetitle )
-      noticetitle = 'editor_msg_convert_draft_load_title';
-    if ( !noticebody )
-      noticebody = 'editor_msg_convert_draft_load_body';
-    
-    miniPromptMessage({
-        title: $lang.get(noticetitle),
-        message: $lang.get(noticebody),
-        buttons: [
-          {
-            text: $lang.get('etc_ok'),
-            onclick: function()
-            {
-              miniPromptDestroy(this);
-              return false;
-            }
-          }
-        ]
-      });
-  }
+	// Do we need to change the format?
+	var need_to_mce = ( targetformat == 'xhtml' && !$dynano('ajaxEditArea').isMCE() );
+	var need_to_wkt = ( targetformat == 'wikitext' && $dynano('ajaxEditArea').isMCE() );
+	if ( need_to_mce )
+	{
+		$dynano('ajaxEditArea').setContent('');
+		$dynano('ajaxEditArea').switchToMCE(false);
+		
+		// Clear out existing buttons
+		var span_wiki = $dynano('enano_edit_btn_pt').object;
+		var span_mce  = $dynano('enano_edit_btn_mce').object;
+		span_wiki.style.display = 'inline';
+		span_mce.style.display = 'none';
+	}
+	else if ( need_to_wkt )
+	{
+		$dynano('ajaxEditArea').setContent('');
+		$dynano('ajaxEditArea').destroyMCE(false);
+		
+		// Clear out existing buttons
+		var span_wiki = $dynano('enano_edit_btn_pt').object;
+		var span_mce  = $dynano('enano_edit_btn_mce').object;
+		span_wiki.style.display = 'none';
+		span_mce.style.display = 'inline';
+	}
+	if ( need_to_mce || need_to_wkt )
+	{
+		// explain the conversion
+		if ( !noticetitle )
+			noticetitle = 'editor_msg_convert_draft_load_title';
+		if ( !noticebody )
+			noticebody = 'editor_msg_convert_draft_load_body';
+		
+		miniPromptMessage({
+				title: $lang.get(noticetitle),
+				message: $lang.get(noticebody),
+				buttons: [
+					{
+						text: $lang.get('etc_ok'),
+						onclick: function()
+						{
+							miniPromptDestroy(this);
+							return false;
+						}
+					}
+				]
+			});
+	}
 }
--- a/includes/clientside/static/enano-lib-basic.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/enano-lib-basic.js	Sun Mar 28 23:10:46 2010 -0400
@@ -17,26 +17,26 @@
 
 if ( typeof(title) != 'string')
 {
-  alert('There was a problem loading the PHP-generated Javascript variables that control parameters for AJAX applets. Most on-page functionality will be very badly broken.\n\nTheme developers, ensure that you are using {JS_DYNAMIC_VARS} *before* you include jsres.php.');
+	alert('There was a problem loading the PHP-generated Javascript variables that control parameters for AJAX applets. Most on-page functionality will be very badly broken.\n\nTheme developers, ensure that you are using {JS_DYNAMIC_VARS} *before* you include jsres.php.');
 }
 
 // placeholder for window.console - used if firebug isn't present
 // http://getfirebug.com/firebug/firebugx.js
 if (!window.console || !console.firebug)
 {
-    var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
-    "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
+		var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
+		"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
 
-    window.console = {};
-    for (var i = 0; i < names.length; ++i)
-        window.console[names[i]] = function() {}
+		window.console = {};
+		for (var i = 0; i < names.length; ++i)
+				window.console[names[i]] = function() {}
 }
 
 console.info('Enano::JS runtime: starting system init');
 
 if ( typeof(ENANO_JSRES_COMPRESSED) == undefined )
 {
-  var ENANO_JSRES_COMPRESSED = false;
+	var ENANO_JSRES_COMPRESSED = false;
 }
 
 // Run-time variables
@@ -48,9 +48,9 @@
 // Detect whether the user is running the Evil One or not...
 
 function checkIt(string) {
-  place = detect.indexOf(string) + 1;
-  thestring = string;
-  return place ? true : false;
+	place = detect.indexOf(string) + 1;
+	thestring = string;
+	return place ? true : false;
 }
 
 var IE = checkIt('msie');
@@ -64,30 +64,30 @@
 
 if ( IE )
 {
-  var version = window.navigator.appVersion;
-  version = version.substr( ( version.indexOf('MSIE') + 5 ) );
-  var rawversion = '';
-  for ( var i = 0; i < version.length; i++ )
-  {
-    var chr = version.substr(i, 1);
-    if ( !chr.match(/[0-9\.]/) )
-    {
-      break;
-    }
-    rawversion += chr;
-  }
-  rawversion = parseInt(rawversion);
-  if ( rawversion < 6 )
-  {
-    KILL_SWITCH = true;
-  }
+	var version = window.navigator.appVersion;
+	version = version.substr( ( version.indexOf('MSIE') + 5 ) );
+	var rawversion = '';
+	for ( var i = 0; i < version.length; i++ )
+	{
+		var chr = version.substr(i, 1);
+		if ( !chr.match(/[0-9\.]/) )
+		{
+			break;
+		}
+		rawversion += chr;
+	}
+	rawversion = parseInt(rawversion);
+	if ( rawversion < 6 )
+	{
+		KILL_SWITCH = true;
+	}
 }
 
 var tinymce_initted = false;
 
 if ( typeof(DISABLE_MCE) == undefined )
 {
-  var DISABLE_MCE = false;
+	var DISABLE_MCE = false;
 }
 
 is_Safari = checkIt('safari') ? true : false;
@@ -156,7 +156,7 @@
 // Can be set to true by slow themes (St. Patty)
 if ( typeof(pref_disable_js_fx) != 'boolean' )
 {
-  var pref_disable_js_fx = false;
+	var pref_disable_js_fx = false;
 }
 var aclDisableTransitionFX = ( is_firefox2 || pref_disable_js_fx ) ? true : false;
 
@@ -167,289 +167,289 @@
 var $_REQUEST = new Object();
 if ( window.location.hash )
 {
-  var hash = String(window.location.hash);
-  hash = hash.substr(1);
-  var reqobj = hash.split(';');
-  var a, b;
-  for ( var i = 0; i < reqobj.length; i++ )
-  {
-    a = reqobj[i].substr(0, reqobj[i].indexOf(':'));
-    b = reqobj[i].substr( ( reqobj[i].indexOf(':') + 1 ) );
-    $_REQUEST[a] = b;
-  }
+	var hash = String(window.location.hash);
+	hash = hash.substr(1);
+	var reqobj = hash.split(';');
+	var a, b;
+	for ( var i = 0; i < reqobj.length; i++ )
+	{
+		a = reqobj[i].substr(0, reqobj[i].indexOf(':'));
+		b = reqobj[i].substr( ( reqobj[i].indexOf(':') + 1 ) );
+		$_REQUEST[a] = b;
+	}
 }
 
 if ( !onload_hooks )
-  var onload_hooks = new Array();
+	var onload_hooks = new Array();
 
 function addOnloadHook(func)
 {
-  if ( typeof ( func ) == 'function' )
-  {
-    if ( typeof(onload_hooks.push) == 'function' )
-    {
-      onload_hooks.push(func);
-    }
-    else
-    {
-      onload_hooks[onload_hooks.length] = func;
-    }
-  }
+	if ( typeof ( func ) == 'function' )
+	{
+		if ( typeof(onload_hooks.push) == 'function' )
+		{
+			onload_hooks.push(func);
+		}
+		else
+		{
+			onload_hooks[onload_hooks.length] = func;
+		}
+	}
 }
 
 function runOnloadHooks(e)
 {
-  var _errorTrapper = 0;
-  for ( var _oLc = 0; _oLc < onload_hooks.length; _oLc++ )
-  {
-    _errorTrapper++;
-    if ( _errorTrapper >= 1000 )
-      break;
-    var _f = onload_hooks[_oLc];
-    if ( typeof(_f) == 'function' )
-    {
-      _f(e);
-    }
-  }
-  onload_hooks = [];
+	var _errorTrapper = 0;
+	for ( var _oLc = 0; _oLc < onload_hooks.length; _oLc++ )
+	{
+		_errorTrapper++;
+		if ( _errorTrapper >= 1000 )
+			break;
+		var _f = onload_hooks[_oLc];
+		if ( typeof(_f) == 'function' )
+		{
+			_f(e);
+		}
+	}
+	onload_hooks = [];
 }
 
 var enano_hooks = {};
 function setHook(hook_name)
 {
-  if ( enano_hooks[hook_name] )
-  {
-    return enano_hooks[hook_name];
-  }
-  return 'void(0);';
+	if ( enano_hooks[hook_name] )
+	{
+		return enano_hooks[hook_name];
+	}
+	return 'void(0);';
 }
 
 function attachHook(hook_name, code)
 {
-  if ( !enano_hooks[hook_name] )
-    enano_hooks[hook_name] = '';
-  
-  enano_hooks[hook_name] += code;
+	if ( !enano_hooks[hook_name] )
+		enano_hooks[hook_name] = '';
+	
+	enano_hooks[hook_name] += code;
 }
 
 var loaded_components = loaded_components || {};
 var _load_component_running = false;
 function load_component(file)
 {
-  var multiple = false;
-  if ( typeof(file) == 'object' )
-  {
-    if ( ENANO_JSRES_COMPRESSED )
-    {
-      multiple = true;
-      for ( var i = 0; i < file.length; i++ )
-      {
-        file[i] = (file[i].replace(/\.js$/, '')) + '.js';
-        if ( loaded_components[file[i]] )
-        {
-          file[i] = false;
-        }
-      }
-      var file2 = [];
-      for ( var i = 0; i < file.length; i++ )
-      {
-        if ( file[i] )
-          file2.push(file[i]);
-      }
-      file = file2;
-      delete(file2);
-      if ( file.length < 1 )
-      {
-        return true;
-      }
-      var file_flat = implode(',', file);
-    }
-    else
-    {
-      for ( var i = 0; i < file.length; i++ )
-      {
-        load_component(file[i]);
-      }
-      return true;
-    }
-  }
-  _load_component_running = true;
-  if ( !multiple )
-  {
-    file = file.replace(/\.js$/, '');
-  
-    if ( loaded_components[file + '.js'] )
-    {
-      // already loaded
-      return true;
-    }
-  }
-  
-  console.info('Loading component %s via AJAX', ( multiple ? file_flat : file ));
-  
-  load_show_win(( multiple ? file_flat : file ));
-  
-  // get an XHR instance
-  var ajax = ajaxMakeXHR();
-  
-  if ( !multiple )
-    file = file + '.js';
-  var uri = ( ENANO_JSRES_COMPRESSED ) ? scriptPath + '/includes/clientside/jsres.php?f=' + (multiple ? file_flat : file ) + '&' + enano_version : scriptPath + '/includes/clientside/static/' + file + '?' + enano_version;
-  try
-  {
-    ajax.open('GET', uri, false);
-    ajax.onreadystatechange = function()
-    {
-      if ( this.readyState == 4 && this.status != 200 )
-      {
-        alert('There was a problem loading a script from the server. Please check your network connection.');
-        load_hide_win();
-        throw('load_component(): XHR for component ' + file + ' failed');
-      }
-    }
-    ajax.send(null);
-    // async request, so if status != 200 at this point then we're screwed
-    if ( ajax.readyState == 4 && ajax.status == 200 )
-    {
-      if ( onload_complete )
-        onload_hooks = [];
-      eval_global(ajax.responseText);
-      if ( window.jQuery && aclDisableTransitionFX )
-        if ( window.jQuery.fx )
-          window.jQuery.fx.off = true;
-      load_hide_win();
-      if ( onload_complete )
-        runOnloadHooks();
-    }
-  }
-  catch(e)
-  {
-    alert('There was a problem loading a script from the server. Please check your network connection.');
-    load_hide_win();
-    console.info("Component loader exception is shown below.");
-    console.debug(e);
-    console.trace();
-    throw('load_component(): XHR for component ' + file + ' failed');
-  }
-  
-  if ( !multiple )
-  {
-    loaded_components[file] = true;
-  }
-  _load_component_running = false;
-  return true;
+	var multiple = false;
+	if ( typeof(file) == 'object' )
+	{
+		if ( ENANO_JSRES_COMPRESSED )
+		{
+			multiple = true;
+			for ( var i = 0; i < file.length; i++ )
+			{
+				file[i] = (file[i].replace(/\.js$/, '')) + '.js';
+				if ( loaded_components[file[i]] )
+				{
+					file[i] = false;
+				}
+			}
+			var file2 = [];
+			for ( var i = 0; i < file.length; i++ )
+			{
+				if ( file[i] )
+					file2.push(file[i]);
+			}
+			file = file2;
+			delete(file2);
+			if ( file.length < 1 )
+			{
+				return true;
+			}
+			var file_flat = implode(',', file);
+		}
+		else
+		{
+			for ( var i = 0; i < file.length; i++ )
+			{
+				load_component(file[i]);
+			}
+			return true;
+		}
+	}
+	_load_component_running = true;
+	if ( !multiple )
+	{
+		file = file.replace(/\.js$/, '');
+	
+		if ( loaded_components[file + '.js'] )
+		{
+			// already loaded
+			return true;
+		}
+	}
+	
+	console.info('Loading component %s via AJAX', ( multiple ? file_flat : file ));
+	
+	load_show_win(( multiple ? file_flat : file ));
+	
+	// get an XHR instance
+	var ajax = ajaxMakeXHR();
+	
+	if ( !multiple )
+		file = file + '.js';
+	var uri = ( ENANO_JSRES_COMPRESSED ) ? scriptPath + '/includes/clientside/jsres.php?f=' + (multiple ? file_flat : file ) + '&' + enano_version : scriptPath + '/includes/clientside/static/' + file + '?' + enano_version;
+	try
+	{
+		ajax.open('GET', uri, false);
+		ajax.onreadystatechange = function()
+		{
+			if ( this.readyState == 4 && this.status != 200 )
+			{
+				alert('There was a problem loading a script from the server. Please check your network connection.');
+				load_hide_win();
+				throw('load_component(): XHR for component ' + file + ' failed');
+			}
+		}
+		ajax.send(null);
+		// async request, so if status != 200 at this point then we're screwed
+		if ( ajax.readyState == 4 && ajax.status == 200 )
+		{
+			if ( onload_complete )
+				onload_hooks = [];
+			eval_global(ajax.responseText);
+			if ( window.jQuery && aclDisableTransitionFX )
+				if ( window.jQuery.fx )
+					window.jQuery.fx.off = true;
+			load_hide_win();
+			if ( onload_complete )
+				runOnloadHooks();
+		}
+	}
+	catch(e)
+	{
+		alert('There was a problem loading a script from the server. Please check your network connection.');
+		load_hide_win();
+		console.info("Component loader exception is shown below.");
+		console.debug(e);
+		console.trace();
+		throw('load_component(): XHR for component ' + file + ' failed');
+	}
+	
+	if ( !multiple )
+	{
+		loaded_components[file] = true;
+	}
+	_load_component_running = false;
+	return true;
 }
 
 function load_show_win(file)
 {
-  var img = '<img style="margin-right: 5px" src="' + cdnPath + '/images/loading.gif" />';
-  if ( document.getElementById('_js_load_component') )
-  {
-    document.getElementById('_js_load_component').innerHTML = img + msg_loading_component.replace('%component%', file);
-    return;
-  }
-  file = file.replace(/\.js$/, '').replace(/\.js,/g, ', ');
-  var ld = document.createElement('div');
-  ld.style.padding = '10px';
-  ld.style.height = '12px';
-  ld.style.position = 'fixed';
-  ld.style.right = '5px';
-  ld.style.bottom = '0px';
-  ld.innerHTML = img + msg_loading_component.replace('%component%', file);
-  ld.id = '_js_load_component';
-  
-  // FYI: The base64 encoded image is a 70% opacity 1x1px white PNG.
-  ld.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAA1JREFUCNdj+P///xkACcgDypG+nnEAAAAASUVORK5CYII=)';
-  
-  document.body.appendChild(ld);
-  document.body.style.cursor = 'wait';
+	var img = '<img style="margin-right: 5px" src="' + cdnPath + '/images/loading.gif" />';
+	if ( document.getElementById('_js_load_component') )
+	{
+		document.getElementById('_js_load_component').innerHTML = img + msg_loading_component.replace('%component%', file);
+		return;
+	}
+	file = file.replace(/\.js$/, '').replace(/\.js,/g, ', ');
+	var ld = document.createElement('div');
+	ld.style.padding = '10px';
+	ld.style.height = '12px';
+	ld.style.position = 'fixed';
+	ld.style.right = '5px';
+	ld.style.bottom = '0px';
+	ld.innerHTML = img + msg_loading_component.replace('%component%', file);
+	ld.id = '_js_load_component';
+	
+	// FYI: The base64 encoded image is a 70% opacity 1x1px white PNG.
+	ld.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAA1JREFUCNdj+P///xkACcgDypG+nnEAAAAASUVORK5CYII=)';
+	
+	document.body.appendChild(ld);
+	document.body.style.cursor = 'wait';
 }
 
 function load_hide_win()
 {
-  var ld = document.getElementById('_js_load_component');
-  if ( !ld )
-    return false;
-  ld.parentNode.removeChild(ld);
-  document.body.style.cursor = 'default';
+	var ld = document.getElementById('_js_load_component');
+	if ( !ld )
+		return false;
+	ld.parentNode.removeChild(ld);
+	document.body.style.cursor = 'default';
 }
 
 // evaluate a snippet of code in the global context, used for dynamic component loading
 // from: http://dean.edwards.name/weblog/2006/11/sandbox/
 function eval_global(_jsString)
 {
-  if (typeof _jsString != "string")
-  {
-    return false;
-  }
+	if (typeof _jsString != "string")
+	{
+		return false;
+	}
 
-  // Check whether window.eval executes code in the global scope.
-  window.eval("var __INCLUDE_TEST_1__ = true;");
-  if (typeof window.__INCLUDE_TEST_1__ != "undefined")
-  {
-    delete window.__INCLUDE_TEST_1__;
-    window.eval(_jsString);
-  }
-  else if (typeof window.execScript != "undefined")	// IE only
-  {
-    window.execScript(_jsString);
-  }
-  else
-  {
-    // Test effectiveness of creating a new SCRIPT element and adding it to the document.
-    this._insertScriptTag = function (_jsCode) {
-      var _script = document.createElement("script");
-      _script.type = "text/javascript";
-      _script.defer = false;
-      _script.text = _jsCode;
-      var _headNodeSet = document.getElementsByTagName("head");
-      if (_headNodeSet.length)
-      {
-        _script = _headNodeSet.item(0).appendChild(_script);
-      }
-      else
-      {
-        var _head = document.createElement("head");
-        _head = document.documentElement.appendChild(_head);
-        _script = _head.appendChild(_script);
-      }
-      return _script;
-    }
-    var _testScript = this._insertScriptTag("var __INCLUDE_TEST_2__ = true;");
-    if (typeof window.__INCLUDE_TEST_2__ == "boolean")
-    {
-      _testScript.parentNode.removeChild(_testScript);
-      this._insertScriptTag(_jsString);
-    }
-    else
-    {
-      // Check whether window.setTimeout works in real time.
-      window.setTimeout("var __INCLUDE_TEST_3__ = true;", 0);
-      if (typeof window.__INCLUDE_TEST_3__ != "undefined")
-      {
-        delete window.__INCLUDE_TEST_3__;
-        window.setTimeout(_jsString, 0);
-      }
-    }
-  }
+	// Check whether window.eval executes code in the global scope.
+	window.eval("var __INCLUDE_TEST_1__ = true;");
+	if (typeof window.__INCLUDE_TEST_1__ != "undefined")
+	{
+		delete window.__INCLUDE_TEST_1__;
+		window.eval(_jsString);
+	}
+	else if (typeof window.execScript != "undefined")	// IE only
+	{
+		window.execScript(_jsString);
+	}
+	else
+	{
+		// Test effectiveness of creating a new SCRIPT element and adding it to the document.
+		this._insertScriptTag = function (_jsCode) {
+			var _script = document.createElement("script");
+			_script.type = "text/javascript";
+			_script.defer = false;
+			_script.text = _jsCode;
+			var _headNodeSet = document.getElementsByTagName("head");
+			if (_headNodeSet.length)
+			{
+				_script = _headNodeSet.item(0).appendChild(_script);
+			}
+			else
+			{
+				var _head = document.createElement("head");
+				_head = document.documentElement.appendChild(_head);
+				_script = _head.appendChild(_script);
+			}
+			return _script;
+		}
+		var _testScript = this._insertScriptTag("var __INCLUDE_TEST_2__ = true;");
+		if (typeof window.__INCLUDE_TEST_2__ == "boolean")
+		{
+			_testScript.parentNode.removeChild(_testScript);
+			this._insertScriptTag(_jsString);
+		}
+		else
+		{
+			// Check whether window.setTimeout works in real time.
+			window.setTimeout("var __INCLUDE_TEST_3__ = true;", 0);
+			if (typeof window.__INCLUDE_TEST_3__ != "undefined")
+			{
+				delete window.__INCLUDE_TEST_3__;
+				window.setTimeout(_jsString, 0);
+			}
+		}
+	}
 
-  return true;
+	return true;
 }
 
 var autofill_check = function()
 {
-  var inputs = document.getElementsByTagName('input');
-  for ( var i = 0; i < inputs.length; i++ )
-  {
-    if ( inputs[i].className )
-    {
-      if ( inputs[i].className.match(/^autofill/) )
-      {
-        load_component('autofill');
-        return;
-      }
-    }
-  }
+	var inputs = document.getElementsByTagName('input');
+	for ( var i = 0; i < inputs.length; i++ )
+	{
+		if ( inputs[i].className )
+		{
+			if ( inputs[i].className.match(/^autofill/) )
+			{
+				load_component('autofill');
+				return;
+			}
+		}
+	}
 }
 
 addOnloadHook(autofill_check);
@@ -459,7 +459,7 @@
 // safari has window.console but not the .debug() method
 if ( is_Safari && !window.console.debug )
 {
-  window.console.debug = function() {};
+	window.console.debug = function() {};
 }
 
 // Do not remove the following comments, they are used by jsres.php.
@@ -468,177 +468,177 @@
 // Start loading files
 // The string from the [ to the ] needs to be valid JSON, it's parsed by jsres.php.
 var thefiles = [
-  'dynano.js',
-  'functions.js',
-  'dropdown.js',
-  'json.js',
-  'sliders.js',
-  'tinymce-init.js',
-  'loader.js'
+	'dynano.js',
+	'functions.js',
+	'dropdown.js',
+	'json.js',
+	'sliders.js',
+	'tinymce-init.js',
+	'loader.js'
 ];
 
 for(var f in thefiles)
 {
-  if ( typeof(thefiles[f]) != 'string' )
-    continue;
-  var script = document.createElement('script');
-  script.type="text/javascript";
-  if ( thefiles[f] == 'json.js' && KILL_SWITCH )
-  {
-    // alert('kill switch and problem script');
-    continue;
-  }
-  script.src=cdnPath+"/includes/clientside/static/"+thefiles[f];
-  head.appendChild(script);
+	if ( typeof(thefiles[f]) != 'string' )
+		continue;
+	var script = document.createElement('script');
+	script.type="text/javascript";
+	if ( thefiles[f] == 'json.js' && KILL_SWITCH )
+	{
+		// alert('kill switch and problem script');
+		continue;
+	}
+	script.src=cdnPath+"/includes/clientside/static/"+thefiles[f];
+	head.appendChild(script);
 }
 
 // Do not remove the following comment, it is used by jsres.php.
 /*!END_INCLUDER*/
 
 addOnloadHook(function() {
-  if ( $_REQUEST['auth'] )
-  {
-    var key = $_REQUEST['auth'];
-    var loc = String(window.location);
-    loc = loc.replace(/#.+$/, '').replace(/&auth=[0-9a-f]+/, '').replace(/\?auth=[0-9a-f]+(&)?/, '$1');
-    if ( key != 'false' )
-    {
-      var sep = loc.indexOf('?') != -1 ? '&' : '?';
-      loc = loc + sep + 'auth=' + key;
-    }
-    console.debug(loc);
-    window.location = loc;
-  }
-  if ( $_REQUEST['do'] )
-  {
-    var act = $_REQUEST['do'];
-    switch(act)
-    {
-      case 'comments':
-        ajaxComments();
-        break;
-      case 'edit':
-        var revid = ( $_REQUEST['rev'] ) ? parseInt($_REQUEST['rev']) : false;
-        ajaxEditor(revid);
-        break;
-      case 'login':
-        ajaxStartLogin();
-        break;
-      case 'history':
-        ajaxHistory();
-        break;
-      case 'catedit':
-        ajaxCatEdit();
-        break;
-      case 'rename':
-        ajaxRename();
-        break;
-      case 'aclmanager':
-        ajaxOpenACLManager();
-        break;
-    }
-  }
+	if ( $_REQUEST['auth'] )
+	{
+		var key = $_REQUEST['auth'];
+		var loc = String(window.location);
+		loc = loc.replace(/#.+$/, '').replace(/&auth=[0-9a-f]+/, '').replace(/\?auth=[0-9a-f]+(&)?/, '$1');
+		if ( key != 'false' )
+		{
+			var sep = loc.indexOf('?') != -1 ? '&' : '?';
+			loc = loc + sep + 'auth=' + key;
+		}
+		console.debug(loc);
+		window.location = loc;
+	}
+	if ( $_REQUEST['do'] )
+	{
+		var act = $_REQUEST['do'];
+		switch(act)
+		{
+			case 'comments':
+				ajaxComments();
+				break;
+			case 'edit':
+				var revid = ( $_REQUEST['rev'] ) ? parseInt($_REQUEST['rev']) : false;
+				ajaxEditor(revid);
+				break;
+			case 'login':
+				ajaxStartLogin();
+				break;
+			case 'history':
+				ajaxHistory();
+				break;
+			case 'catedit':
+				ajaxCatEdit();
+				break;
+			case 'rename':
+				ajaxRename();
+				break;
+			case 'aclmanager':
+				ajaxOpenACLManager();
+				break;
+		}
+	}
 });
 
 function Placeholder(funcname, filename)
 {
-  this.filename = filename;
-  this.funcname = funcname;
-  this.go = function()
-  {
-    window[funcname] = null;
-    load_component(filename);
-    var arglist = [];
-    for ( var i = 0; i < arguments.length; i++ )
-    {
-      arglist[arglist.length] = 'arguments['+i+']';
-    }
-    arglist = implode(', ', arglist);
-    return eval(funcname + '(' + arglist + ');');
-  }
+	this.filename = filename;
+	this.funcname = funcname;
+	this.go = function()
+	{
+		window[funcname] = null;
+		load_component(filename);
+		var arglist = [];
+		for ( var i = 0; i < arguments.length; i++ )
+		{
+			arglist[arglist.length] = 'arguments['+i+']';
+		}
+		arglist = implode(', ', arglist);
+		return eval(funcname + '(' + arglist + ');');
+	}
 }
 
 // list of public functions that need placeholders that fetch the component
 var placeholder_list = {
-  ajaxReset: 'ajax.js',
-  ajaxComments: 'comments.js',
-  ajaxEditor: 'editor.js',
-  ajaxHistory: 'ajax.js',
-  ajaxRename: 'ajax.js',
-  ajaxDelVote: 'ajax.js',
-  ajaxProtect: 'ajax.js',
-  ajaxClearLogs: 'ajax.js',
-  ajaxRollback: 'ajax.js',
-  ajaxResetDelVotes: 'ajax.js',
-  ajaxDeletePage: 'ajax.js',
-  ajaxSetPassword: 'ajax.js',
-  ajaxChangeStyle: 'ajax.js',
-  ajaxCatToTag: 'ajax.js',
-  ajaxCatEdit: 'ajax.js',
-  ajaxReverseDNS: 'ajax.js',
-  ajaxGzipCheck: 'ajax.js',
-  ajaxOpenACLManager: 'acl.js',
-  ajaxOpenDirectACLRule: 'acl.js',
-  ajaxAdminPage: 'login.js',
-  ajaxInitLogout: 'login.js',
-  ajaxStartLogin: 'login.js',
-  ajaxStartAdminLogin: 'login.js',
-  ajaxLoginNavTo: 'login.js',
-  ajaxLogonToElev: 'login.js',
-  ajaxLoginInit: 'login.js',
-  ajaxAdminPage: 'login.js',
-  ajaxAdminUser: 'login.js',
-  mb_logout: 'login.js',
-  selectButtonMajor: 'toolbar.js',
-  selectButtonMinor: 'toolbar.js',
-  unselectAllButtonsMajor: 'toolbar.js',
-  unselectAllButtonsMinor: 'toolbar.js',
-  darken: 'fadefilter.js',
-  enlighten: 'fadefilter.js',
-  password_score: 'pwstrength.js',
-  password_score_field: 'pwstrength.js',
-  ajaxEditTheme: 'theme-manager.js',
-  ajaxToggleSystemThemes: 'theme-manager.js',
-  ajaxInstallTheme: 'theme-manager.js',
-  ajaxInitRankEdit: 'rank-manager.js',
-  ajaxInitRankCreate: 'rank-manager.js',
-  autofill_init_element: 'autofill.js',
-  autofill_init: 'autofill.js',
-  paginator_goto: 'paginate.js'
+	ajaxReset: 'ajax.js',
+	ajaxComments: 'comments.js',
+	ajaxEditor: 'editor.js',
+	ajaxHistory: 'ajax.js',
+	ajaxRename: 'ajax.js',
+	ajaxDelVote: 'ajax.js',
+	ajaxProtect: 'ajax.js',
+	ajaxClearLogs: 'ajax.js',
+	ajaxRollback: 'ajax.js',
+	ajaxResetDelVotes: 'ajax.js',
+	ajaxDeletePage: 'ajax.js',
+	ajaxSetPassword: 'ajax.js',
+	ajaxChangeStyle: 'ajax.js',
+	ajaxCatToTag: 'ajax.js',
+	ajaxCatEdit: 'ajax.js',
+	ajaxReverseDNS: 'ajax.js',
+	ajaxGzipCheck: 'ajax.js',
+	ajaxOpenACLManager: 'acl.js',
+	ajaxOpenDirectACLRule: 'acl.js',
+	ajaxAdminPage: 'login.js',
+	ajaxInitLogout: 'login.js',
+	ajaxStartLogin: 'login.js',
+	ajaxStartAdminLogin: 'login.js',
+	ajaxLoginNavTo: 'login.js',
+	ajaxLogonToElev: 'login.js',
+	ajaxLoginInit: 'login.js',
+	ajaxAdminPage: 'login.js',
+	ajaxAdminUser: 'login.js',
+	mb_logout: 'login.js',
+	selectButtonMajor: 'toolbar.js',
+	selectButtonMinor: 'toolbar.js',
+	unselectAllButtonsMajor: 'toolbar.js',
+	unselectAllButtonsMinor: 'toolbar.js',
+	darken: 'fadefilter.js',
+	enlighten: 'fadefilter.js',
+	password_score: 'pwstrength.js',
+	password_score_field: 'pwstrength.js',
+	ajaxEditTheme: 'theme-manager.js',
+	ajaxToggleSystemThemes: 'theme-manager.js',
+	ajaxInstallTheme: 'theme-manager.js',
+	ajaxInitRankEdit: 'rank-manager.js',
+	ajaxInitRankCreate: 'rank-manager.js',
+	autofill_init_element: 'autofill.js',
+	autofill_init: 'autofill.js',
+	paginator_goto: 'paginate.js'
 };
 
 function AutofillUsername(el, p)
 {
-  p = p || {};
-  el.className = 'autofill username';
-  el.onkeyup = null;
-  autofill_init_element(el, p);
+	p = p || {};
+	el.className = 'autofill username';
+	el.onkeyup = null;
+	autofill_init_element(el, p);
 }
 
 function AutofillPage(el, p)
 {
-  p = p || {};
-  el.className = 'autofill page';
-  el.onkeyup = null;
-  autofill_init_element(el, p);
+	p = p || {};
+	el.className = 'autofill page';
+	el.onkeyup = null;
+	autofill_init_element(el, p);
 }
 
 var placeholder_instances = {};
 
 for ( var i in placeholder_list )
 {
-  var file = placeholder_list[i];
-  placeholder_instances[i] = new Placeholder(i, file);
-  window[i] = window[i] || placeholder_instances[i].go;
+	var file = placeholder_list[i];
+	placeholder_instances[i] = new Placeholder(i, file);
+	window[i] = window[i] || placeholder_instances[i].go;
 }
 
 $lang = window.$lang || {
-  get: function(a, b)
-  {
-    load_component('l10n');
-    return $lang.get(a, b);
-  },
-  placeholder: true
+	get: function(a, b)
+	{
+		load_component('l10n');
+		return $lang.get(a, b);
+	},
+	placeholder: true
 }
 
 //*/
--- a/includes/clientside/static/expander.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/expander.js	Sun Mar 28 23:10:46 2010 -0400
@@ -4,134 +4,134 @@
 
 var expander_onload = function()
 {
-  var sets = document.getElementsByTagName('fieldset');
-  if ( sets.length < 1 )
-    return false;
-  var init_us = [];
-  for ( var index = 0; index < sets.length; index++ )
-  {
-    var mode = sets[index].getAttribute('enano:expand');
-    if ( mode == 'closed' || mode == 'open' )
-    {
-      init_us.push(sets[index]);
-    }
-  }
-  for ( var k = 0; k < init_us.length; k++ )
-  {
-    expander_init_element(init_us[k]);
-  }
+	var sets = document.getElementsByTagName('fieldset');
+	if ( sets.length < 1 )
+		return false;
+	var init_us = [];
+	for ( var index = 0; index < sets.length; index++ )
+	{
+		var mode = sets[index].getAttribute('enano:expand');
+		if ( mode == 'closed' || mode == 'open' )
+		{
+			init_us.push(sets[index]);
+		}
+	}
+	for ( var k = 0; k < init_us.length; k++ )
+	{
+		expander_init_element(init_us[k]);
+	}
 }
 
 function expander_init_element(el)
 {
-  // get the legend tag
-  var legend = el.getElementsByTagName('legend')[0];
-  if ( !legend )
-    return false;
-  // existing content
-  var existing_inner = legend.innerHTML;
-  // blank the innerHTML and replace it with a link
-  legend.innerHTML = '';
-  var button = document.createElement('a');
-  button.className = 'expander expander-open';
-  button.innerHTML = existing_inner;
-  button.href = '#';
-  
-  legend.appendChild(button);
-  
-  button.onclick = function()
-  {
-    try
-    {
-      expander_handle_click(this);
-    }
-    catch(e)
-    {
-      console.debug('Exception caught: ', e);
-    }
-    return false;
-  }
-  
-  if ( el.getAttribute('enano:expand') == 'closed' )
-  {
-    expander_close(el);
-  }
+	// get the legend tag
+	var legend = el.getElementsByTagName('legend')[0];
+	if ( !legend )
+		return false;
+	// existing content
+	var existing_inner = legend.innerHTML;
+	// blank the innerHTML and replace it with a link
+	legend.innerHTML = '';
+	var button = document.createElement('a');
+	button.className = 'expander expander-open';
+	button.innerHTML = existing_inner;
+	button.href = '#';
+	
+	legend.appendChild(button);
+	
+	button.onclick = function()
+	{
+		try
+		{
+			expander_handle_click(this);
+		}
+		catch(e)
+		{
+			console.debug('Exception caught: ', e);
+		}
+		return false;
+	}
+	
+	if ( el.getAttribute('enano:expand') == 'closed' )
+	{
+		expander_close(el);
+	}
 }
 
 function expander_handle_click(el)
 {
-  if ( el.parentNode.parentNode.tagName != 'FIELDSET' )
-    return false;
-  var parent = el.parentNode.parentNode;
-  if ( parent.getAttribute('enano:expand') == 'closed' )
-  {
-    expander_open(parent);
-  }
-  else
-  {
-    expander_close(parent);
-  }
+	if ( el.parentNode.parentNode.tagName != 'FIELDSET' )
+		return false;
+	var parent = el.parentNode.parentNode;
+	if ( parent.getAttribute('enano:expand') == 'closed' )
+	{
+		expander_open(parent);
+	}
+	else
+	{
+		expander_close(parent);
+	}
 }
 
 function expander_close(el)
 {
-  var children = el.childNodes;
-  for ( var i = 0; i < children.length; i++ )
-  {
-    var child = children[i];
-    if ( child.tagName == 'LEGEND' )
-    {
-      var a = child.getElementsByTagName('a')[0];
-      $dynano(a).rmClass('expander-open');
-      $dynano(a).addClass('expander-closed');
-      continue;
-    }
-    if ( child.style )
-    {
-      child.expander_meta_old_state = child.style.display;
-      child.style.display = 'none';
-    }
-  }
-  el.expander_meta_padbak = el.style.padding;
-  el.setAttribute('enano:expand', 'closed');
+	var children = el.childNodes;
+	for ( var i = 0; i < children.length; i++ )
+	{
+		var child = children[i];
+		if ( child.tagName == 'LEGEND' )
+		{
+			var a = child.getElementsByTagName('a')[0];
+			$dynano(a).rmClass('expander-open');
+			$dynano(a).addClass('expander-closed');
+			continue;
+		}
+		if ( child.style )
+		{
+			child.expander_meta_old_state = child.style.display;
+			child.style.display = 'none';
+		}
+	}
+	el.expander_meta_padbak = el.style.padding;
+	el.setAttribute('enano:expand', 'closed');
 }
 
 function expander_open(el)
 {
-  var children = el.childNodes;
-  for ( var i = 0; i < children.length; i++ )
-  {
-    var child = children[i];
-    if ( child.tagName == 'LEGEND' )
-    {
-      var a = child.getElementsByTagName('a')[0];
-      $dynano(a).rmClass('expander-closed');
-      $dynano(a).addClass('expander-open');
-      continue;
-    }
-    if ( child.expander_meta_old_state && child.style )
-    {
-      child.style.display = child.expander_meta_old_state;
-      child.expander_meta_old_state = null;
-    }
-    else
-    {
-      if ( child.style )
-      {
-        child.style.display = null;
-      }
-    }
-  }
-  if ( el.expander_meta_padbak )
-  {
-    el.style.padding = el.expander_meta_padbak;
-    el.expander_meta_padbak = null;
-  }
-  else
-  {
-    el.style.padding = null;
-  }
-  el.setAttribute('enano:expand', 'open');
+	var children = el.childNodes;
+	for ( var i = 0; i < children.length; i++ )
+	{
+		var child = children[i];
+		if ( child.tagName == 'LEGEND' )
+		{
+			var a = child.getElementsByTagName('a')[0];
+			$dynano(a).rmClass('expander-closed');
+			$dynano(a).addClass('expander-open');
+			continue;
+		}
+		if ( child.expander_meta_old_state && child.style )
+		{
+			child.style.display = child.expander_meta_old_state;
+			child.expander_meta_old_state = null;
+		}
+		else
+		{
+			if ( child.style )
+			{
+				child.style.display = null;
+			}
+		}
+	}
+	if ( el.expander_meta_padbak )
+	{
+		el.style.padding = el.expander_meta_padbak;
+		el.expander_meta_padbak = null;
+	}
+	else
+	{
+		el.style.padding = null;
+	}
+	el.setAttribute('enano:expand', 'open');
 }
 
 addOnloadHook(expander_onload);
--- a/includes/clientside/static/fadefilter.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/fadefilter.js	Sun Mar 28 23:10:46 2010 -0400
@@ -9,88 +9,88 @@
 
 function darken(nofade, opacVal, layerid)
 {
-  layerid = ( layerid ) ? layerid : 'specialLayer_darkener';
-  if(IE)
-    nofade = true;
-  if ( !opacVal )
-    opacVal = 70;
-  darkener_index[layerid] = ( typeof(darkener_index[layerid]) == 'number' ) ? darkener_index[layerid] + 1 : 1;
-  if(document.getElementById(layerid) && !document.getElementById(layerid).destroying)
-  {
-    document.getElementById(layerid).style.zIndex = getHighestZ() + 1;
-    if(nofade)
-    {
-      changeOpac(opacVal, layerid);
-      document.getElementById(layerid).style.display = 'block';
-      document.getElementById(layerid).myOpacVal = opacVal;
-    }
-    else
-    {
-      if ( document.getElementById(layerid).style.display != 'none' )
-      {
-        var currentOpac = document.getElementById(layerid).myOpacVal;
-        opacity(layerid, currentOpac, opacVal, FADE_TIME);
-        document.getElementById(layerid).myOpacVal = opacVal;
-      }
-      else
-      {
-        document.getElementById(layerid).style.display = 'block';
-        document.getElementById(layerid).myOpacVal = opacVal;
-        opacity(layerid, 0, opacVal, FADE_TIME);
-      }
-    }
-  }
-  else if(document.getElementById(layerid) && document.getElementById(layerid).destroying)
-  {
-    // fade in progress - abort
-    console.warn('Aborting fade');
-    abortFades();
-    changeOpac(opacVal, layerid);
-    document.getElementById(layerid).destroying = false;
-    return document.getElementById(layerid);
-  }
-  else
-  {
-    w = getWidth();
-    h = getHeight();
-    var thediv = document.createElement('div');
-    if(IE)
-      thediv.style.position = 'absolute';
-    else
-      thediv.style.position = 'fixed';
-    if ( IE )
-    {
-      var top = getScrollOffset();
-      thediv.style.top = String(top) + 'px';
-    }
-    else
-    {
-      thediv.style.top = '0px';
-    }
-    thediv.style.left = '0px';
-    thediv.style.opacity = '0';
-    thediv.style.filter = 'alpha(opacity=0)';
-    thediv.style.backgroundColor = '#000000';
-    thediv.style.width =  '100%';
-    thediv.style.height = IE ? h + 'px' : '100%';
-    thediv.style.zIndex = getHighestZ() + 1;
-    thediv.id = layerid;
-    thediv.myOpacVal = opacVal;
-    if(nofade)
-    {
-      thediv.style.opacity = ( parseFloat(opacVal) / 100 );
-      thediv.style.filter = 'alpha(opacity=' + opacVal + ')';
-      body = document.getElementsByTagName('body');
-      body = body[0];
-      body.appendChild(thediv);
-    } else {
-      body = document.getElementsByTagName('body');
-      body = body[0];
-      body.appendChild(thediv);
-      opacity(layerid, 0, opacVal, FADE_TIME);
-    }
-  }
-  return document.getElementById(layerid);
+	layerid = ( layerid ) ? layerid : 'specialLayer_darkener';
+	if(IE)
+		nofade = true;
+	if ( !opacVal )
+		opacVal = 70;
+	darkener_index[layerid] = ( typeof(darkener_index[layerid]) == 'number' ) ? darkener_index[layerid] + 1 : 1;
+	if(document.getElementById(layerid) && !document.getElementById(layerid).destroying)
+	{
+		document.getElementById(layerid).style.zIndex = getHighestZ() + 1;
+		if(nofade)
+		{
+			changeOpac(opacVal, layerid);
+			document.getElementById(layerid).style.display = 'block';
+			document.getElementById(layerid).myOpacVal = opacVal;
+		}
+		else
+		{
+			if ( document.getElementById(layerid).style.display != 'none' )
+			{
+				var currentOpac = document.getElementById(layerid).myOpacVal;
+				opacity(layerid, currentOpac, opacVal, FADE_TIME);
+				document.getElementById(layerid).myOpacVal = opacVal;
+			}
+			else
+			{
+				document.getElementById(layerid).style.display = 'block';
+				document.getElementById(layerid).myOpacVal = opacVal;
+				opacity(layerid, 0, opacVal, FADE_TIME);
+			}
+		}
+	}
+	else if(document.getElementById(layerid) && document.getElementById(layerid).destroying)
+	{
+		// fade in progress - abort
+		console.warn('Aborting fade');
+		abortFades();
+		changeOpac(opacVal, layerid);
+		document.getElementById(layerid).destroying = false;
+		return document.getElementById(layerid);
+	}
+	else
+	{
+		w = getWidth();
+		h = getHeight();
+		var thediv = document.createElement('div');
+		if(IE)
+			thediv.style.position = 'absolute';
+		else
+			thediv.style.position = 'fixed';
+		if ( IE )
+		{
+			var top = getScrollOffset();
+			thediv.style.top = String(top) + 'px';
+		}
+		else
+		{
+			thediv.style.top = '0px';
+		}
+		thediv.style.left = '0px';
+		thediv.style.opacity = '0';
+		thediv.style.filter = 'alpha(opacity=0)';
+		thediv.style.backgroundColor = '#000000';
+		thediv.style.width =  '100%';
+		thediv.style.height = IE ? h + 'px' : '100%';
+		thediv.style.zIndex = getHighestZ() + 1;
+		thediv.id = layerid;
+		thediv.myOpacVal = opacVal;
+		if(nofade)
+		{
+			thediv.style.opacity = ( parseFloat(opacVal) / 100 );
+			thediv.style.filter = 'alpha(opacity=' + opacVal + ')';
+			body = document.getElementsByTagName('body');
+			body = body[0];
+			body.appendChild(thediv);
+		} else {
+			body = document.getElementsByTagName('body');
+			body = body[0];
+			body.appendChild(thediv);
+			opacity(layerid, 0, opacVal, FADE_TIME);
+		}
+	}
+	return document.getElementById(layerid);
 }
 
 /**
@@ -100,26 +100,26 @@
 
 function enlighten(nofade, layerid)
 {
-  layerid = ( layerid ) ? layerid : 'specialLayer_darkener';
-  
-  if(IE)
-    nofade = true;
-  darkener_index[layerid] -= 1;
-  if ( darkener_index[layerid] > 0 )
-    return false;
-  if(document.getElementById(layerid))
-  {
-    if(nofade)
-    {
-      document.getElementById(layerid).style.display = 'none';
-    }
-    else
-    {
-      document.getElementById(layerid).destroying = true;
-      var from = document.getElementById(layerid).myOpacVal;
-      opacity(layerid, from, 0, FADE_TIME);
-      setTimeout("var l = document.getElementById('" + layerid + "'); var b = document.getElementsByTagName('body')[0]; b.removeChild(l);", 1000);
-    }
-  }
-  return document.getElementById(layerid);
+	layerid = ( layerid ) ? layerid : 'specialLayer_darkener';
+	
+	if(IE)
+		nofade = true;
+	darkener_index[layerid] -= 1;
+	if ( darkener_index[layerid] > 0 )
+		return false;
+	if(document.getElementById(layerid))
+	{
+		if(nofade)
+		{
+			document.getElementById(layerid).style.display = 'none';
+		}
+		else
+		{
+			document.getElementById(layerid).destroying = true;
+			var from = document.getElementById(layerid).myOpacVal;
+			opacity(layerid, from, 0, FADE_TIME);
+			setTimeout("var l = document.getElementById('" + layerid + "'); var b = document.getElementsByTagName('body')[0]; b.removeChild(l);", 1000);
+		}
+	}
+	return document.getElementById(layerid);
 }
--- a/includes/clientside/static/fat.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/fat.js	Sun Mar 28 23:10:46 2010 -0400
@@ -21,7 +21,7 @@
 			if (r)
 			{
 				if (!r[1]) r[1] = "";
-        if (!o.id) o.id = 'autofat_'+Math.floor(Math.random() * 100000);
+				if (!o.id) o.id = 'autofat_'+Math.floor(Math.random() * 100000);
 				if (o.id) Fat.fade_element(o.id,null,null,"#"+r[1]);
 			}
 		}
@@ -66,7 +66,7 @@
 	set_bgcolor : function (id, c)
 	{
 		var o = document.getElementById(id);
-    if(!o) return;
+		if(!o) return;
 		o.style.backgroundColor = c;
 	},
 	get_bgcolor : function (id)
--- a/includes/clientside/static/flyin.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/flyin.js	Sun Mar 28 23:10:46 2010 -0400
@@ -20,25 +20,25 @@
 
 // Effects code - don't bother changing these formulas
 var Back = {
-  easeOut: function(t, b, c, d, s)
-  {
-    if (s == undefined) s = 1.70158;
-    return c * ( ( t=t/d-1 ) * t * ( ( s + 1 ) * t + s) + 1) + b;
-  },
-  easeIn: function (t, b, c, d, s)
-  {
-    if (s == undefined) s = 1.70158;
-    return c * ( t/=d ) * t * ( ( s + 1 ) * t - s) + b;
-  },
-  easeInOut: function (t, b, c, d, s)
-  {
-    if (s == undefined) s = 1.70158; 
-    if ((t /= d/2) < 1) 
-    {
-      return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
-    }
-    return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
-  }
+	easeOut: function(t, b, c, d, s)
+	{
+		if (s == undefined) s = 1.70158;
+		return c * ( ( t=t/d-1 ) * t * ( ( s + 1 ) * t + s) + 1) + b;
+	},
+	easeIn: function (t, b, c, d, s)
+	{
+		if (s == undefined) s = 1.70158;
+		return c * ( t/=d ) * t * ( ( s + 1 ) * t - s) + b;
+	},
+	easeInOut: function (t, b, c, d, s)
+	{
+		if (s == undefined) s = 1.70158; 
+		if ((t /= d/2) < 1) 
+		{
+			return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+		}
+		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+	}
 }
 
 // This should be set to the class name of the effect you want.
@@ -48,146 +48,146 @@
 
 function fly_in_top(element, nofade, height_taken_care_of)
 {
-  return fly_core(element, nofade, FI_TOP, FI_IN, height_taken_care_of);
+	return fly_core(element, nofade, FI_TOP, FI_IN, height_taken_care_of);
 }
 
 function fly_in_bottom(element, nofade, height_taken_care_of)
 {
-  return fly_core(element, nofade, FI_BOTTOM, FI_IN, height_taken_care_of);
+	return fly_core(element, nofade, FI_BOTTOM, FI_IN, height_taken_care_of);
 }
 
 function fly_out_top(element, nofade, height_taken_care_of)
 {
-  return fly_core(element, nofade, FI_TOP, FI_OUT, height_taken_care_of);
+	return fly_core(element, nofade, FI_TOP, FI_OUT, height_taken_care_of);
 }
 
 function fly_out_bottom(element, nofade, height_taken_care_of)
 {
-  return fly_core(element, nofade, FI_BOTTOM, FI_OUT, height_taken_care_of);
+	return fly_core(element, nofade, FI_BOTTOM, FI_OUT, height_taken_care_of);
 }
 
 function fly_core(element, nofade, origin, direction, height_taken_care_of)
 {
-  if ( !element || typeof(element) != 'object' )
-    return false;
-  
-  // force to array
-  if ( !element.length )
-    element = [ element ];
-  
-  // target dimensions
-  var top, left;
-  // initial dimensions
-  var topi, lefti;
-  // current dimensions
-  var topc, leftc;
-  // screen dimensions
-  var w = getWidth();
-  var h = getHeight();
-  var y = parseInt ( getScrollOffset() );
-  // temp vars
-  var dim, off, diff, dist, ratio, opac_factor;
-  // setup element
-  for ( var i = 0; i < element.length; i++ )
-    element[i].style.position = 'absolute';
-  
-  dim = [ $dynano(element[0]).Height(), $dynano(element[0]).Width() ];
-  off = [ $dynano(element[0]).Top(), $dynano(element[0]).Left() ];
-  
-  if ( height_taken_care_of )
-  {
-    top = off[0];
-    left = off[1];
-  }
-  else
-  {
-    top  = Math.round(( h / 2 ) - ( dim[0] / 2 )) + y; // - ( h / 4 ));
-    left = Math.round(( w / 2 ) - ( dim[1] / 2 ));
-  }
-  
-  // you can change this around to get it to fly in from corners or be on the left/right side
-  lefti = left;
-  
-  // calculate first frame Y position
-  if ( origin == FI_TOP && direction == FI_IN )
-  {
-    topi = 0 - dim[0] + y;
-  }
-  else if ( origin == FI_TOP && direction == FI_OUT )
-  {
-    topi = top;
-    top = 0 - dim[0] + y;
-  }
-  else if ( origin == FI_BOTTOM && direction == FI_IN )
-  {
-    topi = h + y;
-  }
-  else if ( origin == FI_BOTTOM && direction == FI_OUT )
-  {
-    topi = top;
-    top = h + y;
-  }
-  
-  var abs_dir = ( ( origin == FI_TOP && direction == FI_IN ) || ( origin == FI_BOTTOM && direction == FI_OUT ) ) ? FI_DOWN : FI_UP;
-  
-  var diff_top = top - topi;
-  var diff_left = left - lefti;
-  
-  var frames = 100;
-  var timeout = 0;
-  var timerstep = 8 * FI_MULTIPLIER;
-  
-  // cache element so it can be changed from within setTimeout()
-  var rand_seed = Math.floor(Math.random() * 1000000);
-  fly_in_cache[rand_seed] = element;
-  
-  for ( var i = 0; i < frames; i++ )
-  {
-    topc = GlideEffect.easeInOut(i, topi, diff_top, frames);
-    leftc = GlideEffect.easeInOut(i, lefti, diff_left, frames);
-    
-    var code = 'var element = fly_in_cache[' + rand_seed + '];' + "\n";
-    code +=    'for ( var i = 0; i < element.length; i++ )' + "\n";
-    code +=    '{' + "\n";
-    code +=    '  element[i].style.top = "' + topc + 'px";' + "\n";
-    if ( !height_taken_care_of )
-      code +=  '  element[i].style.left = "' + leftc + 'px";' + "\n";
-    code +=    '}';
-    
-    setTimeout(code, timeout);
-    
-    timeout += timerstep;
-    
-    var ratio = i / frames;
-    
-    if ( !nofade )
-    {
-      // handle fade
-      var opac_factor = ratio * 100;
-      if ( direction == FI_OUT )
-        opac_factor = 100 - opac_factor;
-      
-      var code = 'var element = fly_in_cache[' + rand_seed + '];' + "\n";
-      code +=    'for ( var i = 0; i < element.length; i++ )' + "\n";
-      code +=    '{' + "\n";
-      code +=    '  domObjChangeOpac(' + opac_factor + ', element[i]);' + "\n";
-      code +=    '}';
-      
-      setTimeout(code, timeout);
-    }
-    
-  }
-  
-  // old framestepper code removed from here in Loch Ness
-  
-  timeout += timerstep;
-  return timeout;
+	if ( !element || typeof(element) != 'object' )
+		return false;
+	
+	// force to array
+	if ( !element.length )
+		element = [ element ];
+	
+	// target dimensions
+	var top, left;
+	// initial dimensions
+	var topi, lefti;
+	// current dimensions
+	var topc, leftc;
+	// screen dimensions
+	var w = getWidth();
+	var h = getHeight();
+	var y = parseInt ( getScrollOffset() );
+	// temp vars
+	var dim, off, diff, dist, ratio, opac_factor;
+	// setup element
+	for ( var i = 0; i < element.length; i++ )
+		element[i].style.position = 'absolute';
+	
+	dim = [ $dynano(element[0]).Height(), $dynano(element[0]).Width() ];
+	off = [ $dynano(element[0]).Top(), $dynano(element[0]).Left() ];
+	
+	if ( height_taken_care_of )
+	{
+		top = off[0];
+		left = off[1];
+	}
+	else
+	{
+		top  = Math.round(( h / 2 ) - ( dim[0] / 2 )) + y; // - ( h / 4 ));
+		left = Math.round(( w / 2 ) - ( dim[1] / 2 ));
+	}
+	
+	// you can change this around to get it to fly in from corners or be on the left/right side
+	lefti = left;
+	
+	// calculate first frame Y position
+	if ( origin == FI_TOP && direction == FI_IN )
+	{
+		topi = 0 - dim[0] + y;
+	}
+	else if ( origin == FI_TOP && direction == FI_OUT )
+	{
+		topi = top;
+		top = 0 - dim[0] + y;
+	}
+	else if ( origin == FI_BOTTOM && direction == FI_IN )
+	{
+		topi = h + y;
+	}
+	else if ( origin == FI_BOTTOM && direction == FI_OUT )
+	{
+		topi = top;
+		top = h + y;
+	}
+	
+	var abs_dir = ( ( origin == FI_TOP && direction == FI_IN ) || ( origin == FI_BOTTOM && direction == FI_OUT ) ) ? FI_DOWN : FI_UP;
+	
+	var diff_top = top - topi;
+	var diff_left = left - lefti;
+	
+	var frames = 100;
+	var timeout = 0;
+	var timerstep = 8 * FI_MULTIPLIER;
+	
+	// cache element so it can be changed from within setTimeout()
+	var rand_seed = Math.floor(Math.random() * 1000000);
+	fly_in_cache[rand_seed] = element;
+	
+	for ( var i = 0; i < frames; i++ )
+	{
+		topc = GlideEffect.easeInOut(i, topi, diff_top, frames);
+		leftc = GlideEffect.easeInOut(i, lefti, diff_left, frames);
+		
+		var code = 'var element = fly_in_cache[' + rand_seed + '];' + "\n";
+		code +=    'for ( var i = 0; i < element.length; i++ )' + "\n";
+		code +=    '{' + "\n";
+		code +=    '  element[i].style.top = "' + topc + 'px";' + "\n";
+		if ( !height_taken_care_of )
+			code +=  '  element[i].style.left = "' + leftc + 'px";' + "\n";
+		code +=    '}';
+		
+		setTimeout(code, timeout);
+		
+		timeout += timerstep;
+		
+		var ratio = i / frames;
+		
+		if ( !nofade )
+		{
+			// handle fade
+			var opac_factor = ratio * 100;
+			if ( direction == FI_OUT )
+				opac_factor = 100 - opac_factor;
+			
+			var code = 'var element = fly_in_cache[' + rand_seed + '];' + "\n";
+			code +=    'for ( var i = 0; i < element.length; i++ )' + "\n";
+			code +=    '{' + "\n";
+			code +=    '  domObjChangeOpac(' + opac_factor + ', element[i]);' + "\n";
+			code +=    '}';
+			
+			setTimeout(code, timeout);
+		}
+		
+	}
+	
+	// old framestepper code removed from here in Loch Ness
+	
+	timeout += timerstep;
+	return timeout;
 }
 
 function abs(i)
 {
-  if ( isNaN(i) )
-    return i;
-  return ( i < 0 ) ? ( 0 - i ) : i;
+	if ( isNaN(i) )
+		return i;
+	return ( i < 0 ) ? ( 0 - i ) : i;
 }
 
--- a/includes/clientside/static/functions.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/functions.js	Sun Mar 28 23:10:46 2010 -0400
@@ -2,69 +2,69 @@
 
 function makeUrl(page, query, html_friendly)
 {
-  url = contentPath+page;
-  if(url.indexOf('?') > 0) sep = '&';
-  else sep = '?';
-  if(query)
-  {
-    url = url + sep + query;
-  }
-  if(html_friendly)
-  {
-    url = url.replace('&', '&amp;');
-    url = url.replace('<', '&lt;');
-    url = url.replace('>', '&gt;');
-  }
-  return append_sid(url);
+	url = contentPath+page;
+	if(url.indexOf('?') > 0) sep = '&';
+	else sep = '?';
+	if(query)
+	{
+		url = url + sep + query;
+	}
+	if(html_friendly)
+	{
+		url = url.replace('&', '&amp;');
+		url = url.replace('<', '&lt;');
+		url = url.replace('>', '&gt;');
+	}
+	return append_sid(url);
 }
 
 function makeUrlNS(namespace, page, query, html_friendly)
 {
-  var url = contentPath+namespace_list[namespace]+(page.replace(/ /g, '_'));
-  if(url.indexOf('?') > 0) sep = '&';
-  else sep = '?';
-  if(query)
-  {
-    url = url + sep + query;
-  }
-  if(html_friendly)
-  {
-    url = url.replace('&', '&amp;');
-    url = url.replace('<', '&lt;');
-    url = url.replace('>', '&gt;');
-  }
-  return append_sid(url);
+	var url = contentPath+namespace_list[namespace]+(page.replace(/ /g, '_'));
+	if(url.indexOf('?') > 0) sep = '&';
+	else sep = '?';
+	if(query)
+	{
+		url = url + sep + query;
+	}
+	if(html_friendly)
+	{
+		url = url.replace('&', '&amp;');
+		url = url.replace('<', '&lt;');
+		url = url.replace('>', '&gt;');
+	}
+	return append_sid(url);
 }
 
 function strToPageID(string)
 {
-  // Convert Special:UploadFile to ['UploadFile', 'Special'], but convert 'Image:Enano.png' to ['Enano.png', 'File']
-  for(var i in namespace_list)
-    if(namespace_list[i] != '')
-      if(namespace_list[i] == string.substr(0, namespace_list[i].length))
-        return [string.substr(namespace_list[i].length), i];
-  return [string, 'Article'];
+	// Convert Special:UploadFile to ['UploadFile', 'Special'], but convert 'Image:Enano.png' to ['Enano.png', 'File']
+	for(var i in namespace_list)
+		if(namespace_list[i] != '')
+			if(namespace_list[i] == string.substr(0, namespace_list[i].length))
+				return [string.substr(namespace_list[i].length), i];
+	return [string, 'Article'];
 }
 
 function append_sid(url)
 {
-  var match = url.match(/#(.*?)$/);
-  url = url.replace(/#(.*?)$/, '');
-  sep = ( url.indexOf('?') > 0 ) ? '&' : '?';
-  if(ENANO_SID.length > 10)
-  {
-    url = url + sep + 'auth=' + ENANO_SID;
-    sep = '&';
-  }
-  if ( pagepass.length > 0 )
-  {
-    url = url + sep + 'pagepass=' + pagepass;
-  }
-  if ( match )
-  {
-    url = url + match[0];
-  }
-  return url;
+	var match = url.match(/#(.*?)$/);
+	url = url.replace(/#(.*?)$/, '');
+	sep = ( url.indexOf('?') > 0 ) ? '&' : '?';
+	if(ENANO_SID.length > 10)
+	{
+		url = url + sep + 'auth=' + ENANO_SID;
+		sep = '&';
+	}
+	if ( pagepass.length > 0 )
+	{
+		url = url + sep + 'pagepass=' + pagepass;
+	}
+	if ( match )
+	{
+		url = url + match[0];
+	}
+	return url;
 }
 
 var stdAjaxPrefix = append_sid(scriptPath+'/ajax.php?title='+title);
@@ -75,93 +75,93 @@
 
 function ajaxMakeXHR()
 {
-  var ajax;
-  if (window.XMLHttpRequest) {
-    ajax = new XMLHttpRequest();
-  } else {
-    if (window.ActiveXObject) {           
-      ajax = new ActiveXObject("Microsoft.XMLHTTP");
-    } else {
-      alert('Enano client-side runtime error: No AJAX support, unable to continue');
-      return;
-    }
-  }
-  return ajax;
+	var ajax;
+	if (window.XMLHttpRequest) {
+		ajax = new XMLHttpRequest();
+	} else {
+		if (window.ActiveXObject) {           
+			ajax = new ActiveXObject("Microsoft.XMLHTTP");
+		} else {
+			alert('Enano client-side runtime error: No AJAX support, unable to continue');
+			return;
+		}
+	}
+	return ajax;
 }
 
 function ajaxGet(uri, f, call_editor_safe) {
-  // Is the editor open?
-  if ( editor_open && !call_editor_safe )
-  {
-    // Make sure the user is willing to close the editor
-    var conf = confirm($lang.get('editor_msg_confirm_ajax'));
-    if ( !conf )
-    {
-      // Kill off any "loading" windows, etc. and cancel the request
-      unsetAjaxLoading();
-      return false;
-    }
-    // The user allowed the editor to be closed. Reset flags and knock out the on-close confirmation.
-    editor_open = false;
-    enableUnload();
-    // destroy the MCE instance so it can be recreated later
-    $dynano('ajaxEditArea').destroyMCE(false);
-  }
-  var ajax = ajaxMakeXHR();
-  if ( !ajax )
-  {
-    console.error('ajaxMakeXHR() failed');
-    return false;
-  }
-  ajax.onreadystatechange = function()
-  {
-    f(ajax);
-  };
-  ajax.open('GET', uri, true);
-  ajax.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
-  ajax.send(null);
-  window.ajax = ajax;
+	// Is the editor open?
+	if ( editor_open && !call_editor_safe )
+	{
+		// Make sure the user is willing to close the editor
+		var conf = confirm($lang.get('editor_msg_confirm_ajax'));
+		if ( !conf )
+		{
+			// Kill off any "loading" windows, etc. and cancel the request
+			unsetAjaxLoading();
+			return false;
+		}
+		// The user allowed the editor to be closed. Reset flags and knock out the on-close confirmation.
+		editor_open = false;
+		enableUnload();
+		// destroy the MCE instance so it can be recreated later
+		$dynano('ajaxEditArea').destroyMCE(false);
+	}
+	var ajax = ajaxMakeXHR();
+	if ( !ajax )
+	{
+		console.error('ajaxMakeXHR() failed');
+		return false;
+	}
+	ajax.onreadystatechange = function()
+	{
+		f(ajax);
+	};
+	ajax.open('GET', uri, true);
+	ajax.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
+	ajax.send(null);
+	window.ajax = ajax;
 }
 
 function ajaxPost(uri, parms, f, call_editor_safe) {
-  // Is the editor open?
-  if ( editor_open && !call_editor_safe )
-  {
-    // Make sure the user is willing to close the editor
-    var conf = confirm($lang.get('editor_msg_confirm_ajax'));
-    if ( !conf )
-    {
-      // Kill off any "loading" windows, etc. and cancel the request
-      unsetAjaxLoading();
-      return false;
-    }
-    // The user allowed the editor to be closed. Reset flags and knock out the on-close confirmation.
-    editor_open = false;
-    enableUnload();
-    // destroy the MCE instance so it can be recreated later
-    $dynano('ajaxEditArea').destroyMCE(false);
-  }
-  var ajax = ajaxMakeXHR();
-  if ( !ajax )
-  {
-    console.error('ajaxMakeXHR() failed');
-    return false;
-  }
-  ajax.onreadystatechange = function()
-  {
-    f(ajax);
-  };
-  ajax.open('POST', uri, true);
-  ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
-  // Setting Content-length in Safari triggers a warning
-  if ( !is_Safari )
-  {
-    ajax.setRequestHeader("Content-length", parms.length);
-  }
-  // fails under chrome 2.0
-  // ajax.setRequestHeader("Connection", "close");
-  ajax.send(parms);
-  window.ajax = ajax;
+	// Is the editor open?
+	if ( editor_open && !call_editor_safe )
+	{
+		// Make sure the user is willing to close the editor
+		var conf = confirm($lang.get('editor_msg_confirm_ajax'));
+		if ( !conf )
+		{
+			// Kill off any "loading" windows, etc. and cancel the request
+			unsetAjaxLoading();
+			return false;
+		}
+		// The user allowed the editor to be closed. Reset flags and knock out the on-close confirmation.
+		editor_open = false;
+		enableUnload();
+		// destroy the MCE instance so it can be recreated later
+		$dynano('ajaxEditArea').destroyMCE(false);
+	}
+	var ajax = ajaxMakeXHR();
+	if ( !ajax )
+	{
+		console.error('ajaxMakeXHR() failed');
+		return false;
+	}
+	ajax.onreadystatechange = function()
+	{
+		f(ajax);
+	};
+	ajax.open('POST', uri, true);
+	ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+	// Setting Content-length in Safari triggers a warning
+	if ( !is_Safari )
+	{
+		ajax.setRequestHeader("Content-length", parms.length);
+	}
+	// fails under chrome 2.0
+	// ajax.setRequestHeader("Connection", "close");
+	ajax.send(parms);
+	window.ajax = ajax;
 }
 
 /**
@@ -172,196 +172,196 @@
 
 function handle_invalid_json(response, customerror)
 {
-  load_component(['messagebox', 'jquery', 'jquery-ui', 'fadefilter', 'flyin', 'l10n']);
-  
-  darken(aclDisableTransitionFX, 70, 'invalidjsondarkener');
-  
-  var box = document.createElement('div');
-  var mainwin = document.createElement('div');
-  var panel = document.createElement('div');
-  
-  //
-  // main window
-  //
-  
-    mainwin.style.padding = '10px';
-    mainwin.style.width = '580px';
-    mainwin.style.height = '360px';
-    mainwin.style.clip = 'rect(0px,auto,auto,0px)';
-    mainwin.style.overflow = 'auto';
-    mainwin.style.backgroundColor = '#ffffff';
-  
-    // Title
-    var h3 = document.createElement('h3');
-    var h3_text = ( $lang.placeholder ) ? 'The site encountered an error while processing your request.' : $lang.get('ajax_badjson_title');
-    h3.appendChild(document.createTextNode(h3_text));
-    mainwin.appendChild(h3);
-    
-    if ( typeof(customerror) == 'string' )
-    {
-      var el = document.createElement('p');
-      el.appendChild(document.createTextNode(customerror));
-      mainwin.appendChild(el);
-    }
-    else
-    {
-      var error = 'We unexpectedly received the following response from the server. The response should have been in the JSON ';
-      error += 'serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers ';
-      error += 'for this problem:';
-      customerror = ( $lang.placeholder ) ? error : $lang.get('ajax_badjson_body');
-      var el = document.createElement('p');
-      el.appendChild(document.createTextNode(customerror));
-      mainwin.appendChild(el);
-      var ul = document.createElement('ul');
-      var li1 = document.createElement('li');
-      var li2 = document.createElement('li');
-      var li3 = document.createElement('li');
-      var li1_text = ( $lang.placeholder ) ? 'The server sent back a bad HTTP response code and thus sent an error page instead of running Enano. This indicates a possible problem with your server, and is not likely to be a bug with Enano.' : $lang.get('ajax_badjson_tip1');
-      var li2_text = ( $lang.placeholder ) ? 'The server sent back the expected JSON response, but also injected some code into the response that should not be there. Typically this consists of advertisement code. In this case, the administrator of this site will have to contact their web host to have advertisements disabled.' : $lang.get('ajax_badjson_tip2');
-      var li3_text = ( $lang.placeholder ) ? 'It\'s possible that Enano triggered a PHP error or warning. In this case, you may be looking at a bug in Enano.' : $lang.get('ajax_badjson_tip3');
-      var osc_ex_data = ( $lang.placeholder ) ? 'This is KNOWN to be the case with the OpenSourceCMS.com demo version of Enano.' : $lang.get('ajax_badjson_osc');
-      li1.appendChild(document.createTextNode(li1_text));
-      var osc_exception = ( window.location.hostname == 'demo.opensourcecms.com' ) ? ' ' + osc_ex_data : '';
-      li2.appendChild(document.createTextNode(li2_text + osc_exception));
-      li3.appendChild(document.createTextNode(li3_text));
-        
-      ul.appendChild(li1);
-      ul.appendChild(li2);
-      ul.appendChild(li3);
-      mainwin.appendChild(ul);
-    }
-    
-    var p2 = document.createElement('p');
-    var p2_text = ( $lang.placeholder ) ? 'The response received from the server is as follows:' : $lang.get('ajax_badjson_msg_response');
-    p2.appendChild(document.createTextNode(p2_text));
-    mainwin.appendChild(p2);
-    
-    var pre = document.createElement('pre');
-    pre.appendChild(document.createTextNode(response));
-    mainwin.appendChild(pre);
-    
-    var p3 = document.createElement('p');
-    var p3_text = $lang.placeholder ? 'You may also choose to view the response as HTML.' : $lang.get('ajax_badjson_msg_viewashtml');
-    p3.appendChild(document.createTextNode(p3_text + ' '));
-    var a = document.createElement('a');
-    var a_text = $lang.placeholder ? 'View as HTML' : $lang.get('ajax_badjson_btn_viewashtml');
-    a.appendChild(document.createTextNode(a_text + '...'));
-    a._resp = response;
-    a.onclick = function()
-    {
-      var vah_title = ( $lang.placeholder ) ? 'View the response as HTML?' : $lang.get('ajax_badjson_html_confirm_title');
-      var vah_body = ( $lang.placeholder ) ? 'If the server\'s response was modified by an attacker to include malicious code, viewing the response as HTML might allow that malicious code to run. Only continue if you have inspected the response text and verified that it is safe.' : $lang.get('ajax_badjson_html_confirm_body');
-      var btn_confirm = $lang.placeholder ? 'View as HTML' : $lang.get('ajax_badjson_btn_viewashtml');
-      var btn_cancel = $lang.placeholder ? 'Cancel' : $lang.get('etc_cancel');
-      var mp = miniPromptMessage({
-          title: vah_title,
-          message: vah_body,
-          buttons: [
-            {
-              text: btn_confirm,
-              color: 'blue',
-              style: {
-                fontWeight: 'bold'
-              },
-              onclick: function() {
-                var mp = miniPromptGetParent(this);
-                var win = window.open('about:blank', 'invalidjson_htmlwin', 'width=550,height=400,status=no,toolbars=no,toolbar=no,address=no,scroll=yes');
-                win.document.write(mp._response);
-                win.document.close();
-                miniPromptDestroy(this);
-              }
-            },
-            {
-              text: btn_cancel,
-              onclick: function() {
-                miniPromptDestroy(this);
-              }
-            }
-          ]
-        });
-      mp._response = this._resp;
-      return false;
-    }
-    a.href = '#';
-    p3.appendChild(a);
-    mainwin.appendChild(p3);
-  
-  //
-  // panel
-  //
-  
-    panel.style.backgroundColor = '#D0D0D0';
-    panel.style.textAlign = 'right';
-    panel.style.padding = '0 10px';
-    panel.style.lineHeight = '40px';
-    panel.style.width = '580px';
-    
-    var closer = document.createElement('input');
-    var btn_close = $lang.placeholder ? 'Close' : $lang.get('ajax_badjson_btn_close');
-    closer.type = 'button';
-    closer.value = btn_close;
-    closer.onclick = function()
-    {
-      var parentdiv = this.parentNode.parentNode;
-      if ( aclDisableTransitionFX )
-      {
-        parentdiv.parentNode.removeChild(parentdiv);
-        enlighten(aclDisableTransitionFX, 'invalidjsondarkener');
-      }
-      else
-      {
-        $(parentdiv).hide("blind", {}, 1000, function()
-          {
-            parentdiv.parentNode.removeChild(parentdiv);
-              enlighten(aclDisableTransitionFX, 'invalidjsondarkener');
-          });
-      }
-    }
-    panel.appendChild(closer);
-    
-  //
-  // put it together
-  //
-  
-    box.appendChild(mainwin);
-    box.appendChild(panel);
-    
-    // add it to the body to allow height/width calculation
-    
-    box.style.display = 'block';
-    box.style.position = 'absolute';
-    box.style.zIndex = getHighestZ() + 1;
-    domObjChangeOpac(0, box);
-    
-    var body = document.getElementsByTagName('body')[0];
-    body.appendChild(box);
-    
-    
-    // calculate position of the box
-    // box should be exactly 640px high, 480px wide
-    var top = ( getHeight() / 2 ) - ( $dynano(box).Height() / 2 ) + getScrollOffset();
-    var left = ( getWidth() / 2 ) - ( $dynano(box).Width() / 2 );
-    console.debug('top = %d, left = %d', top, left);
-    box.style.top = top + 'px';
-    box.style.left = left + 'px';
-    
-    // we have width and height, set display to none and reset opacity
-    if ( aclDisableTransitionFX )
-    {
-      domObjChangeOpac(100, box);
-      box.style.display = 'block';
-    }
-    else
-    {
-      box.style.display = 'none';
-      domObjChangeOpac(100, box);
-      
-      setTimeout(function()
-        {
-          $(box).show("blind", {}, 1000);
-        }, 1000);
-    }
-  return false;
+	load_component(['messagebox', 'jquery', 'jquery-ui', 'fadefilter', 'flyin', 'l10n']);
+	
+	darken(aclDisableTransitionFX, 70, 'invalidjsondarkener');
+	
+	var box = document.createElement('div');
+	var mainwin = document.createElement('div');
+	var panel = document.createElement('div');
+	
+	//
+	// main window
+	//
+	
+		mainwin.style.padding = '10px';
+		mainwin.style.width = '580px';
+		mainwin.style.height = '360px';
+		mainwin.style.clip = 'rect(0px,auto,auto,0px)';
+		mainwin.style.overflow = 'auto';
+		mainwin.style.backgroundColor = '#ffffff';
+	
+		// Title
+		var h3 = document.createElement('h3');
+		var h3_text = ( $lang.placeholder ) ? 'The site encountered an error while processing your request.' : $lang.get('ajax_badjson_title');
+		h3.appendChild(document.createTextNode(h3_text));
+		mainwin.appendChild(h3);
+		
+		if ( typeof(customerror) == 'string' )
+		{
+			var el = document.createElement('p');
+			el.appendChild(document.createTextNode(customerror));
+			mainwin.appendChild(el);
+		}
+		else
+		{
+			var error = 'We unexpectedly received the following response from the server. The response should have been in the JSON ';
+			error += 'serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers ';
+			error += 'for this problem:';
+			customerror = ( $lang.placeholder ) ? error : $lang.get('ajax_badjson_body');
+			var el = document.createElement('p');
+			el.appendChild(document.createTextNode(customerror));
+			mainwin.appendChild(el);
+			var ul = document.createElement('ul');
+			var li1 = document.createElement('li');
+			var li2 = document.createElement('li');
+			var li3 = document.createElement('li');
+			var li1_text = ( $lang.placeholder ) ? 'The server sent back a bad HTTP response code and thus sent an error page instead of running Enano. This indicates a possible problem with your server, and is not likely to be a bug with Enano.' : $lang.get('ajax_badjson_tip1');
+			var li2_text = ( $lang.placeholder ) ? 'The server sent back the expected JSON response, but also injected some code into the response that should not be there. Typically this consists of advertisement code. In this case, the administrator of this site will have to contact their web host to have advertisements disabled.' : $lang.get('ajax_badjson_tip2');
+			var li3_text = ( $lang.placeholder ) ? 'It\'s possible that Enano triggered a PHP error or warning. In this case, you may be looking at a bug in Enano.' : $lang.get('ajax_badjson_tip3');
+			var osc_ex_data = ( $lang.placeholder ) ? 'This is KNOWN to be the case with the OpenSourceCMS.com demo version of Enano.' : $lang.get('ajax_badjson_osc');
+			li1.appendChild(document.createTextNode(li1_text));
+			var osc_exception = ( window.location.hostname == 'demo.opensourcecms.com' ) ? ' ' + osc_ex_data : '';
+			li2.appendChild(document.createTextNode(li2_text + osc_exception));
+			li3.appendChild(document.createTextNode(li3_text));
+				
+			ul.appendChild(li1);
+			ul.appendChild(li2);
+			ul.appendChild(li3);
+			mainwin.appendChild(ul);
+		}
+		
+		var p2 = document.createElement('p');
+		var p2_text = ( $lang.placeholder ) ? 'The response received from the server is as follows:' : $lang.get('ajax_badjson_msg_response');
+		p2.appendChild(document.createTextNode(p2_text));
+		mainwin.appendChild(p2);
+		
+		var pre = document.createElement('pre');
+		pre.appendChild(document.createTextNode(response));
+		mainwin.appendChild(pre);
+		
+		var p3 = document.createElement('p');
+		var p3_text = $lang.placeholder ? 'You may also choose to view the response as HTML.' : $lang.get('ajax_badjson_msg_viewashtml');
+		p3.appendChild(document.createTextNode(p3_text + ' '));
+		var a = document.createElement('a');
+		var a_text = $lang.placeholder ? 'View as HTML' : $lang.get('ajax_badjson_btn_viewashtml');
+		a.appendChild(document.createTextNode(a_text + '...'));
+		a._resp = response;
+		a.onclick = function()
+		{
+			var vah_title = ( $lang.placeholder ) ? 'View the response as HTML?' : $lang.get('ajax_badjson_html_confirm_title');
+			var vah_body = ( $lang.placeholder ) ? 'If the server\'s response was modified by an attacker to include malicious code, viewing the response as HTML might allow that malicious code to run. Only continue if you have inspected the response text and verified that it is safe.' : $lang.get('ajax_badjson_html_confirm_body');
+			var btn_confirm = $lang.placeholder ? 'View as HTML' : $lang.get('ajax_badjson_btn_viewashtml');
+			var btn_cancel = $lang.placeholder ? 'Cancel' : $lang.get('etc_cancel');
+			var mp = miniPromptMessage({
+					title: vah_title,
+					message: vah_body,
+					buttons: [
+						{
+							text: btn_confirm,
+							color: 'blue',
+							style: {
+								fontWeight: 'bold'
+							},
+							onclick: function() {
+								var mp = miniPromptGetParent(this);
+								var win = window.open('about:blank', 'invalidjson_htmlwin', 'width=550,height=400,status=no,toolbars=no,toolbar=no,address=no,scroll=yes');
+								win.document.write(mp._response);
+								win.document.close();
+								miniPromptDestroy(this);
+							}
+						},
+						{
+							text: btn_cancel,
+							onclick: function() {
+								miniPromptDestroy(this);
+							}
+						}
+					]
+				});
+			mp._response = this._resp;
+			return false;
+		}
+		a.href = '#';
+		p3.appendChild(a);
+		mainwin.appendChild(p3);
+	
+	//
+	// panel
+	//
+	
+		panel.style.backgroundColor = '#D0D0D0';
+		panel.style.textAlign = 'right';
+		panel.style.padding = '0 10px';
+		panel.style.lineHeight = '40px';
+		panel.style.width = '580px';
+		
+		var closer = document.createElement('input');
+		var btn_close = $lang.placeholder ? 'Close' : $lang.get('ajax_badjson_btn_close');
+		closer.type = 'button';
+		closer.value = btn_close;
+		closer.onclick = function()
+		{
+			var parentdiv = this.parentNode.parentNode;
+			if ( aclDisableTransitionFX )
+			{
+				parentdiv.parentNode.removeChild(parentdiv);
+				enlighten(aclDisableTransitionFX, 'invalidjsondarkener');
+			}
+			else
+			{
+				$(parentdiv).hide("blind", {}, 1000, function()
+					{
+						parentdiv.parentNode.removeChild(parentdiv);
+							enlighten(aclDisableTransitionFX, 'invalidjsondarkener');
+					});
+			}
+		}
+		panel.appendChild(closer);
+		
+	//
+	// put it together
+	//
+	
+		box.appendChild(mainwin);
+		box.appendChild(panel);
+		
+		// add it to the body to allow height/width calculation
+		
+		box.style.display = 'block';
+		box.style.position = 'absolute';
+		box.style.zIndex = getHighestZ() + 1;
+		domObjChangeOpac(0, box);
+		
+		var body = document.getElementsByTagName('body')[0];
+		body.appendChild(box);
+		
+		
+		// calculate position of the box
+		// box should be exactly 640px high, 480px wide
+		var top = ( getHeight() / 2 ) - ( $dynano(box).Height() / 2 ) + getScrollOffset();
+		var left = ( getWidth() / 2 ) - ( $dynano(box).Width() / 2 );
+		console.debug('top = %d, left = %d', top, left);
+		box.style.top = top + 'px';
+		box.style.left = left + 'px';
+		
+		// we have width and height, set display to none and reset opacity
+		if ( aclDisableTransitionFX )
+		{
+			domObjChangeOpac(100, box);
+			box.style.display = 'block';
+		}
+		else
+		{
+			box.style.display = 'none';
+			domObjChangeOpac(100, box);
+			
+			setTimeout(function()
+				{
+					$(box).show("blind", {}, 1000);
+				}, 1000);
+		}
+	return false;
 }
 
 /**
@@ -372,22 +372,22 @@
 
 function check_json_response(response)
 {
-  response = trim(response);
-  if ( response.substr(0, 1) == '{' && response.substr(response.length - 1, 1) == '}' )
-  {
-    return true;
-  }
-  return false;
+	response = trim(response);
+	if ( response.substr(0, 1) == '{' && response.substr(response.length - 1, 1) == '}' )
+	{
+		return true;
+	}
+	return false;
 }
 
 function ajaxEscape(text)
 {
-  /*
-  text = escape(text);
-  text = text.replace(/\+/g, '%2B', text);
-  */
-  text = window.encodeURIComponent(text);
-  return text;
+	/*
+	text = escape(text);
+	text = text.replace(/\+/g, '%2B', text);
+	*/
+	text = window.encodeURIComponent(text);
+	return text;
 }
 
 /**
@@ -397,60 +397,60 @@
 // Equivalent to PHP trim() function
 function trim(text)
 {
-  text = text.replace(/^([\s]+)/, '');
-  text = text.replace(/([\s]+)$/, '');
-  return text;
+	text = text.replace(/^([\s]+)/, '');
+	text = text.replace(/([\s]+)$/, '');
+	return text;
 }
 
 // Equivalent to PHP implode() function
 function implode(chr, arr)
 {
-  if ( typeof ( arr.toJSONString ) == 'function' )
-    delete(arr.toJSONString);
-  
-  var ret = '';
-  var c = 0;
-  for ( var i in arr )
-  {
-    if(i=='toJSONString')continue;
-    if ( c > 0 )
-      ret += chr;
-    ret += arr[i];
-    c++;
-  }
-  return ret;
+	if ( typeof ( arr.toJSONString ) == 'function' )
+		delete(arr.toJSONString);
+	
+	var ret = '';
+	var c = 0;
+	for ( var i in arr )
+	{
+		if(i=='toJSONString')continue;
+		if ( c > 0 )
+			ret += chr;
+		ret += arr[i];
+		c++;
+	}
+	return ret;
 }
 
 function form_fetch_field(form, name)
 {
-  var fields = form.getElementsByTagName('input');
-  if ( fields.length < 1 )
-    return false;
-  for ( var i = 0; i < fields.length; i++ )
-  {
-    var field = fields[i];
-    if ( field.name == name )
-      return field;
-  }
-  return false;
+	var fields = form.getElementsByTagName('input');
+	if ( fields.length < 1 )
+		return false;
+	for ( var i = 0; i < fields.length; i++ )
+	{
+		var field = fields[i];
+		if ( field.name == name )
+			return field;
+	}
+	return false;
 }
 
 function get_parent_form(o)
 {
-  if ( !o.parentNode )
-    return false;
-  if ( o.tagName == 'FORM' )
-    return o;
-  var p = o.parentNode;
-  while(true)
-  {
-    if ( p.tagName == 'FORM' )
-      return p;
-    else if ( !p )
-      return false;
-    else
-      p = p.parentNode;
-  }
+	if ( !o.parentNode )
+		return false;
+	if ( o.tagName == 'FORM' )
+		return o;
+	var p = o.parentNode;
+	while(true)
+	{
+		if ( p.tagName == 'FORM' )
+			return p;
+		else if ( !p )
+			return false;
+		else
+			p = p.parentNode;
+	}
 }
 
 /**
@@ -465,17 +465,17 @@
 
 function gen_sprite(path, width, height, xpos, ypos)
 {
-  var image = document.createElement('img');
-  image.src = cdnPath + '/images/spacer.gif';
-  image.width = String(width);
-  image.height = String(height);
-  image.style.backgroundImage = 'url(' + path + ')';
-  image.style.backgroundRepeat = 'no-repeat';
-  xpos = ( xpos == 0 ) ? '0' : '-' + String(xpos);
-  ypos = ( ypos == 0 ) ? '0' : '-' + String(ypos);
-  image.style.backgroundPosition = ypos + 'px ' + xpos + 'px';
-  
-  return image;
+	var image = document.createElement('img');
+	image.src = cdnPath + '/images/spacer.gif';
+	image.width = String(width);
+	image.height = String(height);
+	image.style.backgroundImage = 'url(' + path + ')';
+	image.style.backgroundRepeat = 'no-repeat';
+	xpos = ( xpos == 0 ) ? '0' : '-' + String(xpos);
+	ypos = ( ypos == 0 ) ? '0' : '-' + String(ypos);
+	image.style.backgroundPosition = ypos + 'px ' + xpos + 'px';
+	
+	return image;
 }
 
 /**
@@ -490,103 +490,103 @@
 
 function gen_sprite_html(path, width, height, xpos, ypos)
 {
-  var html = '<img src="' + scriptPath + '/images/spacer.gif" width="' + width + '" height="' + height + '" ';
-  xpos = ( xpos == 0 ) ? '0' : '-' + String(xpos);
-  ypos = ( ypos == 0 ) ? '0' : '-' + String(ypos);
-  html += 'style="background-image: url(' + path + '); background-repeat: no-repeat; background-position: ' + ypos + 'px ' + xpos + 'px;"';
-  html += ' />';
-  
-  return html;
+	var html = '<img src="' + scriptPath + '/images/spacer.gif" width="' + width + '" height="' + height + '" ';
+	xpos = ( xpos == 0 ) ? '0' : '-' + String(xpos);
+	ypos = ( ypos == 0 ) ? '0' : '-' + String(ypos);
+	html += 'style="background-image: url(' + path + '); background-repeat: no-repeat; background-position: ' + ypos + 'px ' + xpos + 'px;"';
+	html += ' />';
+	
+	return html;
 }
 
 function findParentForm(o)
 {
-  return get_parent_form(o);
+	return get_parent_form(o);
 }
 
 function domObjChangeOpac(opacity, id)
 {
-  if ( !id )
-    return false;
-  
-  var object = id.style;
-  object.opacity = (opacity / 100);
-  object.MozOpacity = (opacity / 100);
-  object.KhtmlOpacity = (opacity / 100);
-  object.filter = "alpha(opacity=" + opacity + ")";
+	if ( !id )
+		return false;
+	
+	var object = id.style;
+	object.opacity = (opacity / 100);
+	object.MozOpacity = (opacity / 100);
+	object.KhtmlOpacity = (opacity / 100);
+	object.filter = "alpha(opacity=" + opacity + ")";
 }
 
 function getScrollOffset(el)
 {
-  var position;
-  var s = el || self;
-  el = el || document;
-  if ( el.scrollTop )
-  {
-    position = el.scrollTop;
-  }
-  else if (s.pageYOffset)
-  {
-    position = self.pageYOffset;
-  }
-  else if (document.documentElement && document.documentElement.scrollTop)
-  {
-    position = document.documentElement.scrollTop;
-  }
-  else if (document.body)
-  {
-    position = document.body.scrollTop;
-  }
-  return position;
+	var position;
+	var s = el || self;
+	el = el || document;
+	if ( el.scrollTop )
+	{
+		position = el.scrollTop;
+	}
+	else if (s.pageYOffset)
+	{
+		position = self.pageYOffset;
+	}
+	else if (document.documentElement && document.documentElement.scrollTop)
+	{
+		position = document.documentElement.scrollTop;
+	}
+	else if (document.body)
+	{
+		position = document.body.scrollTop;
+	}
+	return position;
 }
 
 function setScrollOffset(offset)
 {
-  window.scroll(0, offset);
+	window.scroll(0, offset);
 }
 
 // Function to fade classes info-box, warning-box, error-box, etc.
 
 function fadeInfoBoxes()
 {
-  var divs = new Array();
-  d = document.getElementsByTagName('div');
-  j = 0;
-  for(var i in d)
-  {
-    if ( !d[i] )
-      continue;
-    if ( !d[i].tagName )
-      continue;
-    if(d[i].className=='info-box' || d[i].className=='error-box' || d[i].className=='warning-box' || d[i].className=='question-box')
-    {
-      divs[j] = d[i];
-      j++;
-    }
-  }
-  if(divs.length < 1) return;
-  load_component('fat');
-  for(i in divs)
-  {
-    if(!divs[i].id) divs[i].id = 'autofade_'+Math.floor(Math.random() * 100000);
-    switch(divs[i].className)
-    {
-      case 'info-box':
-      default:
-        from = '#3333FF';
-        break;
-      case 'error-box':
-        from = '#FF3333';
-        break;
-      case 'warning-box':
-        from = '#FFFF33';
-        break;
-      case 'question-box':
-        from = '#33FF33';
-        break;
-    }
-    Fat.fade_element(divs[i].id,30,2000,from,Fat.get_bgcolor(divs[i].id));
-  }
+	var divs = new Array();
+	d = document.getElementsByTagName('div');
+	j = 0;
+	for(var i in d)
+	{
+		if ( !d[i] )
+			continue;
+		if ( !d[i].tagName )
+			continue;
+		if(d[i].className=='info-box' || d[i].className=='error-box' || d[i].className=='warning-box' || d[i].className=='question-box')
+		{
+			divs[j] = d[i];
+			j++;
+		}
+	}
+	if(divs.length < 1) return;
+	load_component('fat');
+	for(i in divs)
+	{
+		if(!divs[i].id) divs[i].id = 'autofade_'+Math.floor(Math.random() * 100000);
+		switch(divs[i].className)
+		{
+			case 'info-box':
+			default:
+				from = '#3333FF';
+				break;
+			case 'error-box':
+				from = '#FF3333';
+				break;
+			case 'warning-box':
+				from = '#FFFF33';
+				break;
+			case 'question-box':
+				from = '#33FF33';
+				break;
+		}
+		Fat.fade_element(divs[i].id,30,2000,from,Fat.get_bgcolor(divs[i].id));
+	}
 }
 
 addOnloadHook(fadeInfoBoxes);
@@ -595,76 +595,76 @@
 
 function opacity(id, opacStart, opacEnd, millisec)
 {
-    var object = document.getElementById(id);
-    domOpacity(object, opacStart, opacEnd, millisec);
+		var object = document.getElementById(id);
+		domOpacity(object, opacStart, opacEnd, millisec);
 }
 
 var opacityDOMCache = {};
 function domOpacity(obj, opacStart, opacEnd, millisec) {
-    //speed for each frame
-    var speed = Math.round(millisec / 100);
-    var timer = 0;
-    
-    // unique ID for this animation
-    var uniqid = Math.floor(Math.random() * 1000000);
-    opacityDOMCache[uniqid] = obj;
+		//speed for each frame
+		var speed = Math.round(millisec / 100);
+		var timer = 0;
+		
+		// unique ID for this animation
+		var uniqid = Math.floor(Math.random() * 1000000);
+		opacityDOMCache[uniqid] = obj;
 
-    //determine the direction for the blending, if start and end are the same nothing happens
-    if(opacStart > opacEnd) {
-        for(i = opacStart; i >= opacEnd; i--) {
-            setTimeout("if ( opacityDOMCache["+uniqid+"] ) { var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj) }",(timer * speed));
-            timer++;
-        }
-    } else if(opacStart < opacEnd) {
-        for(i = opacStart; i <= opacEnd; i++)
-            {
-            setTimeout("if ( opacityDOMCache["+uniqid+"] ) { var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj); }",(timer * speed));
-            timer++;
-        }
-    }
-    setTimeout("delete(opacityDOMCache["+uniqid+"]);",(timer * speed));
+		//determine the direction for the blending, if start and end are the same nothing happens
+		if(opacStart > opacEnd) {
+				for(i = opacStart; i >= opacEnd; i--) {
+						setTimeout("if ( opacityDOMCache["+uniqid+"] ) { var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj) }",(timer * speed));
+						timer++;
+				}
+		} else if(opacStart < opacEnd) {
+				for(i = opacStart; i <= opacEnd; i++)
+						{
+						setTimeout("if ( opacityDOMCache["+uniqid+"] ) { var obj = opacityDOMCache["+uniqid+"]; domObjChangeOpac(" + i + ",obj); }",(timer * speed));
+						timer++;
+				}
+		}
+		setTimeout("delete(opacityDOMCache["+uniqid+"]);",(timer * speed));
 }
 
 function abortFades()
 {
-  opacityDOMCache = {};
+	opacityDOMCache = {};
 }
 
 // change the opacity for different browsers
 function changeOpac(opacity, id)
 {
-  var object = document.getElementById(id);
-  return domObjChangeOpac(opacity, object);
+	var object = document.getElementById(id);
+	return domObjChangeOpac(opacity, object);
 }
 
 // draw a white ajax-ey "loading" box over an object
 function whiteOutElement(el)
 {
-  var top = $dynano(el).Top();
-  var left = $dynano(el).Left();
-  var width = $dynano(el).Width();
-  var height = $dynano(el).Height();
-  
-  var blackout = document.createElement('div');
-  // using fixed here allows modal windows to be blacked out
-  blackout.style.position = ( el.style.position == 'fixed' ) ? 'fixed' : 'absolute';
-  blackout.style.top = top + 'px';
-  blackout.style.left = left + 'px';
-  blackout.style.width = width + 'px';
-  blackout.style.height = height + 'px';
-  
-  blackout.style.backgroundColor = '#FFFFFF';
-  domObjChangeOpac(60, blackout);
-  var background = ( $dynano(el).Height() < 48 ) ? 'url(' + scriptPath + '/images/loading.gif)' : 'url(' + scriptPath + '/includes/clientside/tinymce/themes/advanced/skins/default/img/progress.gif)';
-  blackout.style.backgroundImage = background;
-  blackout.style.backgroundPosition = 'center center';
-  blackout.style.backgroundRepeat = 'no-repeat';
-  blackout.style.zIndex = getHighestZ() + 2;
-  
-  var body = document.getElementsByTagName('body')[0];
-  body.appendChild(blackout);
-  
-  return blackout;
+	var top = $dynano(el).Top();
+	var left = $dynano(el).Left();
+	var width = $dynano(el).Width();
+	var height = $dynano(el).Height();
+	
+	var blackout = document.createElement('div');
+	// using fixed here allows modal windows to be blacked out
+	blackout.style.position = ( el.style.position == 'fixed' ) ? 'fixed' : 'absolute';
+	blackout.style.top = top + 'px';
+	blackout.style.left = left + 'px';
+	blackout.style.width = width + 'px';
+	blackout.style.height = height + 'px';
+	
+	blackout.style.backgroundColor = '#FFFFFF';
+	domObjChangeOpac(60, blackout);
+	var background = ( $dynano(el).Height() < 48 ) ? 'url(' + scriptPath + '/images/loading.gif)' : 'url(' + scriptPath + '/includes/clientside/tinymce/themes/advanced/skins/default/img/progress.gif)';
+	blackout.style.backgroundImage = background;
+	blackout.style.backgroundPosition = 'center center';
+	blackout.style.backgroundRepeat = 'no-repeat';
+	blackout.style.zIndex = getHighestZ() + 2;
+	
+	var body = document.getElementsByTagName('body')[0];
+	body.appendChild(blackout);
+	
+	return blackout;
 }
 
 /**
@@ -674,47 +674,47 @@
 
 function whiteOutReportSuccess(whitey, nodestroy_mp)
 {
-  whiteOutDestroyWithImage(whitey, cdnPath + '/images/check.png', nodestroy_mp);
+	whiteOutDestroyWithImage(whitey, cdnPath + '/images/check.png', nodestroy_mp);
 }
 
 function whiteOutReportFailure(whitey, nodestroy_mp)
 {
-  if ( typeof(nodestroy_mp) == undefined )
-    nodestroy_mp = true;
-    
-  whiteOutDestroyWithImage(whitey, cdnPath + '/images/checkbad.png', nodestroy_mp);
+	if ( typeof(nodestroy_mp) == undefined )
+		nodestroy_mp = true;
+		
+	whiteOutDestroyWithImage(whitey, cdnPath + '/images/checkbad.png', nodestroy_mp);
 }
 
 function whiteOutDestroyWithImage(whitey, image, nodestroy_mp)
 {
-  // fade the status indicator in and then out
-  whitey.style.backgroundImage = 'url(' + image + ')';
-  if ( whitey.isMiniPrompt && !nodestroy_mp )
-  {
-    setTimeout(function()
-      {
-        whiteOutDestroyOnMiniPrompt(whitey);
-      }, 500);
-    return true;
-  }
-  if ( aclDisableTransitionFX )
-  {
-    domObjChangeOpac(80, whitey);
-  }
-  else
-  {
-    domOpacity(whitey, 60, 80, 500);
-    setTimeout(function()
-      {
-        domOpacity(whitey, 60, 0, 500);
-      }, 750);
-  }
-  setTimeout(function()
-    {
-      if ( whitey )
-        if ( whitey.parentNode )
-          whitey.parentNode.removeChild(whitey);
-    }, 1250);
+	// fade the status indicator in and then out
+	whitey.style.backgroundImage = 'url(' + image + ')';
+	if ( whitey.isMiniPrompt && !nodestroy_mp )
+	{
+		setTimeout(function()
+			{
+				whiteOutDestroyOnMiniPrompt(whitey);
+			}, 500);
+		return true;
+	}
+	if ( aclDisableTransitionFX )
+	{
+		domObjChangeOpac(80, whitey);
+	}
+	else
+	{
+		domOpacity(whitey, 60, 80, 500);
+		setTimeout(function()
+			{
+				domOpacity(whitey, 60, 0, 500);
+			}, 750);
+	}
+	setTimeout(function()
+		{
+			if ( whitey )
+				if ( whitey.parentNode )
+					whitey.parentNode.removeChild(whitey);
+		}, 1250);
 }
 
 /**
@@ -729,144 +729,144 @@
 
 function whiteOutForm(form)
 {
-  if ( !form.getElementsByTagName )
-    return false;
-  
-  // disable all buttons
-  var buttons = form.getElementsByTagName('input');
-  for ( var i = 0; i < buttons.length; i++ )
-  {
-    if ( buttons[i].type == 'button' || buttons[i].type == 'submit' || buttons[i].type == 'image' )
-    {
-      buttons[i].disabled = 'disabled';
-      // ... but also make a hidden element to preserve any flags
-      var clone = buttons[i].cloneNode(true);
-      clone.type = 'hidden';
-      clone.disabled = false;
-      console.debug(clone);
-      form.appendChild(clone);
-    }
-  }
-  var buttons = form.getElementsByTagName('button');
-  for ( var i = 0; i < buttons.length; i++ )
-  {
-    buttons[i].disabled = 'disabled';
-    // ... but also make a hidden element to preserve any flags
-    if ( buttons[i].name )
-    {
-      var clone = document.createElement('input');
-      clone.type = 'hidden';
-      clone.name = buttons[i].name;
-      clone.value = ( buttons[i].value ) ? buttons[i].value : '';
-      form.appendChild(clone);
-    }
-  }
-  
-  return whiteOutElement(form);
+	if ( !form.getElementsByTagName )
+		return false;
+	
+	// disable all buttons
+	var buttons = form.getElementsByTagName('input');
+	for ( var i = 0; i < buttons.length; i++ )
+	{
+		if ( buttons[i].type == 'button' || buttons[i].type == 'submit' || buttons[i].type == 'image' )
+		{
+			buttons[i].disabled = 'disabled';
+			// ... but also make a hidden element to preserve any flags
+			var clone = buttons[i].cloneNode(true);
+			clone.type = 'hidden';
+			clone.disabled = false;
+			console.debug(clone);
+			form.appendChild(clone);
+		}
+	}
+	var buttons = form.getElementsByTagName('button');
+	for ( var i = 0; i < buttons.length; i++ )
+	{
+		buttons[i].disabled = 'disabled';
+		// ... but also make a hidden element to preserve any flags
+		if ( buttons[i].name )
+		{
+			var clone = document.createElement('input');
+			clone.type = 'hidden';
+			clone.name = buttons[i].name;
+			clone.value = ( buttons[i].value ) ? buttons[i].value : '';
+			form.appendChild(clone);
+		}
+	}
+	
+	return whiteOutElement(form);
 }
 
 // other DHTML functions
 
 function fetch_offset(obj)
 {
-  var left_offset = obj.offsetLeft;
-  var top_offset = obj.offsetTop;
-  while ((obj = obj.offsetParent) != null) {
-    left_offset += obj.offsetLeft;
-    top_offset += obj.offsetTop;
-  }
-  return { 'left' : left_offset, 'top' : top_offset };
+	var left_offset = obj.offsetLeft;
+	var top_offset = obj.offsetTop;
+	while ((obj = obj.offsetParent) != null) {
+		left_offset += obj.offsetLeft;
+		top_offset += obj.offsetTop;
+	}
+	return { 'left' : left_offset, 'top' : top_offset };
 }
 
 function fetch_dimensions(o) {
-  var w = o.offsetWidth;
-  var h = o.offsetHeight;
-  return { 'w' : w, 'h' : h };
+	var w = o.offsetWidth;
+	var h = o.offsetHeight;
+	return { 'w' : w, 'h' : h };
 }
 
 function findParentForm(o)
 {
-  if ( o.tagName == 'FORM' )
-    return o;
-  while(true)
-  {
-    o = o.parentNode;
-    if ( !o )
-      return false;
-    if ( o.tagName == 'FORM' )
-      return o;
-  }
-  return false;
+	if ( o.tagName == 'FORM' )
+		return o;
+	while(true)
+	{
+		o = o.parentNode;
+		if ( !o )
+			return false;
+		if ( o.tagName == 'FORM' )
+			return o;
+	}
+	return false;
 }
 
 function bannerOn(text)
 {
-  darken(true);
-  var thediv = document.createElement('div');
-  thediv.className = 'mdg-comment';
-  thediv.style.padding = '0';
-  thediv.style.marginLeft = '0';
-  thediv.style.position = 'absolute';
-  thediv.style.display = 'none';
-  thediv.style.padding = '4px';
-  thediv.style.fontSize = '14pt';
-  thediv.id = 'mdgDynamic_bannerDiv_'+Math.floor(Math.random() * 1000000);
-  thediv.innerHTML = text;
-  
-  var body = document.getElementsByTagName('body');
-  body = body[0];
-  body.appendChild(thediv);
-  body.style.cursor = 'wait';
-  
-  thediv.style.display = 'block';
-  dim = fetch_dimensions(thediv);
-  thediv.style.display = 'none';
-  bdim = { 'w' : getWidth(), 'h' : getHeight() };
-  so = getScrollOffset();
-  
-  var left = (bdim['w'] / 2) - ( dim['w'] / 2 );
-  
-  var top  = (bdim['h'] / 2);
-  top  = top - ( dim['h'] / 2 );
-  
-  top = top + so;
-  
-  thediv.style.top  = top  + 'px';
-  thediv.style.left = left + 'px';
-  
-  thediv.style.display = 'block';
-  
-  return thediv.id;
+	darken(true);
+	var thediv = document.createElement('div');
+	thediv.className = 'mdg-comment';
+	thediv.style.padding = '0';
+	thediv.style.marginLeft = '0';
+	thediv.style.position = 'absolute';
+	thediv.style.display = 'none';
+	thediv.style.padding = '4px';
+	thediv.style.fontSize = '14pt';
+	thediv.id = 'mdgDynamic_bannerDiv_'+Math.floor(Math.random() * 1000000);
+	thediv.innerHTML = text;
+	
+	var body = document.getElementsByTagName('body');
+	body = body[0];
+	body.appendChild(thediv);
+	body.style.cursor = 'wait';
+	
+	thediv.style.display = 'block';
+	dim = fetch_dimensions(thediv);
+	thediv.style.display = 'none';
+	bdim = { 'w' : getWidth(), 'h' : getHeight() };
+	so = getScrollOffset();
+	
+	var left = (bdim['w'] / 2) - ( dim['w'] / 2 );
+	
+	var top  = (bdim['h'] / 2);
+	top  = top - ( dim['h'] / 2 );
+	
+	top = top + so;
+	
+	thediv.style.top  = top  + 'px';
+	thediv.style.left = left + 'px';
+	
+	thediv.style.display = 'block';
+	
+	return thediv.id;
 }
 
 function bannerOff(id)
 {
-  e = document.getElementById(id);
-  if(!e) return;
-  e.innerHTML = '';
-  e.style.display = 'none';
-  var body = document.getElementsByTagName('body');
-  body = body[0];
-  body.style.cursor = 'default';
-  enlighten(true);
+	e = document.getElementById(id);
+	if(!e) return;
+	e.innerHTML = '';
+	e.style.display = 'none';
+	var body = document.getElementsByTagName('body');
+	body = body[0];
+	body.style.cursor = 'default';
+	enlighten(true);
 }
 
 function disableUnload(message)
 {
-  if(typeof message != 'string') message = 'You may want to save your changes first.';
-  window._unloadmsg = message;
-  window.onbeforeunload = function(e)
-  {
-    if ( !e )
-      e = window.event;
-    e.returnValue = window._unloadmsg;
-  }
+	if(typeof message != 'string') message = 'You may want to save your changes first.';
+	window._unloadmsg = message;
+	window.onbeforeunload = function(e)
+	{
+		if ( !e )
+			e = window.event;
+		e.returnValue = window._unloadmsg;
+	}
 }
 
 function enableUnload()
 {
-  window._unloadmsg = null;
-  window.onbeforeunload = null;
+	window._unloadmsg = null;
+	window.onbeforeunload = null;
 }
 
 /**
@@ -875,69 +875,69 @@
  */
 function getHighestZ()
 {
-  z = 0;
-  var divs = document.getElementsByTagName('div');
-  for(var i = 0; i < divs.length; i++)
-  {
-    if ( divs[i].style.zIndex > z && divs[i].style.display != 'none' )
-      z = divs[i].style.zIndex;
-  }
-  return parseInt(z);
+	z = 0;
+	var divs = document.getElementsByTagName('div');
+	for(var i = 0; i < divs.length; i++)
+	{
+		if ( divs[i].style.zIndex > z && divs[i].style.display != 'none' )
+			z = divs[i].style.zIndex;
+	}
+	return parseInt(z);
 }
 
 var shift = false;
 function isKeyPressed(event)
 {
-  if (event.shiftKey==1)
-  {
-    shift = true;
-  }
-  else
-  {
-    shift = false;
-  }
+	if (event.shiftKey==1)
+	{
+		shift = true;
+	}
+	else
+	{
+		shift = false;
+	}
 }
 
 function moveDiv(div, newparent)
 {
-  var backup = div;
-  var oldparent = div.parentNode;
-  oldparent.removeChild(div);
-  newparent.appendChild(backup);
+	var backup = div;
+	var oldparent = div.parentNode;
+	oldparent.removeChild(div);
+	newparent.appendChild(backup);
 }
 
 var busyBannerID;
 function goBusy(msg)
 {
-  if(!msg) msg = 'Please wait...';
-  body = document.getElementsByTagName('body');
-  body = body[0];
-  body.style.cursor = 'wait';
-  busyBannerID = bannerOn(msg);
+	if(!msg) msg = 'Please wait...';
+	body = document.getElementsByTagName('body');
+	body = body[0];
+	body.style.cursor = 'wait';
+	busyBannerID = bannerOn(msg);
 }
 
 function unBusy()
 {
-  body = document.getElementsByTagName('body');
-  body = body[0];
-  body.style.cursor = 'default';
-  bannerOff(busyBannerID);
+	body = document.getElementsByTagName('body');
+	body = body[0];
+	body.style.cursor = 'default';
+	bannerOff(busyBannerID);
 }
 
 function setAjaxLoading()
 {
-  if ( document.getElementById('ajaxloadicon') )
-  {
-    document.getElementById('ajaxloadicon').src=ajax_load_icon;
-  }
+	if ( document.getElementById('ajaxloadicon') )
+	{
+		document.getElementById('ajaxloadicon').src=ajax_load_icon;
+	}
 }
 
 function unsetAjaxLoading()
 {
-  if ( document.getElementById('ajaxloadicon') )
-  {
-    document.getElementById('ajaxloadicon').src=cdnPath + '/images/spacer.gif';
-  }
+	if ( document.getElementById('ajaxloadicon') )
+	{
+		document.getElementById('ajaxloadicon').src=cdnPath + '/images/spacer.gif';
+	}
 }
 
 function readCookie(name) {var nameEQ = name + "=";var ca = document.cookie.split(';');for(var i=0;i < ca.length;i++){var c = ca[i];while (c.charAt(0)==' ') c = c.substring(1,c.length);if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);}return null;}
@@ -952,7 +952,7 @@
 // Included only for API-compatibility
 function ajaxPromptAdminAuth(call_on_ok, level)
 {
-  ajaxLoginInit(call_on_ok, level);
+	ajaxLoginInit(call_on_ok, level);
 }
 
 /**
@@ -964,22 +964,22 @@
 
 function insertAfter(parent, baby, bigsister)
 {
-  try
-  {
-    if ( parent.childNodes[parent.childNodes.length-1] == bigsister )
-      parent.appendChild(baby);
-    else
-      parent.insertBefore(baby, bigsister.nextSibling);
-  }
-  catch(e)
-  {
-    alert(e.toString());
-    if ( window.console )
-    {
-      // Firebug support
-      window.console.warn(e);
-    }
-  }
+	try
+	{
+		if ( parent.childNodes[parent.childNodes.length-1] == bigsister )
+			parent.appendChild(baby);
+		else
+			parent.insertBefore(baby, bigsister.nextSibling);
+	}
+	catch(e)
+	{
+		alert(e.toString());
+		if ( window.console )
+		{
+			// Firebug support
+			window.console.warn(e);
+		}
+	}
 }
 
 /**
@@ -990,7 +990,7 @@
 
 function validateEmail(email)
 {
-  return ( email.match(/^(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)$/) ) ? true : false;
+	return ( email.match(/^(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)$/) ) ? true : false;
 }
 
 /**
@@ -1001,8 +1001,8 @@
 
 function validateUsername(username)
 {
-  var regex = new RegExp('^[^<>&\?\'"%\n\r/]+$', '');
-  return ( username.match(regex) ) ? true : false;
+	var regex = new RegExp('^[^<>&\?\'"%\n\r/]+$', '');
+	return ( username.match(regex) ) ? true : false;
 }
 
 /*
@@ -1010,29 +1010,29 @@
  */
 
 function getHeight() {
-  var myHeight = 0;
-  if( typeof( window.innerWidth ) == 'number' ) {
-    myHeight = window.innerHeight;
-  } else if( document.documentElement &&
-      ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
-    myHeight = document.documentElement.clientHeight;
-  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
-    myHeight = document.body.clientHeight;
-  }
-  return myHeight;
+	var myHeight = 0;
+	if( typeof( window.innerWidth ) == 'number' ) {
+		myHeight = window.innerHeight;
+	} else if( document.documentElement &&
+			( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
+		myHeight = document.documentElement.clientHeight;
+	} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
+		myHeight = document.body.clientHeight;
+	}
+	return myHeight;
 }
 
 function getWidth() {
-  var myWidth = 0;
-  if( typeof( window.innerWidth ) == 'number' ) {
-    myWidth = window.innerWidth;
-  } else if( document.documentElement &&
-      ( document.documentElement.clientWidth || document.documentElement.clientWidth ) ) {
-    myWidth = document.documentElement.clientWidth;
-  } else if( document.body && ( document.body.clientWidth || document.body.clientWidth ) ) {
-    myWidth = document.body.clientWidth;
-  }
-  return myWidth;
+	var myWidth = 0;
+	if( typeof( window.innerWidth ) == 'number' ) {
+		myWidth = window.innerWidth;
+	} else if( document.documentElement &&
+			( document.documentElement.clientWidth || document.documentElement.clientWidth ) ) {
+		myWidth = document.documentElement.clientWidth;
+	} else if( document.body && ( document.body.clientWidth || document.body.clientWidth ) ) {
+		myWidth = document.body.clientWidth;
+	}
+	return myWidth;
 }
 
 /**
@@ -1043,45 +1043,45 @@
 
 function sanitize_page_id(page_id)
 {
-  // Remove character escapes
-  page_id = dirtify_page_id(page_id);
+	// Remove character escapes
+	page_id = dirtify_page_id(page_id);
 
-  var regex = new RegExp('[A-Za-z0-9\\[\\]\./:;\(\)@_-]', 'g');
-  pid_clean = page_id.replace(regex, 'X');
-  var pid_dirty = [];
-  for ( var i = 0; i < pid_clean.length; i++ )
-    pid_dirty[i] = pid_clean.substr(i, 1);
+	var regex = new RegExp('[A-Za-z0-9\\[\\]\./:;\(\)@_-]', 'g');
+	pid_clean = page_id.replace(regex, 'X');
+	var pid_dirty = [];
+	for ( var i = 0; i < pid_clean.length; i++ )
+		pid_dirty[i] = pid_clean.substr(i, 1);
 
-  for ( var i = 0; i < pid_dirty.length; i++ )
-  {
-    var chr = pid_dirty[i];
-    if ( chr == 'X' )
-      continue;
-    var cid = chr.charCodeAt(0);
-    cid = cid.toString(16).toUpperCase();
-    if ( cid.length < 2 )
-    {
-      cid = '0' + cid;
-    }
-    pid_dirty[i] = "." + cid;
-  }
-  
-  var pid_chars = [];
-  for ( var i = 0; i < page_id.length; i++ )
-    pid_chars[i] = page_id.substr(i, 1);
-  
-  var page_id_cleaned = '';
+	for ( var i = 0; i < pid_dirty.length; i++ )
+	{
+		var chr = pid_dirty[i];
+		if ( chr == 'X' )
+			continue;
+		var cid = chr.charCodeAt(0);
+		cid = cid.toString(16).toUpperCase();
+		if ( cid.length < 2 )
+		{
+			cid = '0' + cid;
+		}
+		pid_dirty[i] = "." + cid;
+	}
+	
+	var pid_chars = [];
+	for ( var i = 0; i < page_id.length; i++ )
+		pid_chars[i] = page_id.substr(i, 1);
+	
+	var page_id_cleaned = '';
 
-  for ( var id in pid_chars )
-  {
-    var chr = pid_chars[id];
-    if ( pid_dirty[id] == 'X' )
-      page_id_cleaned += chr;
-    else
-      page_id_cleaned += pid_dirty[id];
-  }
-  
-  return page_id_cleaned;
+	for ( var id in pid_chars )
+	{
+		var chr = pid_chars[id];
+		if ( pid_dirty[id] == 'X' )
+			page_id_cleaned += chr;
+		else
+			page_id_cleaned += pid_dirty[id];
+	}
+	
+	return page_id_cleaned;
 }
 
 /**
@@ -1092,64 +1092,64 @@
 
 function dirtify_page_id(page_id)
 {
-  // First, replace spaces with underscores
-  page_id = page_id.replace(/ /g, '_');
+	// First, replace spaces with underscores
+	page_id = page_id.replace(/ /g, '_');
 
-  var matches = page_id.match(/\.[A-Fa-f0-9][A-Fa-f0-9]/g);
-  
-  if ( matches != null )
-  {
-    for ( var i = 0; i < matches.length; i++ )
-    {
-      var match = matches[i];
-      var byt = (match.substr(1)).toUpperCase();
-      var code = eval("0x" + byt);
-      var regex = new RegExp('\\.' + byt, 'g');
-      page_id = page_id.replace(regex, String.fromCharCode(code));
-    }
-  }
-  
-  return page_id;
+	var matches = page_id.match(/\.[A-Fa-f0-9][A-Fa-f0-9]/g);
+	
+	if ( matches != null )
+	{
+		for ( var i = 0; i < matches.length; i++ )
+		{
+			var match = matches[i];
+			var byt = (match.substr(1)).toUpperCase();
+			var code = eval("0x" + byt);
+			var regex = new RegExp('\\.' + byt, 'g');
+			page_id = page_id.replace(regex, String.fromCharCode(code));
+		}
+	}
+	
+	return page_id;
 }
 
 /*
-    the getElementsByClassName function I pilfered from this guy.  It's
-    a useful function that'll return any/all tags with a specific css class.
+		the getElementsByClassName function I pilfered from this guy.  It's
+		a useful function that'll return any/all tags with a specific css class.
 
-    Written by Jonathan Snook, http://www.snook.ca/jonathan
-    Add-ons by Robert Nyman, http://www.robertnyman.com
-    
-    Modified to match all elements that match the class name plus an integer after the name
-    This is used in Enano to allow sliding sidebar widgets that use their own CSS
+		Written by Jonathan Snook, http://www.snook.ca/jonathan
+		Add-ons by Robert Nyman, http://www.robertnyman.com
+		
+		Modified to match all elements that match the class name plus an integer after the name
+		This is used in Enano to allow sliding sidebar widgets that use their own CSS
 */
 function getElementsByClassName(oElm, strTagName, strClassName)
 {
-    // first it gets all of the specified tags
-    var arrElements = (strTagName == "*" && document.all) ? document.all : oElm.getElementsByTagName(strTagName);
-    
-    // then it sets up an array that'll hold the results
-    var arrReturnElements = new Array();
+		// first it gets all of the specified tags
+		var arrElements = (strTagName == "*" && document.all) ? document.all : oElm.getElementsByTagName(strTagName);
+		
+		// then it sets up an array that'll hold the results
+		var arrReturnElements = new Array();
 
-    // some regex stuff you don't need to worry about
-    strClassName = strClassName.replace(/\-/g, "\\-");
+		// some regex stuff you don't need to worry about
+		strClassName = strClassName.replace(/\-/g, "\\-");
 
-    var oRegExp = new RegExp("(^|\\s)" + strClassName + "([0-9]*)(\\s|$)");
-    var oElement;
-    
-    // now it iterates through the elements it grabbed above
-    for(var i=0; i<arrElements.length; i++)
-    {
-        oElement = arrElements[i];
+		var oRegExp = new RegExp("(^|\\s)" + strClassName + "([0-9]*)(\\s|$)");
+		var oElement;
+		
+		// now it iterates through the elements it grabbed above
+		for(var i=0; i<arrElements.length; i++)
+		{
+				oElement = arrElements[i];
 
-        // if the class matches what we're looking for it ads to the results array
-        if(oElement.className.match(oRegExp))
-        {
-            arrReturnElements.push(oElement);
-        }
-    }
+				// if the class matches what we're looking for it ads to the results array
+				if(oElement.className.match(oRegExp))
+				{
+						arrReturnElements.push(oElement);
+				}
+		}
 
-    // then it kicks the results back to us
-    return (arrReturnElements)
+		// then it kicks the results back to us
+		return (arrReturnElements)
 }
 
 /**
@@ -1158,11 +1158,11 @@
 
 function in_array(needle, haystack)
 {
-  for(var i in haystack)
-  {
-    if(haystack[i] == needle) return i;
-  }
-  return false;
+	for(var i in haystack)
+	{
+		if(haystack[i] == needle) return i;
+	}
+	return false;
 }
 
 /**
@@ -1172,5 +1172,5 @@
 
 function unix_time()
 {
-  return parseInt((new Date()).getTime()/1000);
+	return parseInt((new Date()).getTime()/1000);
 }
--- a/includes/clientside/static/grippy.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/grippy.js	Sun Mar 28 23:10:46 2010 -0400
@@ -2,90 +2,90 @@
 
 function taStartDrag()
 {
-  obj = this;
-  current_ta = obj.previousSibling;
-  startmouseX = mouseX;
-  startmouseY = mouseY;
-  startScroll = getScrollOffset();
-  is_dragging = true;
-  startwidth  = getElementWidth(current_ta.id);
-  startheight = getElementHeight(current_ta.id);
-  var body = document.getElementsByTagName('body');
-  body = body[0];
-  body.style.cursor = 's-resize';
+	obj = this;
+	current_ta = obj.previousSibling;
+	startmouseX = mouseX;
+	startmouseY = mouseY;
+	startScroll = getScrollOffset();
+	is_dragging = true;
+	startwidth  = getElementWidth(current_ta.id);
+	startheight = getElementHeight(current_ta.id);
+	var body = document.getElementsByTagName('body');
+	body = body[0];
+	body.style.cursor = 's-resize';
 }
 function taInDrag()
 {
-  if(!is_dragging) return;
-  cw = startwidth;
-  ch = startheight;
-  mx = mouseX;
-  my = mouseY + getScrollOffset() - startScroll;
-  ch = -6 + ch + ( my - startmouseY );
-  current_ta.style.height = ch+'px';
-  if(do_width)
-  {
-    current_ta.style.width  = mx+'px';
-    current_ta.nextSibling.style.width  = mx+'px';
-  }
+	if(!is_dragging) return;
+	cw = startwidth;
+	ch = startheight;
+	mx = mouseX;
+	my = mouseY + getScrollOffset() - startScroll;
+	ch = -6 + ch + ( my - startmouseY );
+	current_ta.style.height = ch+'px';
+	if(do_width)
+	{
+		current_ta.style.width  = mx+'px';
+		current_ta.nextSibling.style.width  = mx+'px';
+	}
 }
 function taCloseDrag()
 {
-  is_dragging = false;
-  current_ta = false;
-  body = document.getElementsByTagName('body');
-  body = body[0];
-  body.style.cursor = 'default';
+	is_dragging = false;
+	current_ta = false;
+	body = document.getElementsByTagName('body');
+	body = body[0];
+	body.style.cursor = 'default';
 }
 
 var grippied_textareas = new Array();
 
 function initTextareas()
 {
-  var textareas = document.getElementsByTagName('textarea');
-  for (i = 0;i < textareas.length;i++)
-  {
-    if(!textareas[i].id)
-      textareas[i].id = 'autoTextArea_'+Math.floor(Math.random()*100000);
-    cta = textareas[i];
-    var divchk = ( in_array(cta.id, grippied_textareas) ) ? false : true;
-    if(divchk)
-    {
-      grippied_textareas.push(cta.id);
-      makeGrippy(cta);
-    }
-  }
+	var textareas = document.getElementsByTagName('textarea');
+	for (i = 0;i < textareas.length;i++)
+	{
+		if(!textareas[i].id)
+			textareas[i].id = 'autoTextArea_'+Math.floor(Math.random()*100000);
+		cta = textareas[i];
+		var divchk = ( in_array(cta.id, grippied_textareas) ) ? false : true;
+		if(divchk)
+		{
+			grippied_textareas.push(cta.id);
+			makeGrippy(cta);
+		}
+	}
 }
 
 function makeGrippy(cta)
 {
-  var thediv = document.createElement('div');
-  thediv.style.backgroundColor = '#ceceed';
-  thediv.style.backgroundImage = 'url('+scriptPath+'/images/grippy.gif)';
-  thediv.style.backgroundPosition = 'bottom right';
-  thediv.style.backgroundRepeat = 'no-repeat';
-  thediv.style.width = getElementWidth(cta.id)+'px';
-  thediv.style.cursor = 's-resize';
-  thediv.style.className = 'ThisIsATextareaGrippy';
-  thediv.id = 'autoGrippy_'+Math.floor(Math.random()*100000);
-  thediv.style.height = '12px';
-  thediv.onmousedown = taStartDrag;
-  thediv.style.border = '1px solid #0000A0';
-  if(cta.style.marginBottom)
-  {
-    thediv.style.marginBottom = cta.style.marginBottom;
-    cta.style.marginBottom = '0';
-  }
-  if(cta.style.marginLeft)
-  {
-    thediv.style.marginLeft = cta.style.marginLeft;
-  }
-  if(cta.style.marginRight)
-  {
-    thediv.style.marginRight = cta.style.marginRight;
-  }
-  document.onmouseup = taCloseDrag;
-  if(cta.nextSibling) cta.parentNode.insertBefore(thediv, cta.nextSibling);
-  else cta.parentNode.appendChild(thediv);
+	var thediv = document.createElement('div');
+	thediv.style.backgroundColor = '#ceceed';
+	thediv.style.backgroundImage = 'url('+scriptPath+'/images/grippy.gif)';
+	thediv.style.backgroundPosition = 'bottom right';
+	thediv.style.backgroundRepeat = 'no-repeat';
+	thediv.style.width = getElementWidth(cta.id)+'px';
+	thediv.style.cursor = 's-resize';
+	thediv.style.className = 'ThisIsATextareaGrippy';
+	thediv.id = 'autoGrippy_'+Math.floor(Math.random()*100000);
+	thediv.style.height = '12px';
+	thediv.onmousedown = taStartDrag;
+	thediv.style.border = '1px solid #0000A0';
+	if(cta.style.marginBottom)
+	{
+		thediv.style.marginBottom = cta.style.marginBottom;
+		cta.style.marginBottom = '0';
+	}
+	if(cta.style.marginLeft)
+	{
+		thediv.style.marginLeft = cta.style.marginLeft;
+	}
+	if(cta.style.marginRight)
+	{
+		thediv.style.marginRight = cta.style.marginRight;
+	}
+	document.onmouseup = taCloseDrag;
+	if(cta.nextSibling) cta.parentNode.insertBefore(thediv, cta.nextSibling);
+	else cta.parentNode.appendChild(thediv);
 }
 
--- a/includes/clientside/static/json.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/json.js	Sun Mar 28 23:10:46 2010 -0400
@@ -1,182 +1,182 @@
 /*
-    json.js
-    2007-03-20
+		json.js
+		2007-03-20
 
-    All of the code contained within this file is released into
-    the public domain. Optionally, you may distribute this code
-    under the terms of the GNU General Public License as well
-    (public domain licensing allows this).
-    
+		All of the code contained within this file is released into
+		the public domain. Optionally, you may distribute this code
+		under the terms of the GNU General Public License as well
+		(public domain licensing allows this).
+		
 */
 
 function toJSONString(input)
 {
-  if ( window.JSON )
-  {
-    return window.JSON.stringify(input);
-  }
-  var m = {
-          '\b': '\\b',
-          '\t': '\\t',
-          '\n': '\\n',
-          '\f': '\\f',
-          '\r': '\\r',
-          '"' : '\\"',
-          '\\': '\\\\'
-          };
-  var t = typeof(input);
-  switch(t)
-  {
-    case 'string':
-      if (/["\\\x00-\x1f]/.test(input))
-      {
-          return '"' + input.replace(/([\x00-\x1f\\"])/g, function(a, b)
-            {
-              var c = m[b];
-              if (c) {
-                  return c;
-              }
-              c = b.charCodeAt();
-              return '\\u00' +
-                  Math.floor(c / 16).toString(16) +
-                  (c % 16).toString(16);
-          }) + '"';
-      }
-      return '"' + input + '"';
-      break;
-    case 'array':
-      var a = ['['],  
-            b,          
-            i,          
-            l = input.length,
-            v;          
+	if ( window.JSON )
+	{
+		return window.JSON.stringify(input);
+	}
+	var m = {
+					'\b': '\\b',
+					'\t': '\\t',
+					'\n': '\\n',
+					'\f': '\\f',
+					'\r': '\\r',
+					'"' : '\\"',
+					'\\': '\\\\'
+					};
+	var t = typeof(input);
+	switch(t)
+	{
+		case 'string':
+			if (/["\\\x00-\x1f]/.test(input))
+			{
+					return '"' + input.replace(/([\x00-\x1f\\"])/g, function(a, b)
+						{
+							var c = m[b];
+							if (c) {
+									return c;
+							}
+							c = b.charCodeAt();
+							return '\\u00' +
+									Math.floor(c / 16).toString(16) +
+									(c % 16).toString(16);
+					}) + '"';
+			}
+			return '"' + input + '"';
+			break;
+		case 'array':
+			var a = ['['],  
+						b,          
+						i,          
+						l = input.length,
+						v;          
 
-        var p = function (s) {
+				var p = function (s) {
 
-            if (b) {
-                a.push(',');
-            }
-            a.push(s);
-            b = true;
-        }
+						if (b) {
+								a.push(',');
+						}
+						a.push(s);
+						b = true;
+				}
 
-        for (i = 0; i < l; i += 1) {
-            v = input[i];
-            switch (typeof v) {
-            case 'object':
-              if (v) {
-                p(toJSONString(v));
-              } else {
-                p("null");
-              }
-              break;
-            case 'array':
-            case 'string':
-            case 'number':
-            case 'boolean':
-                p(toJSONString(v));
-            }
-        }
+				for (i = 0; i < l; i += 1) {
+						v = input[i];
+						switch (typeof v) {
+						case 'object':
+							if (v) {
+								p(toJSONString(v));
+							} else {
+								p("null");
+							}
+							break;
+						case 'array':
+						case 'string':
+						case 'number':
+						case 'boolean':
+								p(toJSONString(v));
+						}
+				}
 
-        a.push(']');
-        return a.join('');
-      break;
-    case 'date':
-      var f = function (n)
-      {
-        return n < 10 ? '0' + n : n;
-      }
-      return '"' + input.getFullYear() + '-' +
-                 f(input.getMonth() + 1) + '-' +
-                 f(input.getDate()) + 'T' +
-                 f(input.getHours()) + ':' +
-                 f(input.getMinutes()) + ':' +
-                 f(input.getSeconds()) + '"';
-                 
-    case 'boolean':
-      return String(input);
-      break;
-    case 'number':
-      return isFinite(input) ? String(input) : "null";
-      break;
-    case 'object':
-      var a = ['{'],  
-          b,          
-          k,          
-          v;          
+				a.push(']');
+				return a.join('');
+			break;
+		case 'date':
+			var f = function (n)
+			{
+				return n < 10 ? '0' + n : n;
+			}
+			return '"' + input.getFullYear() + '-' +
+ 								f(input.getMonth() + 1) + '-' +
+ 								f(input.getDate()) + 'T' +
+ 								f(input.getHours()) + ':' +
+ 								f(input.getMinutes()) + ':' +
+ 								f(input.getSeconds()) + '"';
+ 								
+		case 'boolean':
+			return String(input);
+			break;
+		case 'number':
+			return isFinite(input) ? String(input) : "null";
+			break;
+		case 'object':
+			var a = ['{'],  
+					b,          
+					k,          
+					v;          
 
-      var p = function (s)
-      {
-        if (b)
-        {
-          a.push(',');
-        }
-        a.push(toJSONString(k), ':', s);
-        b = true;
-      }
+			var p = function (s)
+			{
+				if (b)
+				{
+					a.push(',');
+				}
+				a.push(toJSONString(k), ':', s);
+				b = true;
+			}
 
-      for (k in input) 
-      {
-        if (input.hasOwnProperty(k))
-        {
-          v = input[k];
-          switch (typeof v) {
+			for (k in input) 
+			{
+				if (input.hasOwnProperty(k))
+				{
+					v = input[k];
+					switch (typeof v) {
 
-            case 'object':
-              if (v) {
-                p(toJSONString(v));
-              } else {
-                p("null");
-              }
-              break;
-            case 'string':
-            case 'number':
-            case 'boolean':
-              p(toJSONString(v));
-              break;
-          }
-        }
-      }
+						case 'object':
+							if (v) {
+								p(toJSONString(v));
+							} else {
+								p("null");
+							}
+							break;
+						case 'string':
+						case 'number':
+						case 'boolean':
+							p(toJSONString(v));
+							break;
+					}
+				}
+			}
 
-      a.push('}');
-      return a.join('');
-      break;
-  }
+			a.push('}');
+			return a.join('');
+			break;
+	}
 }
 
 function parseJSON(string, filter)
 {
-  if ( window.JSON )
-  {
-    return window.JSON.parse(string);
-  }
-  
-  try {
-    if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
-            test(string))
-    {
-  
-        var j = eval('(' + string + ')');
-        if (typeof filter === 'function') {
-  
-            function walk(k, v)
-            {
-                if (v && typeof v === 'object') {
-                    for (var i in v) {
-                        if (v.hasOwnProperty(i)) {
-                            v[i] = walk(i, v[i]);
-                        }
-                    }
-                }
-                return filter(k, v);
-            }
-  
-            j = walk('', j);
-        }
-        return j;
-    }
-  } catch (e) {
-  
-  }
-  throw new SyntaxError("parseJSON");
+	if ( window.JSON )
+	{
+		return window.JSON.parse(string);
+	}
+	
+	try {
+		if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
+						test(string))
+		{
+	
+				var j = eval('(' + string + ')');
+				if (typeof filter === 'function') {
+	
+						function walk(k, v)
+						{
+								if (v && typeof v === 'object') {
+										for (var i in v) {
+												if (v.hasOwnProperty(i)) {
+														v[i] = walk(i, v[i]);
+												}
+										}
+								}
+								return filter(k, v);
+						}
+	
+						j = walk('', j);
+				}
+				return j;
+		}
+	} catch (e) {
+	
+	}
+	throw new SyntaxError("parseJSON");
 }
--- a/includes/clientside/static/l10n.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/l10n.js	Sun Mar 28 23:10:46 2010 -0400
@@ -4,85 +4,85 @@
 
 var Language = function(lang_id)
 {
-  var have_lang = false;
-  
-  if ( typeof(enano_lang) == 'object' )
-  {
-    if ( typeof(enano_lang[lang_id]) == 'object' )
-    {
-      have_lang = true;
-    }
-  }
-  if ( !have_lang )
-  {
-    // load the language file
-    load_show_win('strings');
-    console.info('Loading language ' + lang_id + ' via AJAX synchronous request');
-    var ajax = ajaxMakeXHR();
-    var uri = makeUrlNS('Special', 'LangExportJSON/' + lang_id, enano_version);
-    ajax.open('GET', uri, false);
-    ajax.send(null);
-    if ( ajax.readyState == 4 && ajax.status == 200 )
-    {
-      eval_global(ajax.responseText);
-    }
-    load_hide_win();
-  }
-  
-  if ( typeof(enano_lang) != 'object' )
-    return false;
-  if ( typeof(enano_lang[lang_id]) != 'object' )
-    return false;
-  this.strings = enano_lang[lang_id];
-  this.lang_id = lang_id;
-  this.placeholder = false;
-  
-  this.get = function(string_id, subst)
-  {
-    var catname = string_id.substr(0, string_id.indexOf('_'));
-    var string_name = string_id.substr(string_id.indexOf('_') + 1);
-    if ( typeof(this.strings[catname]) != 'object' )
-      return string_id;
-    if ( typeof(this.strings[catname][string_name]) != 'string' )
-      return string_id;
-    return this.perform_subst(this.strings[catname][string_name], subst);
-  }
-  
-  this.perform_subst = function(str, subst)
-  {
-    var this_regex = /%this\.([a-z0-9_]+)%/;
-    var match;
-    while ( str.match(this_regex) )
-    {
-      match = str.match(this_regex);
-      str = str.replace(match[0], this.get(match[1]));
-    }
-    // hackish workaround for %config.*%
-    str = str.replace(/%config\.([a-z0-9_]+)%/g, '%$1%');
-    if ( typeof(subst) == 'object' )
-    {
-      for ( var i in subst )
-      {
-        if ( !i.match(/^([a-z0-9_]+)$/) )
-          continue;
-        var regex = new RegExp('%' + i + '%', 'g');
-        str = str.replace(regex, subst[i]);
-      }
-    }
-    return str;
-  }
-  
+	var have_lang = false;
+	
+	if ( typeof(enano_lang) == 'object' )
+	{
+		if ( typeof(enano_lang[lang_id]) == 'object' )
+		{
+			have_lang = true;
+		}
+	}
+	if ( !have_lang )
+	{
+		// load the language file
+		load_show_win('strings');
+		console.info('Loading language ' + lang_id + ' via AJAX synchronous request');
+		var ajax = ajaxMakeXHR();
+		var uri = makeUrlNS('Special', 'LangExportJSON/' + lang_id, enano_version);
+		ajax.open('GET', uri, false);
+		ajax.send(null);
+		if ( ajax.readyState == 4 && ajax.status == 200 )
+		{
+			eval_global(ajax.responseText);
+		}
+		load_hide_win();
+	}
+	
+	if ( typeof(enano_lang) != 'object' )
+		return false;
+	if ( typeof(enano_lang[lang_id]) != 'object' )
+		return false;
+	this.strings = enano_lang[lang_id];
+	this.lang_id = lang_id;
+	this.placeholder = false;
+	
+	this.get = function(string_id, subst)
+	{
+		var catname = string_id.substr(0, string_id.indexOf('_'));
+		var string_name = string_id.substr(string_id.indexOf('_') + 1);
+		if ( typeof(this.strings[catname]) != 'object' )
+			return string_id;
+		if ( typeof(this.strings[catname][string_name]) != 'string' )
+			return string_id;
+		return this.perform_subst(this.strings[catname][string_name], subst);
+	}
+	
+	this.perform_subst = function(str, subst)
+	{
+		var this_regex = /%this\.([a-z0-9_]+)%/;
+		var match;
+		while ( str.match(this_regex) )
+		{
+			match = str.match(this_regex);
+			str = str.replace(match[0], this.get(match[1]));
+		}
+		// hackish workaround for %config.*%
+		str = str.replace(/%config\.([a-z0-9_]+)%/g, '%$1%');
+		if ( typeof(subst) == 'object' )
+		{
+			for ( var i in subst )
+			{
+				if ( !i.match(/^([a-z0-9_]+)$/) )
+					continue;
+				var regex = new RegExp('%' + i + '%', 'g');
+				str = str.replace(regex, subst[i]);
+			}
+		}
+		return str;
+	}
+	
 }
 
 var language_onload = function()
 {
-  $lang = new Language(ENANO_LANG_ID);
+	$lang = new Language(ENANO_LANG_ID);
 }
 
 var $lang = {
-  get: function(t, s) {
-    language_onload();
-    return $lang.get(t, s);
-  },
-  placeholder: true
+	get: function(t, s) {
+		language_onload();
+		return $lang.get(t, s);
+	},
+	placeholder: true
 };
--- a/includes/clientside/static/loader.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/loader.js	Sun Mar 28 23:10:46 2010 -0400
@@ -4,30 +4,30 @@
 
 function mdgInnerLoader(e)
 {
-  window.onkeydown = isKeyPressed;
-  window.onkeyup = function(e) { isKeyPressed(e); };
-  
-  if ( typeof(dbx_set_key) == 'function')
-  {
-    dbx_set_key();
-  }
-  
-  runOnloadHooks(e);
+	window.onkeydown = isKeyPressed;
+	window.onkeyup = function(e) { isKeyPressed(e); };
+	
+	if ( typeof(dbx_set_key) == 'function')
+	{
+		dbx_set_key();
+	}
+	
+	runOnloadHooks(e);
 }
 
 // Enano's main init function.
 function enano_init(e)
 {
-  mdgInnerLoader(e);
-  
-  // we're loaded; set flags to true
-  console.info('Enano::JS runtime: system init complete');
-  onload_complete = true;
+	mdgInnerLoader(e);
+	
+	// we're loaded; set flags to true
+	console.info('Enano::JS runtime: system init complete');
+	onload_complete = true;
 }
 
 // don't init the page if less than IE6
 if ( typeof(KILL_SWITCH) == 'boolean' && !KILL_SWITCH )
 {
-  window.onload = enano_init;
+	window.onload = enano_init;
 }
 
--- a/includes/clientside/static/login.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/login.js	Sun Mar 28 23:10:46 2010 -0400
@@ -12,22 +12,22 @@
 
 window.ajaxLogonToMember = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  if ( auth_level >= USER_LEVEL_MEMBER )
-    return true;
-  ajaxLoginInit(function(k)
-    {
-      if ( on_main_page && main_page_members != physical_title )
-      {
-        window.location = makeUrl(main_page_members);
-      }
-      else
-      {
-        window.location.reload();
-      }
-    }, USER_LEVEL_MEMBER);
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	if ( auth_level >= USER_LEVEL_MEMBER )
+		return true;
+	ajaxLoginInit(function(k)
+		{
+			if ( on_main_page && main_page_members != physical_title )
+			{
+				window.location = makeUrl(main_page_members);
+			}
+			else
+			{
+				window.location.reload();
+			}
+		}, USER_LEVEL_MEMBER);
 }
 
 /**
@@ -36,16 +36,16 @@
 
 window.ajaxLogonToElev = function()
 {
-  if ( auth_level == user_level )
-    return true;
-  
-  ajaxLoginInit(function(k)
-    {
-      ENANO_SID = k;
-      var url = String(' ' + window.location).substr(1);
-      url = append_sid(url);
-      window.location = url;
-    }, user_level);
+	if ( auth_level == user_level )
+		return true;
+	
+	ajaxLoginInit(function(k)
+		{
+			ENANO_SID = k;
+			var url = String(' ' + window.location).substr(1);
+			url = append_sid(url);
+			window.location = url;
+		}, user_level);
 }
 
 /*
@@ -65,13 +65,13 @@
  */
 
 if ( !ajax_login_loadimg_path )
-  var ajax_login_loadimg_path = false;
+	var ajax_login_loadimg_path = false;
 
 if ( !ajax_login_successimg_path )
-  var ajax_login_successimg_path = false;
+	var ajax_login_successimg_path = false;
 
 if ( !ajax_login_lockimg_path )
-  var ajax_login_lockimg_path = false;
+	var ajax_login_lockimg_path = false;
 
 /**
  * Status variables
@@ -108,58 +108,58 @@
 
 window.ajaxLoginInit = function(call_on_finish, user_level)
 {
-  load_component(['messagebox', 'flyin', 'fadefilter', 'jquery', 'jquery-ui', 'l10n', 'crypto']);
-  
-  logindata = {};
-  
-  var title = ( user_level > USER_LEVEL_MEMBER ) ? $lang.get('user_login_ajax_prompt_title_elev') : $lang.get('user_login_ajax_prompt_title');
-  logindata.mb_object = new MessageBox(MB_OKCANCEL | MB_ICONLOCK, title, '');
-  
-  //
-  // Cancel function: called when the "Cancel" button is clicked
-  //
-  logindata.mb_object.onclick['Cancel'] = function()
-  {
-    // Hide the error message, if any
-    $('#ajax_login_error_box').remove();
-    // Hide the captcha, if any
-    if ( document.getElementById('autoCaptcha') )
-    {
-      var to = fly_out_top(document.getElementById('autoCaptcha'), false, true);
-      setTimeout(function() {
-          var d = document.getElementById('autoCaptcha');
-          d.parentNode.removeChild(d);
-        }, to);
-    }
-    // Ask the server to delete the encryption key we're using
-    ajaxLoginPerformRequest({
-        mode: 'clean_key',
-        key_aes: logindata.key_aes,
-        key_dh: logindata.key_dh
-    });
-  };
-  
-  // Clicking OK will not cause the box to destroy, as this function returns true.
-  logindata.mb_object.onbeforeclick['OK'] = function()
-  {
-    // Just call the submitter and let it take care of everything
-    ajaxLoginSubmitForm();
-    return true;
-  }
-  
-  // Fetch the inner content area
-  logindata.mb_inner = document.getElementById('messageBox').getElementsByTagName('div')[0];
-  
-  // Initialize state
-  logindata.showing_status = false;
-  logindata.user_level = user_level;
-  logindata.successfunc = call_on_finish;
-  
-  // Build the "loading" window
-  ajaxLoginSetStatus(AJAX_STATUS_LOADING_KEY);
-  
-  // Request the key
-  ajaxLoginPerformRequest({ mode: 'getkey' });
+	load_component(['messagebox', 'flyin', 'fadefilter', 'jquery', 'jquery-ui', 'l10n', 'crypto']);
+	
+	logindata = {};
+	
+	var title = ( user_level > USER_LEVEL_MEMBER ) ? $lang.get('user_login_ajax_prompt_title_elev') : $lang.get('user_login_ajax_prompt_title');
+	logindata.mb_object = new MessageBox(MB_OKCANCEL | MB_ICONLOCK, title, '');
+	
+	//
+	// Cancel function: called when the "Cancel" button is clicked
+	//
+	logindata.mb_object.onclick['Cancel'] = function()
+	{
+		// Hide the error message, if any
+		$('#ajax_login_error_box').remove();
+		// Hide the captcha, if any
+		if ( document.getElementById('autoCaptcha') )
+		{
+			var to = fly_out_top(document.getElementById('autoCaptcha'), false, true);
+			setTimeout(function() {
+					var d = document.getElementById('autoCaptcha');
+					d.parentNode.removeChild(d);
+				}, to);
+		}
+		// Ask the server to delete the encryption key we're using
+		ajaxLoginPerformRequest({
+				mode: 'clean_key',
+				key_aes: logindata.key_aes,
+				key_dh: logindata.key_dh
+		});
+	};
+	
+	// Clicking OK will not cause the box to destroy, as this function returns true.
+	logindata.mb_object.onbeforeclick['OK'] = function()
+	{
+		// Just call the submitter and let it take care of everything
+		ajaxLoginSubmitForm();
+		return true;
+	}
+	
+	// Fetch the inner content area
+	logindata.mb_inner = document.getElementById('messageBox').getElementsByTagName('div')[0];
+	
+	// Initialize state
+	logindata.showing_status = false;
+	logindata.user_level = user_level;
+	logindata.successfunc = call_on_finish;
+	
+	// Build the "loading" window
+	ajaxLoginSetStatus(AJAX_STATUS_LOADING_KEY);
+	
+	// Request the key
+	ajaxLoginPerformRequest({ mode: 'getkey' });
 }
 
 /**
@@ -169,7 +169,7 @@
 
 window.ajaxLogonInit = function(call_on_finish, user_level)
 {
-  return ajaxLoginInit(call_on_finish, user_level);
+	return ajaxLoginInit(call_on_finish, user_level);
 }
 
 /**
@@ -179,200 +179,200 @@
 
 window.ajaxLoginSetStatus = function(status)
 {
-  if ( !logindata.mb_inner )
-    return false;
-  if ( logindata.showing_status )
-  {
-    var div = document.getElementById('ajax_login_status');
-    if ( div )
-      logindata.mb_inner.removeChild(div);
-  }
-  switch(status)
-  {
-    case AJAX_STATUS_LOADING_KEY:
-      
-      // Create the status div
-      var div = document.createElement('div');
-      div.id = 'ajax_login_status';
-      div.style.marginTop = '10px';
-      div.style.textAlign = 'center';
-      
-      // The circly ball ajaxy image + status message
-      var status_msg = $lang.get('user_login_ajax_fetching_key');
-      
-      // Insert the status message
-      div.appendChild(document.createTextNode(status_msg));
-      
-      // Append a br or two to space things properly
-      div.appendChild(document.createElement('br'));
-      div.appendChild(document.createElement('br'));
-      
-      var img = document.createElement('img');
-      img.src = ( ajax_login_loadimg_path ) ? ajax_login_loadimg_path : scriptPath + '/images/loading-big.gif';
-      div.appendChild(img);
-      
-      // Another coupla brs
-      div.appendChild(document.createElement('br'));
-      div.appendChild(document.createElement('br'));
-      
-      // The link to the full login form
-      var small = document.createElement('small');
-      small.innerHTML = $lang.get('user_login_ajax_link_fullform', { link_full_form: makeUrlNS('Special', 'Login/' + title) });
-      div.appendChild(small);
-      
-      // Insert the entire message into the login window
-      logindata.mb_inner.innerHTML = '';
-      logindata.mb_inner.appendChild(div);
-      
-      break;
-    case AJAX_STATUS_GENERATING_KEY:
-      
-      // Create the status div
-      var div = document.createElement('div');
-      div.id = 'ajax_login_status';
-      div.style.marginTop = '10px';
-      div.style.textAlign = 'center';
-      
-      // The circly ball ajaxy image + status message
-      var status_msg = $lang.get('user_login_ajax_generating_key');
-      
-      // Insert the status message
-      div.appendChild(document.createTextNode(status_msg));
-      
-      // Append a br or two to space things properly
-      div.appendChild(document.createElement('br'));
-      div.appendChild(document.createElement('br'));
-      
-      var img = document.createElement('img');
-      img.src = ( ajax_login_lockimg_path ) ? ajax_login_lockimg_path : scriptPath + '/images/lock48.png';
-      div.appendChild(img);
-      
-      // Another coupla brs
-      div.appendChild(document.createElement('br'));
-      div.appendChild(document.createElement('br'));
-      
-      // The link to the full login form
-      var small = document.createElement('small');
-      small.innerHTML = $lang.get('user_login_ajax_link_fullform_dh', { link_full_form: makeUrlNS('Special', 'Login/' + title) });
-      div.appendChild(small);
-      
-      // Insert the entire message into the login window
-      logindata.mb_inner.innerHTML = '';
-      logindata.mb_inner.appendChild(div);
-      
-      break;
-    case AJAX_STATUS_LOGGING_IN:
-      
-      // Create the status div
-      var div = document.createElement('div');
-      div.id = 'ajax_login_status';
-      div.style.marginTop = '10px';
-      div.style.textAlign = 'center';
-      
-      // The circly ball ajaxy image + status message
-      var status_msg = $lang.get('user_login_ajax_loggingin');
-      
-      // Insert the status message
-      div.appendChild(document.createTextNode(status_msg));
-      
-      // Append a br or two to space things properly
-      div.appendChild(document.createElement('br'));
-      div.appendChild(document.createElement('br'));
-      
-      var img = document.createElement('img');
-      img.src = ( ajax_login_loadimg_path ) ? ajax_login_loadimg_path : scriptPath + '/images/loading-big.gif';
-      div.appendChild(img);
-      
-      // Insert the entire message into the login window
-      logindata.mb_inner.innerHTML = '';
-      logindata.mb_inner.appendChild(div);
-      
-      break;
-    case AJAX_STATUS_SUCCESS:
-      
-      // Create the status div
-      var div = document.createElement('div');
-      div.id = 'ajax_login_status';
-      div.style.marginTop = '10px';
-      div.style.textAlign = 'center';
-      
-      // The circly ball ajaxy image + status message
-      var status_msg = $lang.get('user_login_success_short');
-      
-      // Insert the status message
-      div.appendChild(document.createTextNode(status_msg));
-      
-      // Append a br or two to space things properly
-      div.appendChild(document.createElement('br'));
-      div.appendChild(document.createElement('br'));
-      
-      var img = document.createElement('img');
-      img.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/check.png';
-      div.appendChild(img);
-      
-      // Insert the entire message into the login window
-      logindata.mb_inner.innerHTML = '';
-      logindata.mb_inner.appendChild(div);
-      
-      break;
-      
-    case AJAX_STATUS_ERROR:
-      // Create the status div
-      var div = document.createElement('div');
-      div.id = 'ajax_login_status';
-      div.style.marginTop = '10px';
-      div.style.textAlign = 'center';
-      
-      // The circly ball ajaxy image + status message
-      var status_msg = $lang.get('user_login_ajax_err_crypto');
-      
-      // Insert the status message
-      div.appendChild(document.createTextNode(status_msg));
-      
-      // Append a br or two to space things properly
-      div.appendChild(document.createElement('br'));
-      div.appendChild(document.createElement('br'));
-      
-      var img = document.createElement('img');
-      img.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/checkbad.png';
-      div.appendChild(img);
-      
-      // Append a br or two to space things properly
-      div.appendChild(document.createElement('br'));
-      div.appendChild(document.createElement('br'));
-      
-      // The circly ball ajaxy image + status message
-      var detail_msg = $lang.get('user_login_ajax_err_crypto_details');
-      var full_link = $lang.get('user_login_ajax_err_crypto_link');
-      var link = document.createElement('a');
-      link.href = makeUrlNS('Special', 'Login/' + title, 'level=' + logindata.user_level, true);
-      link.appendChild(document.createTextNode(full_link));
-      var span = document.createElement('span');
-      span.style.fontSize = 'smaller';
-      
-      // Insert the message
-      span.appendChild(document.createTextNode(detail_msg + ' '));
-      span.appendChild(link);
-      div.appendChild(span);
-      
-      // Insert the entire message into the login window
-      logindata.mb_inner.innerHTML = '';
-      logindata.mb_inner.appendChild(div);
-      
-      break;
-      
-    default:
-      eval(setHook('login_set_status'));
-      break;
-      
-    case AJAX_STATUS_DESTROY:
-    case null:
-    case undefined:
-      logindata.showing_status = false;
-      return;
-      break;
-  }
-  logindata.showing_status = true;
+	if ( !logindata.mb_inner )
+		return false;
+	if ( logindata.showing_status )
+	{
+		var div = document.getElementById('ajax_login_status');
+		if ( div )
+			logindata.mb_inner.removeChild(div);
+	}
+	switch(status)
+	{
+		case AJAX_STATUS_LOADING_KEY:
+			
+			// Create the status div
+			var div = document.createElement('div');
+			div.id = 'ajax_login_status';
+			div.style.marginTop = '10px';
+			div.style.textAlign = 'center';
+			
+			// The circly ball ajaxy image + status message
+			var status_msg = $lang.get('user_login_ajax_fetching_key');
+			
+			// Insert the status message
+			div.appendChild(document.createTextNode(status_msg));
+			
+			// Append a br or two to space things properly
+			div.appendChild(document.createElement('br'));
+			div.appendChild(document.createElement('br'));
+			
+			var img = document.createElement('img');
+			img.src = ( ajax_login_loadimg_path ) ? ajax_login_loadimg_path : scriptPath + '/images/loading-big.gif';
+			div.appendChild(img);
+			
+			// Another coupla brs
+			div.appendChild(document.createElement('br'));
+			div.appendChild(document.createElement('br'));
+			
+			// The link to the full login form
+			var small = document.createElement('small');
+			small.innerHTML = $lang.get('user_login_ajax_link_fullform', { link_full_form: makeUrlNS('Special', 'Login/' + title) });
+			div.appendChild(small);
+			
+			// Insert the entire message into the login window
+			logindata.mb_inner.innerHTML = '';
+			logindata.mb_inner.appendChild(div);
+			
+			break;
+		case AJAX_STATUS_GENERATING_KEY:
+			
+			// Create the status div
+			var div = document.createElement('div');
+			div.id = 'ajax_login_status';
+			div.style.marginTop = '10px';
+			div.style.textAlign = 'center';
+			
+			// The circly ball ajaxy image + status message
+			var status_msg = $lang.get('user_login_ajax_generating_key');
+			
+			// Insert the status message
+			div.appendChild(document.createTextNode(status_msg));
+			
+			// Append a br or two to space things properly
+			div.appendChild(document.createElement('br'));
+			div.appendChild(document.createElement('br'));
+			
+			var img = document.createElement('img');
+			img.src = ( ajax_login_lockimg_path ) ? ajax_login_lockimg_path : scriptPath + '/images/lock48.png';
+			div.appendChild(img);
+			
+			// Another coupla brs
+			div.appendChild(document.createElement('br'));
+			div.appendChild(document.createElement('br'));
+			
+			// The link to the full login form
+			var small = document.createElement('small');
+			small.innerHTML = $lang.get('user_login_ajax_link_fullform_dh', { link_full_form: makeUrlNS('Special', 'Login/' + title) });
+			div.appendChild(small);
+			
+			// Insert the entire message into the login window
+			logindata.mb_inner.innerHTML = '';
+			logindata.mb_inner.appendChild(div);
+			
+			break;
+		case AJAX_STATUS_LOGGING_IN:
+			
+			// Create the status div
+			var div = document.createElement('div');
+			div.id = 'ajax_login_status';
+			div.style.marginTop = '10px';
+			div.style.textAlign = 'center';
+			
+			// The circly ball ajaxy image + status message
+			var status_msg = $lang.get('user_login_ajax_loggingin');
+			
+			// Insert the status message
+			div.appendChild(document.createTextNode(status_msg));
+			
+			// Append a br or two to space things properly
+			div.appendChild(document.createElement('br'));
+			div.appendChild(document.createElement('br'));
+			
+			var img = document.createElement('img');
+			img.src = ( ajax_login_loadimg_path ) ? ajax_login_loadimg_path : scriptPath + '/images/loading-big.gif';
+			div.appendChild(img);
+			
+			// Insert the entire message into the login window
+			logindata.mb_inner.innerHTML = '';
+			logindata.mb_inner.appendChild(div);
+			
+			break;
+		case AJAX_STATUS_SUCCESS:
+			
+			// Create the status div
+			var div = document.createElement('div');
+			div.id = 'ajax_login_status';
+			div.style.marginTop = '10px';
+			div.style.textAlign = 'center';
+			
+			// The circly ball ajaxy image + status message
+			var status_msg = $lang.get('user_login_success_short');
+			
+			// Insert the status message
+			div.appendChild(document.createTextNode(status_msg));
+			
+			// Append a br or two to space things properly
+			div.appendChild(document.createElement('br'));
+			div.appendChild(document.createElement('br'));
+			
+			var img = document.createElement('img');
+			img.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/check.png';
+			div.appendChild(img);
+			
+			// Insert the entire message into the login window
+			logindata.mb_inner.innerHTML = '';
+			logindata.mb_inner.appendChild(div);
+			
+			break;
+			
+		case AJAX_STATUS_ERROR:
+			// Create the status div
+			var div = document.createElement('div');
+			div.id = 'ajax_login_status';
+			div.style.marginTop = '10px';
+			div.style.textAlign = 'center';
+			
+			// The circly ball ajaxy image + status message
+			var status_msg = $lang.get('user_login_ajax_err_crypto');
+			
+			// Insert the status message
+			div.appendChild(document.createTextNode(status_msg));
+			
+			// Append a br or two to space things properly
+			div.appendChild(document.createElement('br'));
+			div.appendChild(document.createElement('br'));
+			
+			var img = document.createElement('img');
+			img.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/checkbad.png';
+			div.appendChild(img);
+			
+			// Append a br or two to space things properly
+			div.appendChild(document.createElement('br'));
+			div.appendChild(document.createElement('br'));
+			
+			// The circly ball ajaxy image + status message
+			var detail_msg = $lang.get('user_login_ajax_err_crypto_details');
+			var full_link = $lang.get('user_login_ajax_err_crypto_link');
+			var link = document.createElement('a');
+			link.href = makeUrlNS('Special', 'Login/' + title, 'level=' + logindata.user_level, true);
+			link.appendChild(document.createTextNode(full_link));
+			var span = document.createElement('span');
+			span.style.fontSize = 'smaller';
+			
+			// Insert the message
+			span.appendChild(document.createTextNode(detail_msg + ' '));
+			span.appendChild(link);
+			div.appendChild(span);
+			
+			// Insert the entire message into the login window
+			logindata.mb_inner.innerHTML = '';
+			logindata.mb_inner.appendChild(div);
+			
+			break;
+			
+		default:
+			eval(setHook('login_set_status'));
+			break;
+			
+		case AJAX_STATUS_DESTROY:
+		case null:
+		case undefined:
+			logindata.showing_status = false;
+			return;
+			break;
+	}
+	logindata.showing_status = true;
 }
 
 /**
@@ -383,24 +383,24 @@
 
 window.ajaxLoginPerformRequest = function(json, _hookfunc)
 {
-  json = toJSONString(json);
-  json = ajaxEscape(json);
-  var hookfunc = typeof(_hookfunc) == 'function' ? _hookfunc : false;
-  ajaxPost(makeUrlNS('Special', 'Login/action.json'), 'r=' + json, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        // parse response
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(response);
-          return false;
-        }
-        response = parseJSON(response);
-        ajaxLoginProcessResponse(response, hookfunc);
-      }
-    }, true);
+	json = toJSONString(json);
+	json = ajaxEscape(json);
+	var hookfunc = typeof(_hookfunc) == 'function' ? _hookfunc : false;
+	ajaxPost(makeUrlNS('Special', 'Login/action.json'), 'r=' + json, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				// parse response
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(response);
+					return false;
+				}
+				response = parseJSON(response);
+				ajaxLoginProcessResponse(response, hookfunc);
+			}
+		}, true);
 }
 
 /**
@@ -410,78 +410,78 @@
 
 window.ajaxLoginProcessResponse = function(response, hookfunc)
 {
-  // Did the server send a plaintext error?
-  if ( response.mode == 'error' )
-  {
-    if ( logindata.mb_object )
-    {
-      logindata.mb_object.destroy();
-      var error_msg = $lang.get('user_' + ( response.error.toLowerCase() ));
-      new MessageBox(MB_ICONSTOP | MB_OK, $lang.get('user_err_login_generic_title'), error_msg);
-    }
-    else
-    {
-      alert(response.error);
-    }
-    return false;
-  }
-  
-  // Main mode switch
-  switch ( response.mode )
-  {
-    case 'initial':
-      // Rid ourselves of any loading windows
-      ajaxLoginSetStatus(AJAX_STATUS_DESTROY);
-      // show any errors
-      ajaxLoginShowFriendlyError(response);
-      // The server wants us to build the login form, all the information is there
-      ajaxLoginBuildForm(response);
-      break;
-    case 'login_success':
-      ajaxLoginSetStatus(AJAX_STATUS_SUCCESS);
-      logindata.successfunc(response.key, response);
-      break;
-    case 'reset_pass_used':
-      // We logged in with a temporary password. Prompt the user to go to the temp password page and
-      // reset their real password. If they click no, treat it as a login failure, as no session key
-      // is actually issued when this type of login is performed.
-      
-      var conf = confirm($lang.get('user_login_ajax_msg_used_temp_pass'));
-      if ( conf )
-      {
-        var url = makeUrlNS('Special', 'PasswordReset/stage2/' + response.user_id + '/' + response.temp_password);
-        window.location = url;
-        break;
-      }
-      // else, treat as a failure
-    default:
-      // Rid ourselves of any loading windows
-      ajaxLoginSetStatus(AJAX_STATUS_DESTROY);
-      document.getElementById('messageBox').style.backgroundColor = '#C0C0C0';
-      var mb_parent = document.getElementById('messageBox').parentNode;
-      $(mb_parent).effect("shake", {}, 200);
-      setTimeout(function()
-        {
-          document.getElementById('messageBox').style.backgroundColor = '#FFF';
-          console.debug(response);
-          ajaxLoginShowFriendlyError(response);
-          ajaxLoginBuildForm(response);
-        }, 2500);
-      
-      break;
-    case 'logout_success':
-      if ( ENANO_SID )
-      {
-        ajaxLoginReplaceSIDInline(false, ENANO_SID, USER_LEVEL_MEMBER);
-      }
-      break;
-    case 'noop':
-      break;
-  }
-  if ( hookfunc )
-  {
-    hookfunc(response);
-  }
+	// Did the server send a plaintext error?
+	if ( response.mode == 'error' )
+	{
+		if ( logindata.mb_object )
+		{
+			logindata.mb_object.destroy();
+			var error_msg = $lang.get('user_' + ( response.error.toLowerCase() ));
+			new MessageBox(MB_ICONSTOP | MB_OK, $lang.get('user_err_login_generic_title'), error_msg);
+		}
+		else
+		{
+			alert(response.error);
+		}
+		return false;
+	}
+	
+	// Main mode switch
+	switch ( response.mode )
+	{
+		case 'initial':
+			// Rid ourselves of any loading windows
+			ajaxLoginSetStatus(AJAX_STATUS_DESTROY);
+			// show any errors
+			ajaxLoginShowFriendlyError(response);
+			// The server wants us to build the login form, all the information is there
+			ajaxLoginBuildForm(response);
+			break;
+		case 'login_success':
+			ajaxLoginSetStatus(AJAX_STATUS_SUCCESS);
+			logindata.successfunc(response.key, response);
+			break;
+		case 'reset_pass_used':
+			// We logged in with a temporary password. Prompt the user to go to the temp password page and
+			// reset their real password. If they click no, treat it as a login failure, as no session key
+			// is actually issued when this type of login is performed.
+			
+			var conf = confirm($lang.get('user_login_ajax_msg_used_temp_pass'));
+			if ( conf )
+			{
+				var url = makeUrlNS('Special', 'PasswordReset/stage2/' + response.user_id + '/' + response.temp_password);
+				window.location = url;
+				break;
+			}
+			// else, treat as a failure
+		default:
+			// Rid ourselves of any loading windows
+			ajaxLoginSetStatus(AJAX_STATUS_DESTROY);
+			document.getElementById('messageBox').style.backgroundColor = '#C0C0C0';
+			var mb_parent = document.getElementById('messageBox').parentNode;
+			$(mb_parent).effect("shake", {}, 200);
+			setTimeout(function()
+				{
+					document.getElementById('messageBox').style.backgroundColor = '#FFF';
+					console.debug(response);
+					ajaxLoginShowFriendlyError(response);
+					ajaxLoginBuildForm(response);
+				}, 2500);
+			
+			break;
+		case 'logout_success':
+			if ( ENANO_SID )
+			{
+				ajaxLoginReplaceSIDInline(false, ENANO_SID, USER_LEVEL_MEMBER);
+			}
+			break;
+		case 'noop':
+			break;
+	}
+	if ( hookfunc )
+	{
+		hookfunc(response);
+	}
 }
 
 /*
@@ -495,837 +495,837 @@
 
 window.ajaxLoginBuildForm = function(data)
 {
-  // let's hope this effectively preloads the image...
-  var _1 = document.createElement('img');
-  _1.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/check.png';
-  var _2 = document.createElement('img');
-  _2.src = ( ajax_login_lockimg_path ) ? ajax_login_lockimg_path : scriptPath + '/images/lock48.png';
-  
-  var div = document.createElement('div');
-  div.id = 'ajax_login_form';
-  
-  var show_captcha = ( data.lockout.active && data.lockout.policy == 'captcha' ) ? data.lockout.captcha : false;
-  
-  // text displayed on re-auth
-  if ( logindata.user_level > USER_LEVEL_MEMBER )
-  {
-    div.innerHTML += $lang.get('user_login_ajax_prompt_body_elev') + '<br /><br />';
-  }
-  
-  // Create the form
-  var form = document.createElement('form');
-  form.action = 'javascript:void(ajaxLoginSubmitForm());';
-  form.onsubmit = function()
-  {
-    ajaxLoginSubmitForm();
-    return false;
-  }
-  if ( IE )
-  {
-    form.style.marginTop = '-20px';
-  }
-  
-  // Using tables to wrap form elements because it results in a
-  // more visually appealing form. Yes, tables suck. I don't really
-  // care - they make forms look good.
-  
-  var table = document.createElement('table');
-  table.style.margin = '0 auto';
-  
-  // Field - username
-  var tr1 = document.createElement('tr');
-  var td1_1 = document.createElement('td');
-  td1_1.appendChild(document.createTextNode($lang.get('user_login_field_username') + ':'));
-  tr1.appendChild(td1_1);
-  var td1_2 = document.createElement('td');
-  var f_username = document.createElement('input');
-  f_username.id = 'ajax_login_field_username';
-  f_username.name = 'ajax_login_field_username';
-  f_username.type = 'text';
-  f_username.size = '25';
-  if ( data.username )
-    f_username.value = data.username;
-  td1_2.appendChild(f_username);
-  tr1.appendChild(td1_2);
-  table.appendChild(tr1);
-  
-  // Field - password
-  var tr2 = document.createElement('tr');
-  var td2_1 = document.createElement('td');
-  td2_1.appendChild(document.createTextNode($lang.get('user_login_field_password') + ':'));
-  tr2.appendChild(td2_1);
-  var td2_2 = document.createElement('td');
-  var f_password = document.createElement('input');
-  f_password.id = 'ajax_login_field_password';
-  f_password.name = 'ajax_login_field_username';
-  f_password.type = 'password';
-  f_password.size = '25';
-  if ( !show_captcha )
-  {
-    f_password.onkeyup = function(e)
-    {
-      if ( !e )
-        e = window.event;
-      if ( !e && IE )
-        return true;
-      if ( e.keyCode == 13 )
-      {
-        ajaxLoginSubmitForm();
-      }
-    }
-  }
-  td2_2.appendChild(f_password);
-  tr2.appendChild(td2_2);
-  table.appendChild(tr2);
-  
-  // Field - captcha
-  if ( show_captcha )
-  {
-    var tr3 = document.createElement('tr');
-    var td3_1 = document.createElement('td');
-    td3_1.appendChild(document.createTextNode($lang.get('user_login_field_captcha') + ':'));
-    tr3.appendChild(td3_1);
-    var td3_2 = document.createElement('td');
-    var f_captcha = document.createElement('input');
-    f_captcha.id = 'ajax_login_field_captcha';
-    f_captcha.name = 'ajax_login_field_username';
-    f_captcha.type = 'text';
-    f_captcha.size = '25';
-    f_captcha.onkeyup = function(e)
-    {
-      if ( !e )
-        e = window.event;
-      if ( !e.keyCode )
-        return true;
-      if ( e.keyCode == 13 )
-      {
-        ajaxLoginSubmitForm();
-      }
-    }
-    td3_2.appendChild(f_captcha);
-    tr3.appendChild(td3_2);
-    table.appendChild(tr3);
-  }
-  
-  // ok, this is a compatibility hack
-  data.locked_out = { locked_out: data.lockout.active };
-  
-  // hook for the login form
-  eval(setHook('login_build_form'));
-  
-  delete(data.locked_out);
-  
-  // Done building the main part of the form
-  form.appendChild(table);
-  
-  // Checkbox container
-  var boxen = document.createElement('div');
-  boxen.style.textAlign = 'center';
-  boxen.style.padding = '7px 0';
-  
-  // Field: remember login
-  if ( logindata.user_level <= USER_LEVEL_MEMBER )
-  {
-    var lbl_remember = document.createElement('label');
-    lbl_remember.style.fontSize = 'smaller';
-    lbl_remember.style.textAlign = 'center';
-    
-    // figure out what text to put in the "remember me" checkbox
-    // infinite session length?
-    if ( data.extended_time == 0 )
-    {
-      // yes, infinite
-      var txt_remember = $lang.get('user_login_ajax_check_remember_infinite');
-    }
-    else
-    {
-      if ( data.extended_time % 7 == 0 )
-      {
-        // number of days is a multiple of 7
-        // use weeks as our unit
-        var sess_time = data.extended_time / 7;
-        var unit = 'week';
-      }
-      else
-      {
-        // use days as our unit
-        var sess_time = data.extended_time;
-        var unit = 'day';
-      }
-      // more than one week or day?
-      if ( sess_time != 1 )
-        unit += 's';
-      
-      // assemble the string
-      var txt_remember = $lang.get('user_login_ajax_check_remember', {
-          session_length: sess_time,
-          length_units: $lang.get('etc_unit_' + unit)
-        });
-    }
-    var check_remember = document.createElement('input');
-    check_remember.type = 'checkbox';
-    // this onclick attribute changes the cookie whenever the checkbox or label is clicked
-    check_remember.setAttribute('onclick', 'var ck = ( this.checked ) ? "enable" : "disable"; createCookie("login_remember", ck, 3650);');
-    if ( readCookie('login_remember') != 'disable' )
-      check_remember.setAttribute('checked', 'checked');
-    check_remember.id = 'ajax_login_field_remember';
-    lbl_remember.appendChild(check_remember);
-    lbl_remember.innerHTML += ' ' + txt_remember;
-    
-    boxen.appendChild(lbl_remember);
-  }
-  
-  var bullet = document.createElement('span');
-  bullet.innerHTML = '&nbsp;';
-  bullet.style.fontSize = '12pt';
-  bullet.style.borderRight = '1px solid #aaa';
-  bullet.style.margin = '0 6px 0 4px';
-  
-  // Field: enable Diffie Hellman
-  if ( ajax_login_prevent_dh )
-  {
-    if ( logindata.user_level <= USER_LEVEL_MEMBER )
-      // only show this if both checkboxes are visible
-      boxen.appendChild(bullet);
-      
-    var lbl_dh = document.createElement('span');
-    lbl_dh.style.fontSize = 'smaller';
-    lbl_dh.style.textAlign = 'center';
-    lbl_dh.innerHTML = $lang.get('user_login_ajax_check_dh_ie');
-    boxen.appendChild(lbl_dh);
-  }
-  else if ( !data.crypto.dh_enable )
-  {
-    // create hidden control - server requested that DiffieHellman be disabled (usually means not supported)
-    var check_dh = document.createElement('input');
-    check_dh.type = 'hidden';
-    check_dh.id = 'ajax_login_field_dh';
-    boxen.appendChild(check_dh);
-  }
-  else
-  {
-    if ( logindata.user_level <= USER_LEVEL_MEMBER )
-      // only show this if both checkboxes are visible
-      boxen.appendChild(bullet);
-    
-    var lbl_dh = document.createElement('label');
-    lbl_dh.style.fontSize = 'smaller';
-    lbl_dh.style.textAlign = 'center';
-    var check_dh = document.createElement('input');
-    check_dh.type = 'checkbox';
-    // this onclick attribute changes the cookie whenever the checkbox or label is clicked
-    check_dh.setAttribute('onclick', 'var ck = ( this.checked ) ? "enable" : "disable"; createCookie("diffiehellman_login", ck, 3650);');
-    if ( readCookie('diffiehellman_login') != 'disable' )
-      check_dh.setAttribute('checked', 'checked');
-    check_dh.id = 'ajax_login_field_dh';
-    lbl_dh.appendChild(check_dh);
-    lbl_dh.innerHTML += ' ' + $lang.get('user_login_ajax_check_dh');
-    boxen.appendChild(lbl_dh);
-  }
-  
-  form.appendChild(boxen);
-  
-  if ( IE )
-  {
-    div.innerHTML += form.outerHTML;
-  }
-  else
-  {
-    div.appendChild(form);
-  }
-  
-  // Diagnostic / help links
-  // (only displayed in login, not in re-auth)
-  if ( logindata.user_level == USER_LEVEL_MEMBER )
-  {
-    var links = document.createElement('small');
-    links.style.display = 'block';
-    links.style.textAlign = 'center';
-    links.innerHTML = '';
-    if ( !show_captcha )
-      links.innerHTML += $lang.get('user_login_ajax_link_fullform', { link_full_form: makeUrlNS('Special', 'Login/' + title) }) + ' &bull; ';
-    // Always shown
-    links.innerHTML += $lang.get('user_login_ajax_link_forgotpass', { forgotpass_link: makeUrlNS('Special', 'PasswordReset') }) + ' &bull; ';
-    if ( !show_captcha )
-      links.innerHTML += $lang.get('user_login_ajax_createaccount_blurb', { reg_link: makeUrlNS('Special', 'Register') });
-    div.appendChild(links);
-  }
-  
-  // Insert the entire form into the login window
-  logindata.mb_inner.innerHTML = '';
-  logindata.mb_inner.appendChild(div);
-  
-  // Post operations: field focus
-  setTimeout(
-    function()
-    {
-      if ( logindata.loggedin_username )
-        document.getElementById('ajax_login_field_password').focus();
-      else
-        document.getElementById('ajax_login_field_username').focus();
-    }, 750);        
-  
-  // Post operations: show captcha window
-  if ( show_captcha )
-  {
-    ajaxShowCaptcha(show_captcha);
-  }
-  
-  // Post operations: stash encryption keys and All That Jazz(TM)
-  logindata.key_aes = data.crypto.aes_key;
-  logindata.key_dh = data.crypto.dh_public_key;
-  logindata.captcha_hash = show_captcha;
-  logindata.loggedin_username = data.username;
-  
-  // If policy is lockout, also disable controls
-  if ( data.lockout.policy == 'lockout' && data.lockout.active )
-  {
-    f_username.setAttribute('disabled', 'disabled');
-    f_password.setAttribute('disabled', 'disabled');
-  }
+	// let's hope this effectively preloads the image...
+	var _1 = document.createElement('img');
+	_1.src = ( ajax_login_successimg_path ) ? ajax_login_successimg_path : scriptPath + '/images/check.png';
+	var _2 = document.createElement('img');
+	_2.src = ( ajax_login_lockimg_path ) ? ajax_login_lockimg_path : scriptPath + '/images/lock48.png';
+	
+	var div = document.createElement('div');
+	div.id = 'ajax_login_form';
+	
+	var show_captcha = ( data.lockout.active && data.lockout.policy == 'captcha' ) ? data.lockout.captcha : false;
+	
+	// text displayed on re-auth
+	if ( logindata.user_level > USER_LEVEL_MEMBER )
+	{
+		div.innerHTML += $lang.get('user_login_ajax_prompt_body_elev') + '<br /><br />';
+	}
+	
+	// Create the form
+	var form = document.createElement('form');
+	form.action = 'javascript:void(ajaxLoginSubmitForm());';
+	form.onsubmit = function()
+	{
+		ajaxLoginSubmitForm();
+		return false;
+	}
+	if ( IE )
+	{
+		form.style.marginTop = '-20px';
+	}
+	
+	// Using tables to wrap form elements because it results in a
+	// more visually appealing form. Yes, tables suck. I don't really
+	// care - they make forms look good.
+	
+	var table = document.createElement('table');
+	table.style.margin = '0 auto';
+	
+	// Field - username
+	var tr1 = document.createElement('tr');
+	var td1_1 = document.createElement('td');
+	td1_1.appendChild(document.createTextNode($lang.get('user_login_field_username') + ':'));
+	tr1.appendChild(td1_1);
+	var td1_2 = document.createElement('td');
+	var f_username = document.createElement('input');
+	f_username.id = 'ajax_login_field_username';
+	f_username.name = 'ajax_login_field_username';
+	f_username.type = 'text';
+	f_username.size = '25';
+	if ( data.username )
+		f_username.value = data.username;
+	td1_2.appendChild(f_username);
+	tr1.appendChild(td1_2);
+	table.appendChild(tr1);
+	
+	// Field - password
+	var tr2 = document.createElement('tr');
+	var td2_1 = document.createElement('td');
+	td2_1.appendChild(document.createTextNode($lang.get('user_login_field_password') + ':'));
+	tr2.appendChild(td2_1);
+	var td2_2 = document.createElement('td');
+	var f_password = document.createElement('input');
+	f_password.id = 'ajax_login_field_password';
+	f_password.name = 'ajax_login_field_username';
+	f_password.type = 'password';
+	f_password.size = '25';
+	if ( !show_captcha )
+	{
+		f_password.onkeyup = function(e)
+		{
+			if ( !e )
+				e = window.event;
+			if ( !e && IE )
+				return true;
+			if ( e.keyCode == 13 )
+			{
+				ajaxLoginSubmitForm();
+			}
+		}
+	}
+	td2_2.appendChild(f_password);
+	tr2.appendChild(td2_2);
+	table.appendChild(tr2);
+	
+	// Field - captcha
+	if ( show_captcha )
+	{
+		var tr3 = document.createElement('tr');
+		var td3_1 = document.createElement('td');
+		td3_1.appendChild(document.createTextNode($lang.get('user_login_field_captcha') + ':'));
+		tr3.appendChild(td3_1);
+		var td3_2 = document.createElement('td');
+		var f_captcha = document.createElement('input');
+		f_captcha.id = 'ajax_login_field_captcha';
+		f_captcha.name = 'ajax_login_field_username';
+		f_captcha.type = 'text';
+		f_captcha.size = '25';
+		f_captcha.onkeyup = function(e)
+		{
+			if ( !e )
+				e = window.event;
+			if ( !e.keyCode )
+				return true;
+			if ( e.keyCode == 13 )
+			{
+				ajaxLoginSubmitForm();
+			}
+		}
+		td3_2.appendChild(f_captcha);
+		tr3.appendChild(td3_2);
+		table.appendChild(tr3);
+	}
+	
+	// ok, this is a compatibility hack
+	data.locked_out = { locked_out: data.lockout.active };
+	
+	// hook for the login form
+	eval(setHook('login_build_form'));
+	
+	delete(data.locked_out);
+	
+	// Done building the main part of the form
+	form.appendChild(table);
+	
+	// Checkbox container
+	var boxen = document.createElement('div');
+	boxen.style.textAlign = 'center';
+	boxen.style.padding = '7px 0';
+	
+	// Field: remember login
+	if ( logindata.user_level <= USER_LEVEL_MEMBER )
+	{
+		var lbl_remember = document.createElement('label');
+		lbl_remember.style.fontSize = 'smaller';
+		lbl_remember.style.textAlign = 'center';
+		
+		// figure out what text to put in the "remember me" checkbox
+		// infinite session length?
+		if ( data.extended_time == 0 )
+		{
+			// yes, infinite
+			var txt_remember = $lang.get('user_login_ajax_check_remember_infinite');
+		}
+		else
+		{
+			if ( data.extended_time % 7 == 0 )
+			{
+				// number of days is a multiple of 7
+				// use weeks as our unit
+				var sess_time = data.extended_time / 7;
+				var unit = 'week';
+			}
+			else
+			{
+				// use days as our unit
+				var sess_time = data.extended_time;
+				var unit = 'day';
+			}
+			// more than one week or day?
+			if ( sess_time != 1 )
+				unit += 's';
+			
+			// assemble the string
+			var txt_remember = $lang.get('user_login_ajax_check_remember', {
+					session_length: sess_time,
+					length_units: $lang.get('etc_unit_' + unit)
+				});
+		}
+		var check_remember = document.createElement('input');
+		check_remember.type = 'checkbox';
+		// this onclick attribute changes the cookie whenever the checkbox or label is clicked
+		check_remember.setAttribute('onclick', 'var ck = ( this.checked ) ? "enable" : "disable"; createCookie("login_remember", ck, 3650);');
+		if ( readCookie('login_remember') != 'disable' )
+			check_remember.setAttribute('checked', 'checked');
+		check_remember.id = 'ajax_login_field_remember';
+		lbl_remember.appendChild(check_remember);
+		lbl_remember.innerHTML += ' ' + txt_remember;
+		
+		boxen.appendChild(lbl_remember);
+	}
+	
+	var bullet = document.createElement('span');
+	bullet.innerHTML = '&nbsp;';
+	bullet.style.fontSize = '12pt';
+	bullet.style.borderRight = '1px solid #aaa';
+	bullet.style.margin = '0 6px 0 4px';
+	
+	// Field: enable Diffie Hellman
+	if ( ajax_login_prevent_dh )
+	{
+		if ( logindata.user_level <= USER_LEVEL_MEMBER )
+			// only show this if both checkboxes are visible
+			boxen.appendChild(bullet);
+			
+		var lbl_dh = document.createElement('span');
+		lbl_dh.style.fontSize = 'smaller';
+		lbl_dh.style.textAlign = 'center';
+		lbl_dh.innerHTML = $lang.get('user_login_ajax_check_dh_ie');
+		boxen.appendChild(lbl_dh);
+	}
+	else if ( !data.crypto.dh_enable )
+	{
+		// create hidden control - server requested that DiffieHellman be disabled (usually means not supported)
+		var check_dh = document.createElement('input');
+		check_dh.type = 'hidden';
+		check_dh.id = 'ajax_login_field_dh';
+		boxen.appendChild(check_dh);
+	}
+	else
+	{
+		if ( logindata.user_level <= USER_LEVEL_MEMBER )
+			// only show this if both checkboxes are visible
+			boxen.appendChild(bullet);
+		
+		var lbl_dh = document.createElement('label');
+		lbl_dh.style.fontSize = 'smaller';
+		lbl_dh.style.textAlign = 'center';
+		var check_dh = document.createElement('input');
+		check_dh.type = 'checkbox';
+		// this onclick attribute changes the cookie whenever the checkbox or label is clicked
+		check_dh.setAttribute('onclick', 'var ck = ( this.checked ) ? "enable" : "disable"; createCookie("diffiehellman_login", ck, 3650);');
+		if ( readCookie('diffiehellman_login') != 'disable' )
+			check_dh.setAttribute('checked', 'checked');
+		check_dh.id = 'ajax_login_field_dh';
+		lbl_dh.appendChild(check_dh);
+		lbl_dh.innerHTML += ' ' + $lang.get('user_login_ajax_check_dh');
+		boxen.appendChild(lbl_dh);
+	}
+	
+	form.appendChild(boxen);
+	
+	if ( IE )
+	{
+		div.innerHTML += form.outerHTML;
+	}
+	else
+	{
+		div.appendChild(form);
+	}
+	
+	// Diagnostic / help links
+	// (only displayed in login, not in re-auth)
+	if ( logindata.user_level == USER_LEVEL_MEMBER )
+	{
+		var links = document.createElement('small');
+		links.style.display = 'block';
+		links.style.textAlign = 'center';
+		links.innerHTML = '';
+		if ( !show_captcha )
+			links.innerHTML += $lang.get('user_login_ajax_link_fullform', { link_full_form: makeUrlNS('Special', 'Login/' + title) }) + ' &bull; ';
+		// Always shown
+		links.innerHTML += $lang.get('user_login_ajax_link_forgotpass', { forgotpass_link: makeUrlNS('Special', 'PasswordReset') }) + ' &bull; ';
+		if ( !show_captcha )
+			links.innerHTML += $lang.get('user_login_ajax_createaccount_blurb', { reg_link: makeUrlNS('Special', 'Register') });
+		div.appendChild(links);
+	}
+	
+	// Insert the entire form into the login window
+	logindata.mb_inner.innerHTML = '';
+	logindata.mb_inner.appendChild(div);
+	
+	// Post operations: field focus
+	setTimeout(
+		function()
+		{
+			if ( logindata.loggedin_username )
+				document.getElementById('ajax_login_field_password').focus();
+			else
+				document.getElementById('ajax_login_field_username').focus();
+		}, 750);        
+	
+	// Post operations: show captcha window
+	if ( show_captcha )
+	{
+		ajaxShowCaptcha(show_captcha);
+	}
+	
+	// Post operations: stash encryption keys and All That Jazz(TM)
+	logindata.key_aes = data.crypto.aes_key;
+	logindata.key_dh = data.crypto.dh_public_key;
+	logindata.captcha_hash = show_captcha;
+	logindata.loggedin_username = data.username;
+	
+	// If policy is lockout, also disable controls
+	if ( data.lockout.policy == 'lockout' && data.lockout.active )
+	{
+		f_username.setAttribute('disabled', 'disabled');
+		f_password.setAttribute('disabled', 'disabled');
+	}
 }
 
 window.ajaxLoginSubmitForm = function(real, username, password, captcha, remember)
 {
-  // Perform AES test to make sure it's all working
-  if ( !aes_self_test() )
-  {
-    alert('BUG: AES self-test failed');
-    login_cache.mb_object.destroy();
-    return false;
-  }
-  // Hide the error message and captcha
-  if ( document.getElementById('ajax_login_error_box') )
-  {
-    document.getElementById('ajax_login_error_box').parentNode.removeChild(document.getElementById('ajax_login_error_box'));
-  }
-  if ( document.getElementById('autoCaptcha') )
-  {
-    var to = fly_out_top(document.getElementById('autoCaptcha'), false, true);
-    setTimeout(function() {
-        var d = document.getElementById('autoCaptcha');
-        d.parentNode.removeChild(d);
-      }, to);
-  }
-  // "Remember session" switch
-  if ( typeof(remember) == 'boolean' )
-  {
-    var remember_session = remember;
-  }
-  else
-  {
-    if ( document.getElementById('ajax_login_field_remember') )
-    {
-      var remember_session = ( document.getElementById('ajax_login_field_remember').checked ) ? true : false;
-    }
-    else
-    {
-      var remember_session = false;
-    }
-  }
-  // Encryption: preprocessor
-  if ( real )
-  {
-    var do_dh = true;
-  }
-  else if ( document.getElementById('ajax_login_field_dh') )
-  {
-    var do_dh = document.getElementById('ajax_login_field_dh').checked;
-  }
-  else
-  {
-    if ( ajax_login_prevent_dh )
-    {
-      // IE/MobileSafari doesn't have this control, continue silently IF the rest
-      // of the login form is there
-      if ( !document.getElementById('ajax_login_field_username') )
-      {
-        return false;
-      }
-    }
-    else
-    {
-      // The user probably clicked ok when the form wasn't in there.
-      return false;
-    }
-  }
-  
-  if ( typeof(username) != 'string' )
-  {
-    var username = document.getElementById('ajax_login_field_username').value;
-  }
-  if ( typeof(password) != 'string' )
-  {
-    var password = document.getElementById('ajax_login_field_password').value;
-  }
-  if ( !captcha && document.getElementById('ajax_login_field_captcha') )
-  {
-    var captcha = document.getElementById('ajax_login_field_captcha').value;
-  }
-  
-  // Only run early submit hook once
-  if ( !window.logindata.early_submit_run )
-    eval(setHook('login_submit_early'));
-  
-  window.logindata.early_submit_run = true;
-  
-  try
-  {
-  
-  if ( do_dh )
-  {
-    ajaxLoginSetStatus(AJAX_STATUS_GENERATING_KEY);
-    if ( !real )
-    {
-      // Wait while the browser updates the login window
-      setTimeout(function()
-        {
-          ajaxLoginSubmitForm(true, username, password, captcha, remember_session);
-        }, 20);
-      return true;
-    }
-    var dh_start = (new Date()).getTime();
-    // Perform Diffie Hellman stuff
-    var dh_priv = dh_gen_private();
-    var dh_pub = dh_gen_public(dh_priv);
-    var secret = dh_gen_shared_secret(dh_priv, logindata.key_dh);
-    // secret_hash is used to verify that the server guesses the correct secret
-    var secret_hash = hex_sha1(secret);
-    // crypt_key is the actual AES key
-    var crypt_key = (hex_sha256(secret)).substr(0, (keySizeInBits / 4));
-    var dh_time = (new Date()).getTime() - dh_start;
-    console.debug("DH: complete, time = %dms", dh_time);
-  }
-  else
-  {
-    var crypt_key = logindata.key_aes;
-  }
-  
-  ajaxLoginSetStatus(AJAX_STATUS_LOGGING_IN);
-  
-  // Encrypt the password and username
-  var userinfo = {
-      username: username,
-      password: password
-    };
-    
-  eval(setHook('login_build_userinfo'));
-    
-  userinfo = toJSONString(userinfo);
-  var crypt_key_ba = hexToByteArray(crypt_key);
-  userinfo = stringToByteArray(userinfo);
-  
-  userinfo = rijndaelEncrypt(userinfo, crypt_key_ba, 'ECB');
-  userinfo = byteArrayToHex(userinfo);
-  // Encrypted username and password (serialized with JSON) are now in the userinfo string
-  
-  // Collect other needed information
-  if ( logindata.captcha_hash )
-  {
-    var captcha_hash = logindata.captcha_hash;
-    var captcha_code = captcha;
-  }
-  else
-  {
-    var captcha_hash = false;
-    var captcha_code = false;
-  }
-  
-  // Ship it across the 'net
-  if ( do_dh )
-  {
-    var json_packet = {
-      mode: 'login_dh',
-      userinfo: userinfo,
-      captcha_code: captcha_code,
-      captcha_hash: captcha_hash,
-      dh_public_key: logindata.key_dh,
-      dh_client_key: dh_pub,
-      dh_secret_hash: secret_hash,
-      level: logindata.user_level,
-      remember: remember_session
-    }
-  }
-  else
-  {
-    var json_packet = {
-      mode: 'login_aes',
-      userinfo: userinfo,
-      captcha_code: captcha_code,
-      captcha_hash: captcha_hash,
-      key_aes: hex_md5(crypt_key),
-      level: logindata.user_level,
-      remember: remember_session
-    }
-  }
-  }
-  catch(e)
-  {
-    ajaxLoginSetStatus(AJAX_STATUS_ERROR);
-    console.error('Exception caught in login process; backtrace follows');
-    console.debug(e);
-    return false;
-  }
-  // reset this...
-  window.logindata.early_submit_run = false;
-  ajaxLoginPerformRequest(json_packet);
+	// Perform AES test to make sure it's all working
+	if ( !aes_self_test() )
+	{
+		alert('BUG: AES self-test failed');
+		login_cache.mb_object.destroy();
+		return false;
+	}
+	// Hide the error message and captcha
+	if ( document.getElementById('ajax_login_error_box') )
+	{
+		document.getElementById('ajax_login_error_box').parentNode.removeChild(document.getElementById('ajax_login_error_box'));
+	}
+	if ( document.getElementById('autoCaptcha') )
+	{
+		var to = fly_out_top(document.getElementById('autoCaptcha'), false, true);
+		setTimeout(function() {
+				var d = document.getElementById('autoCaptcha');
+				d.parentNode.removeChild(d);
+			}, to);
+	}
+	// "Remember session" switch
+	if ( typeof(remember) == 'boolean' )
+	{
+		var remember_session = remember;
+	}
+	else
+	{
+		if ( document.getElementById('ajax_login_field_remember') )
+		{
+			var remember_session = ( document.getElementById('ajax_login_field_remember').checked ) ? true : false;
+		}
+		else
+		{
+			var remember_session = false;
+		}
+	}
+	// Encryption: preprocessor
+	if ( real )
+	{
+		var do_dh = true;
+	}
+	else if ( document.getElementById('ajax_login_field_dh') )
+	{
+		var do_dh = document.getElementById('ajax_login_field_dh').checked;
+	}
+	else
+	{
+		if ( ajax_login_prevent_dh )
+		{
+			// IE/MobileSafari doesn't have this control, continue silently IF the rest
+			// of the login form is there
+			if ( !document.getElementById('ajax_login_field_username') )
+			{
+				return false;
+			}
+		}
+		else
+		{
+			// The user probably clicked ok when the form wasn't in there.
+			return false;
+		}
+	}
+	
+	if ( typeof(username) != 'string' )
+	{
+		var username = document.getElementById('ajax_login_field_username').value;
+	}
+	if ( typeof(password) != 'string' )
+	{
+		var password = document.getElementById('ajax_login_field_password').value;
+	}
+	if ( !captcha && document.getElementById('ajax_login_field_captcha') )
+	{
+		var captcha = document.getElementById('ajax_login_field_captcha').value;
+	}
+	
+	// Only run early submit hook once
+	if ( !window.logindata.early_submit_run )
+		eval(setHook('login_submit_early'));
+	
+	window.logindata.early_submit_run = true;
+	
+	try
+	{
+	
+	if ( do_dh )
+	{
+		ajaxLoginSetStatus(AJAX_STATUS_GENERATING_KEY);
+		if ( !real )
+		{
+			// Wait while the browser updates the login window
+			setTimeout(function()
+				{
+					ajaxLoginSubmitForm(true, username, password, captcha, remember_session);
+				}, 20);
+			return true;
+		}
+		var dh_start = (new Date()).getTime();
+		// Perform Diffie Hellman stuff
+		var dh_priv = dh_gen_private();
+		var dh_pub = dh_gen_public(dh_priv);
+		var secret = dh_gen_shared_secret(dh_priv, logindata.key_dh);
+		// secret_hash is used to verify that the server guesses the correct secret
+		var secret_hash = hex_sha1(secret);
+		// crypt_key is the actual AES key
+		var crypt_key = (hex_sha256(secret)).substr(0, (keySizeInBits / 4));
+		var dh_time = (new Date()).getTime() - dh_start;
+		console.debug("DH: complete, time = %dms", dh_time);
+	}
+	else
+	{
+		var crypt_key = logindata.key_aes;
+	}
+	
+	ajaxLoginSetStatus(AJAX_STATUS_LOGGING_IN);
+	
+	// Encrypt the password and username
+	var userinfo = {
+			username: username,
+			password: password
+		};
+		
+	eval(setHook('login_build_userinfo'));
+		
+	userinfo = toJSONString(userinfo);
+	var crypt_key_ba = hexToByteArray(crypt_key);
+	userinfo = stringToByteArray(userinfo);
+	
+	userinfo = rijndaelEncrypt(userinfo, crypt_key_ba, 'ECB');
+	userinfo = byteArrayToHex(userinfo);
+	// Encrypted username and password (serialized with JSON) are now in the userinfo string
+	
+	// Collect other needed information
+	if ( logindata.captcha_hash )
+	{
+		var captcha_hash = logindata.captcha_hash;
+		var captcha_code = captcha;
+	}
+	else
+	{
+		var captcha_hash = false;
+		var captcha_code = false;
+	}
+	
+	// Ship it across the 'net
+	if ( do_dh )
+	{
+		var json_packet = {
+			mode: 'login_dh',
+			userinfo: userinfo,
+			captcha_code: captcha_code,
+			captcha_hash: captcha_hash,
+			dh_public_key: logindata.key_dh,
+			dh_client_key: dh_pub,
+			dh_secret_hash: secret_hash,
+			level: logindata.user_level,
+			remember: remember_session
+		}
+	}
+	else
+	{
+		var json_packet = {
+			mode: 'login_aes',
+			userinfo: userinfo,
+			captcha_code: captcha_code,
+			captcha_hash: captcha_hash,
+			key_aes: hex_md5(crypt_key),
+			level: logindata.user_level,
+			remember: remember_session
+		}
+	}
+	}
+	catch(e)
+	{
+		ajaxLoginSetStatus(AJAX_STATUS_ERROR);
+		console.error('Exception caught in login process; backtrace follows');
+		console.debug(e);
+		return false;
+	}
+	// reset this...
+	window.logindata.early_submit_run = false;
+	ajaxLoginPerformRequest(json_packet);
 }
 
 window.ajaxLoginShowFriendlyError = function(response)
 {
-  var text = ajaxLoginGetErrorText(response);
-  if ( text == false )
-    return true;
-    
-  if ( document.getElementById('ajax_login_error_box') )
-  {
-    // console.info('Reusing existing error-box');
-    document.getElementById('ajax_login_error_box').innerHTML = text;
-    return true;
-  }
-  
-  // console.info('Drawing new error-box');
-  
-  // calculate position for the top of the box
-  var mb_bottom = $dynano('messageBoxButtons').Top() + $dynano('messageBoxButtons').Height();
-  // if the box isn't done flying in yet, just estimate
-  if ( mb_bottom < ( getHeight() / 2 ) )
-  {
-    mb_bottom = ( getHeight() / 2 ) + 120;
-  }
-  var win_bottom = getHeight() + getScrollOffset();
-  var top = mb_bottom + ( ( win_bottom - mb_bottom ) / 2 ) - 32;
-  // left position = 0.2 * window_width, seeing as the box is 60% width this works hackishly but nice and quick
-  var left = getWidth() * 0.2;
-  
-  // create the div
-  var errbox = document.createElement('div');
-  errbox.className = 'error-box-mini';
-  errbox.style.position = 'absolute';
-  errbox.style.width = '60%';
-  errbox.style.top = top + 'px';
-  errbox.style.left = left + 'px';
-  errbox.style.zIndex = getHighestZ();
-  errbox.innerHTML = text;
-  errbox.id = 'ajax_login_error_box';
-  
-  var body = document.getElementsByTagName('body')[0];
-  body.appendChild(errbox);
+	var text = ajaxLoginGetErrorText(response);
+	if ( text == false )
+		return true;
+		
+	if ( document.getElementById('ajax_login_error_box') )
+	{
+		// console.info('Reusing existing error-box');
+		document.getElementById('ajax_login_error_box').innerHTML = text;
+		return true;
+	}
+	
+	// console.info('Drawing new error-box');
+	
+	// calculate position for the top of the box
+	var mb_bottom = $dynano('messageBoxButtons').Top() + $dynano('messageBoxButtons').Height();
+	// if the box isn't done flying in yet, just estimate
+	if ( mb_bottom < ( getHeight() / 2 ) )
+	{
+		mb_bottom = ( getHeight() / 2 ) + 120;
+	}
+	var win_bottom = getHeight() + getScrollOffset();
+	var top = mb_bottom + ( ( win_bottom - mb_bottom ) / 2 ) - 32;
+	// left position = 0.2 * window_width, seeing as the box is 60% width this works hackishly but nice and quick
+	var left = getWidth() * 0.2;
+	
+	// create the div
+	var errbox = document.createElement('div');
+	errbox.className = 'error-box-mini';
+	errbox.style.position = 'absolute';
+	errbox.style.width = '60%';
+	errbox.style.top = top + 'px';
+	errbox.style.left = left + 'px';
+	errbox.style.zIndex = getHighestZ();
+	errbox.innerHTML = text;
+	errbox.id = 'ajax_login_error_box';
+	
+	var body = document.getElementsByTagName('body')[0];
+	body.appendChild(errbox);
 }
 
 window.ajaxLoginGetErrorText = function(response)
 {
-  if ( response.lockout )
-  {
-    // set this pluralality thing
-    response.lockout.plural = response.lockout.time_rem == 1 ? '' : $lang.get('meta_plural');
-  }
-  
-  if ( response.mode == 'initial' )
-  {
-    // Just showing the box for the first time. If there's an error now, it's based on a preexisting lockout.
-    if ( response.lockout.active )
-    {
-      return $lang.get('user_err_locked_out_initial_' + response.lockout.policy, response.lockout);
-    }
-    return false;
-  }
-  else
-  {
-    // An attempt was made.
-    switch(response.mode)
-    {
-      case 'login_failure':
-        // Generic login user error.
-        var error = '', x;
-        if ( (x = $lang.get(response.error)) != response.error || ! (/^[a-z0-9_]+/).test(response.error) )
-          error = x;
-        else
-          error = $lang.get('user_err_' + response.error);
-        if ( response.lockout.active && response.lockout.policy == 'lockout' )
-        {
-          // Lockout enforcement was just activated.
-          return $lang.get('user_err_locked_out_initial_' + response.lockout.policy, response.lockout);
-        }
-        else if ( response.lockout.policy != 'disable' && !response.lockout.active && response.lockout.fails > 0 )
-        {
-          // Lockout is in a warning state.
-          error += ' ' + $lang.get('user_err_invalid_credentials_' + response.lockout.policy, response.lockout);
-        }
-        return error;
-        break;
-      case 'api_error':
-        // Error in the API.
-        return $lang.get('user_err_login_generic_title') + ': ' + $lang.get('user_' + response.error.toLowerCase());
-        break;
-    }
-  }
-  
-  return typeof(response.error) == 'string' ? response.error : false;
+	if ( response.lockout )
+	{
+		// set this pluralality thing
+		response.lockout.plural = response.lockout.time_rem == 1 ? '' : $lang.get('meta_plural');
+	}
+	
+	if ( response.mode == 'initial' )
+	{
+		// Just showing the box for the first time. If there's an error now, it's based on a preexisting lockout.
+		if ( response.lockout.active )
+		{
+			return $lang.get('user_err_locked_out_initial_' + response.lockout.policy, response.lockout);
+		}
+		return false;
+	}
+	else
+	{
+		// An attempt was made.
+		switch(response.mode)
+		{
+			case 'login_failure':
+				// Generic login user error.
+				var error = '', x;
+				if ( (x = $lang.get(response.error)) != response.error || ! (/^[a-z0-9_]+/).test(response.error) )
+					error = x;
+				else
+					error = $lang.get('user_err_' + response.error);
+				if ( response.lockout.active && response.lockout.policy == 'lockout' )
+				{
+					// Lockout enforcement was just activated.
+					return $lang.get('user_err_locked_out_initial_' + response.lockout.policy, response.lockout);
+				}
+				else if ( response.lockout.policy != 'disable' && !response.lockout.active && response.lockout.fails > 0 )
+				{
+					// Lockout is in a warning state.
+					error += ' ' + $lang.get('user_err_invalid_credentials_' + response.lockout.policy, response.lockout);
+				}
+				return error;
+				break;
+			case 'api_error':
+				// Error in the API.
+				return $lang.get('user_err_login_generic_title') + ': ' + $lang.get('user_' + response.error.toLowerCase());
+				break;
+		}
+	}
+	
+	return typeof(response.error) == 'string' ? response.error : false;
 }
 
 window.ajaxShowCaptcha = function(code)
 {
-  var mydiv = document.createElement('div');
-  mydiv.style.backgroundColor = '#FFFFFF';
-  mydiv.style.padding = '10px';
-  mydiv.style.position = 'absolute';
-  mydiv.style.top = '0px';
-  mydiv.id = 'autoCaptcha';
-  mydiv.style.zIndex = String( getHighestZ() + 1 );
-  var img = document.createElement('img');
-  img.onload = function()
-  {
-    if ( this.loaded )
-      return true;
-    var mydiv = document.getElementById('autoCaptcha');
-    var width = getWidth();
-    var divw = $dynano(mydiv).Width();
-    var left = ( width / 2 ) - ( divw / 2 );
-    mydiv.style.left = left + 'px';
-    fly_in_top(mydiv, false, true);
-    this.loaded = true;
-  };
-  img.src = makeUrlNS('Special', 'Captcha/' + code);
-  img.onclick = function() { this.src = this.src + '/a'; };
-  img.style.cursor = 'pointer';
-  mydiv.appendChild(img);
-  domObjChangeOpac(0, mydiv);
-  var body = document.getElementsByTagName('body')[0];
-  body.appendChild(mydiv);
+	var mydiv = document.createElement('div');
+	mydiv.style.backgroundColor = '#FFFFFF';
+	mydiv.style.padding = '10px';
+	mydiv.style.position = 'absolute';
+	mydiv.style.top = '0px';
+	mydiv.id = 'autoCaptcha';
+	mydiv.style.zIndex = String( getHighestZ() + 1 );
+	var img = document.createElement('img');
+	img.onload = function()
+	{
+		if ( this.loaded )
+			return true;
+		var mydiv = document.getElementById('autoCaptcha');
+		var width = getWidth();
+		var divw = $dynano(mydiv).Width();
+		var left = ( width / 2 ) - ( divw / 2 );
+		mydiv.style.left = left + 'px';
+		fly_in_top(mydiv, false, true);
+		this.loaded = true;
+	};
+	img.src = makeUrlNS('Special', 'Captcha/' + code);
+	img.onclick = function() { this.src = this.src + '/a'; };
+	img.style.cursor = 'pointer';
+	mydiv.appendChild(img);
+	domObjChangeOpac(0, mydiv);
+	var body = document.getElementsByTagName('body')[0];
+	body.appendChild(mydiv);
 }
 
 window.ajaxInitLogout = function()
 {
-  load_component(['messagebox', 'l10n', 'flyin', 'fadefilter', 'jquery', 'jquery-ui']);
-  
-  var title = $lang.get('user_logout_confirm_title');
-  var message = ( auth_level > USER_LEVEL_MEMBER ) ? $lang.get('user_logout_confirm_body_nelev') : $lang.get('user_logout_confirm_body_normal');
-  var buttons = [];
-  buttons.push({
-      text: $lang.get('user_logout_confirm_btn_logout'),
-      color: 'red',
-      style: {
-        fontWeight: 'bold'
-      },
-      onclick: function()
-      {
-        miniPromptDestroy(this);
-        window.location = makeUrlNS('Special', 'Logout/' + csrf_token + '/' + window.title);
-        return false;
-      }
-    });
-  if ( auth_level > USER_LEVEL_MEMBER )
-  {
-    buttons.push({
-        text: $lang.get('user_logout_confirm_btn_deauth'),
-        color: 'blue',
-        onclick: function()
-        {
-          var mp = miniPromptGetParent(this);
-          var whitey = whiteOutMiniPrompt(mp);
-          
-          ajaxLoginPerformRequest({
-              mode:  'logout',
-              level: auth_level,
-              csrf_token: csrf_token
-          }, function(response)
-            {
-              whiteOutReportSuccess(whitey);
-            });
-          return false;
-        }
-      });
-  }
-  buttons.push({
-      text: $lang.get('etc_cancel'),
-      onclick: function()
-      {
-        miniPromptDestroy(this);
-        return false;
-      }
-    });
-  
-  miniPromptMessage({
-      title: title,
-      message: message,
-      buttons: buttons
-  });
+	load_component(['messagebox', 'l10n', 'flyin', 'fadefilter', 'jquery', 'jquery-ui']);
+	
+	var title = $lang.get('user_logout_confirm_title');
+	var message = ( auth_level > USER_LEVEL_MEMBER ) ? $lang.get('user_logout_confirm_body_nelev') : $lang.get('user_logout_confirm_body_normal');
+	var buttons = [];
+	buttons.push({
+			text: $lang.get('user_logout_confirm_btn_logout'),
+			color: 'red',
+			style: {
+				fontWeight: 'bold'
+			},
+			onclick: function()
+			{
+				miniPromptDestroy(this);
+				window.location = makeUrlNS('Special', 'Logout/' + csrf_token + '/' + window.title);
+				return false;
+			}
+		});
+	if ( auth_level > USER_LEVEL_MEMBER )
+	{
+		buttons.push({
+				text: $lang.get('user_logout_confirm_btn_deauth'),
+				color: 'blue',
+				onclick: function()
+				{
+					var mp = miniPromptGetParent(this);
+					var whitey = whiteOutMiniPrompt(mp);
+					
+					ajaxLoginPerformRequest({
+							mode:  'logout',
+							level: auth_level,
+							csrf_token: csrf_token
+					}, function(response)
+						{
+							whiteOutReportSuccess(whitey);
+						});
+					return false;
+				}
+			});
+	}
+	buttons.push({
+			text: $lang.get('etc_cancel'),
+			onclick: function()
+			{
+				miniPromptDestroy(this);
+				return false;
+			}
+		});
+	
+	miniPromptMessage({
+			title: title,
+			message: message,
+			buttons: buttons
+	});
 }
 
 window.mb_logout = function()
 {
-  ajaxInitLogout();
+	ajaxInitLogout();
 }
 
 window.ajaxStartLogin = function()
 {
-  ajaxLogonToMember();
+	ajaxLogonToMember();
 }
 
 window.ajaxStartAdminLogin = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  if ( auth_level < USER_LEVEL_ADMIN )
-  {
-    ajaxLoginInit(function(k) {
-      ENANO_SID = k;
-      auth_level = USER_LEVEL_ADMIN;
-      var loc = makeUrlNS('Special', 'Administration');
-      if ( (ENANO_SID + ' ').length > 1 )
-        window.location = loc;
-    }, USER_LEVEL_ADMIN);
-    return false;
-  }
-  var loc = makeUrlNS('Special', 'Administration');
-  window.location = loc;
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	if ( auth_level < USER_LEVEL_ADMIN )
+	{
+		ajaxLoginInit(function(k) {
+			ENANO_SID = k;
+			auth_level = USER_LEVEL_ADMIN;
+			var loc = makeUrlNS('Special', 'Administration');
+			if ( (ENANO_SID + ' ').length > 1 )
+				window.location = loc;
+		}, USER_LEVEL_ADMIN);
+		return false;
+	}
+	var loc = makeUrlNS('Special', 'Administration');
+	window.location = loc;
 }
 
 window.ajaxAdminPage = function()
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  if ( auth_level < USER_LEVEL_ADMIN )
-  {
-    ajaxPromptAdminAuth(function(k) {
-      ENANO_SID = k;
-      auth_level = USER_LEVEL_ADMIN;
-      var loc = String(window.location + '');
-      window.location = append_sid(loc);
-      var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'PageManager&source=ajax&page_id=' + ajaxEscape(title));
-      if ( (ENANO_SID + ' ').length > 1 )
-        window.location = loc;
-    }, 9);
-    return false;
-  }
-  var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'PageManager&source=ajax&page_id=' + ajaxEscape(title));
-  window.location = loc;
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	if ( auth_level < USER_LEVEL_ADMIN )
+	{
+		ajaxPromptAdminAuth(function(k) {
+			ENANO_SID = k;
+			auth_level = USER_LEVEL_ADMIN;
+			var loc = String(window.location + '');
+			window.location = append_sid(loc);
+			var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'PageManager&source=ajax&page_id=' + ajaxEscape(title));
+			if ( (ENANO_SID + ' ').length > 1 )
+				window.location = loc;
+		}, 9);
+		return false;
+	}
+	var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'PageManager&source=ajax&page_id=' + ajaxEscape(title));
+	window.location = loc;
 }
 
 window.ajaxLoginNavTo = function(namespace, page_id, min_level, get)
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  void(namespace);
-  void(page_id);
-  get = get || false;
-  if ( auth_level < min_level )
-  {
-    ajaxPromptAdminAuth(function(k) {
-      ENANO_SID = k;
-      auth_level = min_level;
-      var loc = makeUrlNS(namespace, page_id, get);
-      if ( (ENANO_SID + ' ').length > 1 )
-        window.location = loc;
-    }, min_level);
-    return false;
-  }
-  var loc = makeUrlNS(namespace, page_id, get);
-  window.location = loc;
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	void(namespace);
+	void(page_id);
+	get = get || false;
+	if ( auth_level < min_level )
+	{
+		ajaxPromptAdminAuth(function(k) {
+			ENANO_SID = k;
+			auth_level = min_level;
+			var loc = makeUrlNS(namespace, page_id, get);
+			if ( (ENANO_SID + ' ').length > 1 )
+				window.location = loc;
+		}, min_level);
+		return false;
+	}
+	var loc = makeUrlNS(namespace, page_id, get);
+	window.location = loc;
 }
 
 window.ajaxAdminUser = function(username)
 {
-  // IE <6 pseudo-compatibility
-  if ( KILL_SWITCH )
-    return true;
-  if ( auth_level < USER_LEVEL_ADMIN )
-  {
-    ajaxPromptAdminAuth(function(k) {
-      ENANO_SID = k;
-      auth_level = USER_LEVEL_ADMIN;
-      var loc = String(window.location + '');
-      window.location = append_sid(loc);
-      var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'UserManager&src=get&user=' + ajaxEscape(username));
-      if ( (ENANO_SID + ' ').length > 1 )
-        window.location = loc;
-    }, 9);
-    return false;
-  }
-  var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'UserManager&src=get&user=' + ajaxEscape(username));
-  window.location = loc;
+	// IE <6 pseudo-compatibility
+	if ( KILL_SWITCH )
+		return true;
+	if ( auth_level < USER_LEVEL_ADMIN )
+	{
+		ajaxPromptAdminAuth(function(k) {
+			ENANO_SID = k;
+			auth_level = USER_LEVEL_ADMIN;
+			var loc = String(window.location + '');
+			window.location = append_sid(loc);
+			var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'UserManager&src=get&user=' + ajaxEscape(username));
+			if ( (ENANO_SID + ' ').length > 1 )
+				window.location = loc;
+		}, 9);
+		return false;
+	}
+	var loc = makeUrlNS('Special', 'Administration', 'module=' + namespace_list['Admin'] + 'UserManager&src=get&user=' + ajaxEscape(username));
+	window.location = loc;
 }
 
 window.ajaxDynamicReauth = function(adminpage, level)
 {
-  if ( auth_level < USER_LEVEL_MEMBER )
-  {
-    ajaxStartLogin();
-    return false;
-  }
-  
-  var old_sid = ENANO_SID;
-  var targetpage = adminpage;
-  if ( !level )
-  {
-    level = USER_LEVEL_ADMIN;
-  }
-  
-  ajaxLogonInit(function(k, response)
-    {
-      ajaxLoginReplaceSIDInline(k, old_sid, level);
-      window.user_id = response.user_id;
-      window.user_level = response.user_level;
-      mb_current_obj.destroy();
-      if ( typeof(targetpage) == 'string' )
-      {
-        ajaxPage(targetpage);
-      }
-      else if ( typeof(targetpage) == 'function' )
-      {
-        targetpage(k);
-      }
-    }, level);
-  if ( typeof(adminpage) == 'string' )
-  {
-    ajaxLoginShowFriendlyError({
-        error_code: 'admin_session_timed_out',
-        respawn_info: {}
-    });
-  }
+	if ( auth_level < USER_LEVEL_MEMBER )
+	{
+		ajaxStartLogin();
+		return false;
+	}
+	
+	var old_sid = ENANO_SID;
+	var targetpage = adminpage;
+	if ( !level )
+	{
+		level = USER_LEVEL_ADMIN;
+	}
+	
+	ajaxLogonInit(function(k, response)
+		{
+			ajaxLoginReplaceSIDInline(k, old_sid, level);
+			window.user_id = response.user_id;
+			window.user_level = response.user_level;
+			mb_current_obj.destroy();
+			if ( typeof(targetpage) == 'string' )
+			{
+				ajaxPage(targetpage);
+			}
+			else if ( typeof(targetpage) == 'function' )
+			{
+				targetpage(k);
+			}
+		}, level);
+	if ( typeof(adminpage) == 'string' )
+	{
+		ajaxLoginShowFriendlyError({
+				error_code: 'admin_session_timed_out',
+				respawn_info: {}
+		});
+	}
 }
 
 window.ajaxRenewSession = function()
 {
-  ajaxDynamicReauth(false);
+	ajaxDynamicReauth(false);
 }
 
 window.ajaxTrashElevSession = function()
 {
-  load_component(['messagebox', 'fadefilter', 'l10n', 'flyin', 'jquery', 'jquery-ui']);
-  miniPromptMessage({
-    title: $lang.get('user_logout_confirm_title_elev'),
-    message: $lang.get('user_logout_confirm_body_elev'),
-    buttons: [
-      {
-        text: $lang.get('user_logout_confirm_btn_logout'),
-        color: 'red',
-        style: {
-          fontWeight: 'bold'
-        },
-        onclick: function()
-        {
-          ajaxLoginPerformRequest({
-              mode:  'logout',
-              level: auth_level,
-              csrf_token: csrf_token
-          });
-          miniPromptDestroy(this);
-        }
-      },
-      {
-        text: $lang.get('etc_cancel'),
-        onclick: function()
-        {
-          miniPromptDestroy(this);
-        }
-      }
-    ]
-  });
+	load_component(['messagebox', 'fadefilter', 'l10n', 'flyin', 'jquery', 'jquery-ui']);
+	miniPromptMessage({
+		title: $lang.get('user_logout_confirm_title_elev'),
+		message: $lang.get('user_logout_confirm_body_elev'),
+		buttons: [
+			{
+				text: $lang.get('user_logout_confirm_btn_logout'),
+				color: 'red',
+				style: {
+					fontWeight: 'bold'
+				},
+				onclick: function()
+				{
+					ajaxLoginPerformRequest({
+							mode:  'logout',
+							level: auth_level,
+							csrf_token: csrf_token
+					});
+					miniPromptDestroy(this);
+				}
+			},
+			{
+				text: $lang.get('etc_cancel'),
+				onclick: function()
+				{
+					miniPromptDestroy(this);
+				}
+			}
+		]
+	});
 }
 
 /**
@@ -1337,84 +1337,84 @@
 
 window.ajaxLoginReplaceSIDInline = function(key, oldkey, level)
 {
-  var host = String(window.location.hostname);
-  var exp = new RegExp('^https?://' + host.replace('.', '\.') + contentPath.replace('.', '\.'), 'g');
-  var rexp = new RegExp('^https?://' + host.replace('.', '\.'), 'g');
-  
-  if ( key )
-  {
-    if ( oldkey )
-    {
-      var body = document.getElementsByTagName('body')[0];
-      var replace = new RegExp(oldkey, 'g');
-      body.innerHTML = body.innerHTML.replace(replace, key);
-      ENANO_SID = key;
-    }
-    else
-    {
-      // append SID to all internal links
-      ENANO_SID = key;
-      
-      var links = document.getElementsByTagName('a');
-      for ( var i = 0; i < links.length; i++ )
-      {
-        if ( links[i].href.match(exp, links[i]) && links[i].href.indexOf('#') == -1 )
-        {
-          var newurl = (String(append_sid(links[i].href))).replace(rexp, '');
-          links[i].href = newurl;
-        }
-      }
-      
-      var forms = document.getElementsByTagName('form');
-      for ( var i = 0; i < forms.length; i++ )
-      {
-        if ( forms[i].method.toLowerCase() == 'post' )
-        {
-          if ( forms[i].action.match(exp, links[i]) )
-          {
-            var newurl = (String(append_sid(forms[i].action))).replace(rexp, '');
-            forms[i].action = newurl;
-          }
-        }
-        else
-        {
-          if ( !forms[i].auth )
-          {
-            var auth = document.createElement('input');
-            auth.type = 'hidden';
-            auth.name = 'auth';
-            auth.value = key;
-            forms[i].appendChild(auth);
-          }
-          else
-          {
-            forms[i].auth.value = key;
-          }
-        }
-      }
-    }
-    if ( level )
-    {
-      auth_level = level;
-    }
-    window.location.hash = '#auth:' + key;
-  }
-  else
-  {
-    auth_level = USER_LEVEL_MEMBER;
-    ENANO_SID = false;
-    if ( oldkey )
-    {
-      var links = document.getElementsByTagName('a');
-      for ( var i = 0; i < links.length; i++ )
-      {
-        if ( links[i].href.match(exp, links[i]) && links[i].href.indexOf('#') == -1 )
-        {
-          links[i].href = links[i].href.replace(/\?auth=([a-f0-9]+)(&|#|$)/, '$2').replace(/&auth=([a-f0-9]+)/, '').replace(rexp, '');
-        }
-      }
-    }
-    window.location.hash = '#auth:false';
-  }
-  window.stdAjaxPrefix = append_sid(scriptPath + '/ajax.php?title=' + title);
+	var host = String(window.location.hostname);
+	var exp = new RegExp('^https?://' + host.replace('.', '\.') + contentPath.replace('.', '\.'), 'g');
+	var rexp = new RegExp('^https?://' + host.replace('.', '\.'), 'g');
+	
+	if ( key )
+	{
+		if ( oldkey )
+		{
+			var body = document.getElementsByTagName('body')[0];
+			var replace = new RegExp(oldkey, 'g');
+			body.innerHTML = body.innerHTML.replace(replace, key);
+			ENANO_SID = key;
+		}
+		else
+		{
+			// append SID to all internal links
+			ENANO_SID = key;
+			
+			var links = document.getElementsByTagName('a');
+			for ( var i = 0; i < links.length; i++ )
+			{
+				if ( links[i].href.match(exp, links[i]) && links[i].href.indexOf('#') == -1 )
+				{
+					var newurl = (String(append_sid(links[i].href))).replace(rexp, '');
+					links[i].href = newurl;
+				}
+			}
+			
+			var forms = document.getElementsByTagName('form');
+			for ( var i = 0; i < forms.length; i++ )
+			{
+				if ( forms[i].method.toLowerCase() == 'post' )
+				{
+					if ( forms[i].action.match(exp, links[i]) )
+					{
+						var newurl = (String(append_sid(forms[i].action))).replace(rexp, '');
+						forms[i].action = newurl;
+					}
+				}
+				else
+				{
+					if ( !forms[i].auth )
+					{
+						var auth = document.createElement('input');
+						auth.type = 'hidden';
+						auth.name = 'auth';
+						auth.value = key;
+						forms[i].appendChild(auth);
+					}
+					else
+					{
+						forms[i].auth.value = key;
+					}
+				}
+			}
+		}
+		if ( level )
+		{
+			auth_level = level;
+		}
+		window.location.hash = '#auth:' + key;
+	}
+	else
+	{
+		auth_level = USER_LEVEL_MEMBER;
+		ENANO_SID = false;
+		if ( oldkey )
+		{
+			var links = document.getElementsByTagName('a');
+			for ( var i = 0; i < links.length; i++ )
+			{
+				if ( links[i].href.match(exp, links[i]) && links[i].href.indexOf('#') == -1 )
+				{
+					links[i].href = links[i].href.replace(/\?auth=([a-f0-9]+)(&|#|$)/, '$2').replace(/&auth=([a-f0-9]+)/, '').replace(rexp, '');
+				}
+			}
+		}
+		window.location.hash = '#auth:false';
+	}
+	window.stdAjaxPrefix = append_sid(scriptPath + '/ajax.php?title=' + title);
 }
--- a/includes/clientside/static/messagebox.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/messagebox.js	Sun Mar 28 23:10:46 2010 -0400
@@ -29,287 +29,287 @@
 
 function MessageBox(type, title, message)
 {
-  if ( !aclDisableTransitionFX )
-  {
-    load_component('flyin');
-  }
-  
-  var y = getScrollOffset();
-  
-  // Prevent multiple instances
-  if ( document.getElementById('messageBox') )
-    return;
-  
-  if ( document.getElementById('specialLayer_darkener') )
-    if ( document.getElementById('specialLayer_darkener').style.display == 'block' )
-      mb_previously_had_darkener = true;
-  if ( !mb_previously_had_darkener )
-    darken(true);
-  if ( aclDisableTransitionFX )
-  {
-    document.getElementById('specialLayer_darkener').style.zIndex = '5';
-  }
-  var master_div = document.createElement('div');
-  master_div.style.zIndex = getHighestZ() + 1;
-  var mydiv = document.createElement('div');
-  mydiv.style.height = '200px';
-  w = getWidth();
-  h = getHeight();
-  if ( aclDisableTransitionFX )
-  {
-    master_div.style.left = ((w / 2) - 200)+'px';
-    master_div.style.top = ((h / 2) + y - 120)+'px';
-    master_div.style.position = 'absolute';
-  }
-  else
-  {
-    master_div.style.top = '-10000px';
-    master_div.style.position = ( IE ) ? 'absolute' : 'fixed';
-  }
-  z = ( aclDisableTransitionFX ) ? document.getElementById('specialLayer_darkener').style.zIndex + 1: getHighestZ() + 1;
-  mydiv.style.backgroundColor = '#FFFFFF';
-  mydiv.style.padding = '10px';
-  mydiv.style.marginBottom = '1px';
-  mydiv.id = 'messageBox';
-  mydiv.style.overflow = 'auto';
-  
-  var buttondiv = document.createElement('div');
-  
-  mydiv.style.width = '400px';
-  buttondiv.style.width = '400px';
-  
-  w = getWidth();
-  h = getHeight();
-  if ( aclDisableTransitionFX )
-  {
-    //buttondiv.style.left = ((w / 2) - 200)+'px';
-    //buttondiv.style.top = ((h / 2) + y + 101)+'px';
-  }
-  //buttondiv.style.position = ( IE ) ? 'absolute' : 'fixed';
-  z = ( aclDisableTransitionFX ) ? document.getElementById('specialLayer_darkener').style.zIndex : getHighestZ();
-  buttondiv.style.backgroundColor = '#C0C0C0';
-  buttondiv.style.padding = '10px';
-  buttondiv.style.textAlign = 'right';
-  buttondiv.style.verticalAlign = 'middle';
-  buttondiv.id = 'messageBoxButtons';
-  
-  this.clickHandler = function() { messagebox_click(this, mb_current_obj); };
-  
-  if( ( type & MB_ICONINFORMATION || type & MB_ICONSTOP || type & MB_ICONQUESTION || type & MB_ICONEXCLAMATION ) && !(type & MB_ICONLOCK) )
-  {
-    mydiv.style.paddingLeft = '50px';
-    mydiv.style.width = '360px';
-    mydiv.style.backgroundRepeat = 'no-repeat';
-    mydiv.style.backgroundPosition = '8px 8px';
-  }
-  else if ( type & MB_ICONLOCK )
-  {
-    mydiv.style.paddingLeft = '50px';
-    mydiv.style.width = '360px';
-    mydiv.style.backgroundRepeat = 'no-repeat';
-  }
-  
-  if(type & MB_ICONINFORMATION)
-  {
-    mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/info.png\')';
-  }
-  
-  if(type & MB_ICONQUESTION)
-  {
-    mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/question.png\')';
-  }
-  
-  if(type & MB_ICONSTOP)
-  {
-    mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/error.png\')';
-  }
-  
-  if(type & MB_ICONEXCLAMATION)
-  {
-    mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/warning.png\')';
-  }
-  
-  if(type & MB_ICONLOCK)
-  {
-    mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/lock.png\')';
-  }
-  
-  if(type & MB_OK)
-  {
-    btn = document.createElement('input');
-    btn.type = 'button';
-    btn.value = $lang.get('etc_ok');
-    btn._GenericName = 'OK';
-    btn.onclick = this.clickHandler;
-    btn.style.margin = '0 3px';
-    buttondiv.appendChild(btn);
-  }
-  
-  if(type & MB_OKCANCEL)
-  {
-    btn = document.createElement('input');
-    btn.type = 'button';
-    btn.value = $lang.get('etc_ok');
-    btn._GenericName = 'OK';
-    btn.onclick = this.clickHandler;
-    btn.style.margin = '0 3px';
-    buttondiv.appendChild(btn);
-    
-    btn = document.createElement('input');
-    btn.type = 'button';
-    btn.value = $lang.get('etc_cancel');
-    btn._GenericName = 'Cancel';
-    btn.onclick = this.clickHandler;
-    btn.style.margin = '0 3px';
-    buttondiv.appendChild(btn);
-  }
-  
-  if(type & MB_YESNO)
-  {
-    btn = document.createElement('input');
-    btn.type = 'button';
-    btn.value = $lang.get('etc_yes');
-    btn._GenericName = 'Yes';
-    btn.onclick = this.clickHandler;
-    btn.style.margin = '0 3px';
-    buttondiv.appendChild(btn);
-    
-    btn = document.createElement('input');
-    btn.type = 'button';
-    btn.value = $lang.get('etc_no');
-    btn._GenericName = 'No';
-    btn.onclick = this.clickHandler;
-    btn.style.margin = '0 3px';
-    buttondiv.appendChild(btn);
-  }
-  
-  if(type & MB_YESNOCANCEL)
-  {
-    btn = document.createElement('input');
-    btn.type = 'button';
-    btn.value = $lang.get('etc_yes');
-    btn._GenericName = 'Yes';
-    btn.onclick = this.clickHandler;
-    btn.style.margin = '0 3px';
-    buttondiv.appendChild(btn);
-    
-    btn = document.createElement('input');
-    btn.type = 'button';
-    btn.value = $lang.get('etc_no');
-    btn._GenericName = 'No';
-    btn.onclick = this.clickHandler;
-    btn.style.margin = '0 3px';
-    buttondiv.appendChild(btn);
-    
-    btn = document.createElement('input');
-    btn.type = 'button';
-    btn.value = $lang.get('etc_cancel');
-    btn._GenericName = 'Cancel';
-    btn.onclick = this.clickHandler;
-    btn.style.margin = '0 3px';
-    buttondiv.appendChild(btn);
-  }
-  
-  heading = document.createElement('h2');
-  heading.innerHTML = title;
-  heading.style.color = '#50A0D0';
-  heading.style.fontFamily = 'trebuchet ms, verdana, arial, helvetica, sans-serif';
-  heading.style.fontSize = '12pt';
-  heading.style.fontWeight = 'lighter';
-  heading.style.textTransform = 'lowercase';
-  heading.style.marginTop = '0';
-  mydiv.appendChild(heading);
-  
-  var text = document.createElement('div');
-  text.innerHTML = String(message);
-  this.text_area = text;
-  mydiv.appendChild(text);
-  
-  this.updateContent = function(text)
-    {
-      this.text_area.innerHTML = text;
-    };
-    
-  this.destroy = function()
-    {
-      var mbdiv = document.getElementById('messageBox');
-      mbdiv.parentNode.parentNode.removeChild(mbdiv.parentNode);
-      if ( !mb_previously_had_darkener )
-        enlighten(true);
-    };
-  
-  //domObjChangeOpac(0, mydiv);
-  //domObjChangeOpac(0, master_div);
-  
-  body = document.getElementsByTagName('body');
-  body = body[0];
-  master_div.appendChild(mydiv);
-  master_div.appendChild(buttondiv);
-  
-  body.appendChild(master_div);
-  
-  if ( !aclDisableTransitionFX )
-    setTimeout('mb_runFlyIn();', 100);
-  
-  this.onclick = new Array();
-  this.onbeforeclick = new Array();
-  mb_current_obj = this;
+	if ( !aclDisableTransitionFX )
+	{
+		load_component('flyin');
+	}
+	
+	var y = getScrollOffset();
+	
+	// Prevent multiple instances
+	if ( document.getElementById('messageBox') )
+		return;
+	
+	if ( document.getElementById('specialLayer_darkener') )
+		if ( document.getElementById('specialLayer_darkener').style.display == 'block' )
+			mb_previously_had_darkener = true;
+	if ( !mb_previously_had_darkener )
+		darken(true);
+	if ( aclDisableTransitionFX )
+	{
+		document.getElementById('specialLayer_darkener').style.zIndex = '5';
+	}
+	var master_div = document.createElement('div');
+	master_div.style.zIndex = getHighestZ() + 1;
+	var mydiv = document.createElement('div');
+	mydiv.style.height = '200px';
+	w = getWidth();
+	h = getHeight();
+	if ( aclDisableTransitionFX )
+	{
+		master_div.style.left = ((w / 2) - 200)+'px';
+		master_div.style.top = ((h / 2) + y - 120)+'px';
+		master_div.style.position = 'absolute';
+	}
+	else
+	{
+		master_div.style.top = '-10000px';
+		master_div.style.position = ( IE ) ? 'absolute' : 'fixed';
+	}
+	z = ( aclDisableTransitionFX ) ? document.getElementById('specialLayer_darkener').style.zIndex + 1: getHighestZ() + 1;
+	mydiv.style.backgroundColor = '#FFFFFF';
+	mydiv.style.padding = '10px';
+	mydiv.style.marginBottom = '1px';
+	mydiv.id = 'messageBox';
+	mydiv.style.overflow = 'auto';
+	
+	var buttondiv = document.createElement('div');
+	
+	mydiv.style.width = '400px';
+	buttondiv.style.width = '400px';
+	
+	w = getWidth();
+	h = getHeight();
+	if ( aclDisableTransitionFX )
+	{
+		//buttondiv.style.left = ((w / 2) - 200)+'px';
+		//buttondiv.style.top = ((h / 2) + y + 101)+'px';
+	}
+	//buttondiv.style.position = ( IE ) ? 'absolute' : 'fixed';
+	z = ( aclDisableTransitionFX ) ? document.getElementById('specialLayer_darkener').style.zIndex : getHighestZ();
+	buttondiv.style.backgroundColor = '#C0C0C0';
+	buttondiv.style.padding = '10px';
+	buttondiv.style.textAlign = 'right';
+	buttondiv.style.verticalAlign = 'middle';
+	buttondiv.id = 'messageBoxButtons';
+	
+	this.clickHandler = function() { messagebox_click(this, mb_current_obj); };
+	
+	if( ( type & MB_ICONINFORMATION || type & MB_ICONSTOP || type & MB_ICONQUESTION || type & MB_ICONEXCLAMATION ) && !(type & MB_ICONLOCK) )
+	{
+		mydiv.style.paddingLeft = '50px';
+		mydiv.style.width = '360px';
+		mydiv.style.backgroundRepeat = 'no-repeat';
+		mydiv.style.backgroundPosition = '8px 8px';
+	}
+	else if ( type & MB_ICONLOCK )
+	{
+		mydiv.style.paddingLeft = '50px';
+		mydiv.style.width = '360px';
+		mydiv.style.backgroundRepeat = 'no-repeat';
+	}
+	
+	if(type & MB_ICONINFORMATION)
+	{
+		mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/info.png\')';
+	}
+	
+	if(type & MB_ICONQUESTION)
+	{
+		mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/question.png\')';
+	}
+	
+	if(type & MB_ICONSTOP)
+	{
+		mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/error.png\')';
+	}
+	
+	if(type & MB_ICONEXCLAMATION)
+	{
+		mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/warning.png\')';
+	}
+	
+	if(type & MB_ICONLOCK)
+	{
+		mydiv.style.backgroundImage = 'url(\''+scriptPath+'/images/lock.png\')';
+	}
+	
+	if(type & MB_OK)
+	{
+		btn = document.createElement('input');
+		btn.type = 'button';
+		btn.value = $lang.get('etc_ok');
+		btn._GenericName = 'OK';
+		btn.onclick = this.clickHandler;
+		btn.style.margin = '0 3px';
+		buttondiv.appendChild(btn);
+	}
+	
+	if(type & MB_OKCANCEL)
+	{
+		btn = document.createElement('input');
+		btn.type = 'button';
+		btn.value = $lang.get('etc_ok');
+		btn._GenericName = 'OK';
+		btn.onclick = this.clickHandler;
+		btn.style.margin = '0 3px';
+		buttondiv.appendChild(btn);
+		
+		btn = document.createElement('input');
+		btn.type = 'button';
+		btn.value = $lang.get('etc_cancel');
+		btn._GenericName = 'Cancel';
+		btn.onclick = this.clickHandler;
+		btn.style.margin = '0 3px';
+		buttondiv.appendChild(btn);
+	}
+	
+	if(type & MB_YESNO)
+	{
+		btn = document.createElement('input');
+		btn.type = 'button';
+		btn.value = $lang.get('etc_yes');
+		btn._GenericName = 'Yes';
+		btn.onclick = this.clickHandler;
+		btn.style.margin = '0 3px';
+		buttondiv.appendChild(btn);
+		
+		btn = document.createElement('input');
+		btn.type = 'button';
+		btn.value = $lang.get('etc_no');
+		btn._GenericName = 'No';
+		btn.onclick = this.clickHandler;
+		btn.style.margin = '0 3px';
+		buttondiv.appendChild(btn);
+	}
+	
+	if(type & MB_YESNOCANCEL)
+	{
+		btn = document.createElement('input');
+		btn.type = 'button';
+		btn.value = $lang.get('etc_yes');
+		btn._GenericName = 'Yes';
+		btn.onclick = this.clickHandler;
+		btn.style.margin = '0 3px';
+		buttondiv.appendChild(btn);
+		
+		btn = document.createElement('input');
+		btn.type = 'button';
+		btn.value = $lang.get('etc_no');
+		btn._GenericName = 'No';
+		btn.onclick = this.clickHandler;
+		btn.style.margin = '0 3px';
+		buttondiv.appendChild(btn);
+		
+		btn = document.createElement('input');
+		btn.type = 'button';
+		btn.value = $lang.get('etc_cancel');
+		btn._GenericName = 'Cancel';
+		btn.onclick = this.clickHandler;
+		btn.style.margin = '0 3px';
+		buttondiv.appendChild(btn);
+	}
+	
+	heading = document.createElement('h2');
+	heading.innerHTML = title;
+	heading.style.color = '#50A0D0';
+	heading.style.fontFamily = 'trebuchet ms, verdana, arial, helvetica, sans-serif';
+	heading.style.fontSize = '12pt';
+	heading.style.fontWeight = 'lighter';
+	heading.style.textTransform = 'lowercase';
+	heading.style.marginTop = '0';
+	mydiv.appendChild(heading);
+	
+	var text = document.createElement('div');
+	text.innerHTML = String(message);
+	this.text_area = text;
+	mydiv.appendChild(text);
+	
+	this.updateContent = function(text)
+		{
+			this.text_area.innerHTML = text;
+		};
+		
+	this.destroy = function()
+		{
+			var mbdiv = document.getElementById('messageBox');
+			mbdiv.parentNode.parentNode.removeChild(mbdiv.parentNode);
+			if ( !mb_previously_had_darkener )
+				enlighten(true);
+		};
+	
+	//domObjChangeOpac(0, mydiv);
+	//domObjChangeOpac(0, master_div);
+	
+	body = document.getElementsByTagName('body');
+	body = body[0];
+	master_div.appendChild(mydiv);
+	master_div.appendChild(buttondiv);
+	
+	body.appendChild(master_div);
+	
+	if ( !aclDisableTransitionFX )
+		setTimeout('mb_runFlyIn();', 100);
+	
+	this.onclick = new Array();
+	this.onbeforeclick = new Array();
+	mb_current_obj = this;
 }
 
 var messagebox = MessageBox;
 
 function mb_runFlyIn()
 {
-  var mydiv = document.getElementById('messageBox');
-  var maindiv = mydiv.parentNode;
-  fly_in_top(maindiv, true, false);
+	var mydiv = document.getElementById('messageBox');
+	var maindiv = mydiv.parentNode;
+	fly_in_top(maindiv, true, false);
 }
 
 function messagebox_click(obj, mb)
 {
-  val = ( typeof ( obj._GenericName ) == 'string' ) ? obj._GenericName : obj.value;
-  if(typeof mb.onbeforeclick[val] == 'function')
-  {
-    var o = mb.onbeforeclick[val];
-    var resp = o();
-    if ( resp )
-      return false;
-    o = false;
-  }
-  
-  var mydiv = document.getElementById('messageBox');
-  var maindiv = mydiv.parentNode;
-  
-  if ( aclDisableTransitionFX )
-  {
-    var mbdiv = document.getElementById('messageBox');
-    mbdiv.parentNode.removeChild(mbdiv.nextSibling);
-    mbdiv.parentNode.removeChild(mbdiv);
-    if ( !mb_previously_had_darkener )
-      enlighten(true);
-  }
-  else
-  {
-    var to = fly_out_top(maindiv, true, false);
-    setTimeout("var mbdiv = document.getElementById('messageBox'); mbdiv.parentNode.parentNode.removeChild(mbdiv.parentNode); if ( !mb_previously_had_darkener ) enlighten(true);", to);
-  }
-  if(typeof mb.onclick[val] == 'function')
-  {
-    (mb.onclick[val])();
-  }
+	val = ( typeof ( obj._GenericName ) == 'string' ) ? obj._GenericName : obj.value;
+	if(typeof mb.onbeforeclick[val] == 'function')
+	{
+		var o = mb.onbeforeclick[val];
+		var resp = o();
+		if ( resp )
+			return false;
+		o = false;
+	}
+	
+	var mydiv = document.getElementById('messageBox');
+	var maindiv = mydiv.parentNode;
+	
+	if ( aclDisableTransitionFX )
+	{
+		var mbdiv = document.getElementById('messageBox');
+		mbdiv.parentNode.removeChild(mbdiv.nextSibling);
+		mbdiv.parentNode.removeChild(mbdiv);
+		if ( !mb_previously_had_darkener )
+			enlighten(true);
+	}
+	else
+	{
+		var to = fly_out_top(maindiv, true, false);
+		setTimeout("var mbdiv = document.getElementById('messageBox'); mbdiv.parentNode.parentNode.removeChild(mbdiv.parentNode); if ( !mb_previously_had_darkener ) enlighten(true);", to);
+	}
+	if(typeof mb.onclick[val] == 'function')
+	{
+		(mb.onclick[val])();
+	}
 }
 
 function testMessageBox()
 {
-  mb = new MessageBox(MB_OKCANCEL|MB_ICONINFORMATION, 'Javascripted dynamic message boxes', 'This is soooooo coool, now if only document.createElement() worked in IE!<br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text');
-  mb.onclick['OK'] = function()
-    {
-      alert('You clicked OK!');
-    }
-  mb.onbeforeclick['Cancel'] = function()
-    {
-      alert('You clicked Cancel!');
-    }
+	mb = new MessageBox(MB_OKCANCEL|MB_ICONINFORMATION, 'Javascripted dynamic message boxes', 'This is soooooo coool, now if only document.createElement() worked in IE!<br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text<br /><br /><br /><br /><br />this is some more text');
+	mb.onclick['OK'] = function()
+		{
+			alert('You clicked OK!');
+		}
+	mb.onbeforeclick['Cancel'] = function()
+		{
+			alert('You clicked Cancel!');
+		}
 }
 
 /**
@@ -319,64 +319,64 @@
 
 function miniPrompt(call_on_create)
 {
-  if ( !aclDisableTransitionFX )
-  {
-    load_component(['flyin', 'jquery', 'jquery-ui', 'fadefilter']);
-  }
-  else
-  {
-    load_component(['fadefilter']);
-  }
-  var darkener = darken(aclDisableTransitionFX, 40, 'miniprompt_darkener');
-  
-  var wrapper = document.createElement('div');
-  wrapper.className = 'miniprompt';
-  var top = document.createElement('div');
-  top.className = 'mp-top';
-  var body = document.createElement('div');
-  body.className = 'mp-body';
-  var bottom = document.createElement('div');
-  bottom.className = 'mp-bottom';
-  if ( typeof(call_on_create) == 'function' )
-  {
-    call_on_create(body);
-  }
-  wrapper.appendChild(top);
-  wrapper.appendChild(body);
-  wrapper.appendChild(bottom);
-  var left = ( getWidth() / 2 ) - ( 388 / 2 );
-  wrapper.style.left = left + 'px';
-  var top = getScrollOffset() - 27;
-  wrapper.style.top = top + 'px';
-  domObjChangeOpac(0, wrapper);
-  var realbody = document.getElementsByTagName('body')[0];
-  realbody.appendChild(wrapper);
-  
-  if ( aclDisableTransitionFX )
-  {
-    domObjChangeOpac(100, wrapper);
-  }
-  else
-  {
-    fly_in_top(wrapper, true, true);
-    
-    setTimeout(function()
-      {
-        domObjChangeOpac(100, wrapper);
-      }, 40);
-  }
-  
-  // set the darkener's onclick to refocus/shake the miniprompt
-  darkener.miniprompt = wrapper;
-  darkener.onclick = function()
-  {
-    if ( !aclDisableTransitionFX )
-    {
-      $(this.miniprompt).effect("pulsate", { times: 2 }, 200);
-    }
-  }
-  
-  return wrapper;
+	if ( !aclDisableTransitionFX )
+	{
+		load_component(['flyin', 'jquery', 'jquery-ui', 'fadefilter']);
+	}
+	else
+	{
+		load_component(['fadefilter']);
+	}
+	var darkener = darken(aclDisableTransitionFX, 40, 'miniprompt_darkener');
+	
+	var wrapper = document.createElement('div');
+	wrapper.className = 'miniprompt';
+	var top = document.createElement('div');
+	top.className = 'mp-top';
+	var body = document.createElement('div');
+	body.className = 'mp-body';
+	var bottom = document.createElement('div');
+	bottom.className = 'mp-bottom';
+	if ( typeof(call_on_create) == 'function' )
+	{
+		call_on_create(body);
+	}
+	wrapper.appendChild(top);
+	wrapper.appendChild(body);
+	wrapper.appendChild(bottom);
+	var left = ( getWidth() / 2 ) - ( 388 / 2 );
+	wrapper.style.left = left + 'px';
+	var top = getScrollOffset() - 27;
+	wrapper.style.top = top + 'px';
+	domObjChangeOpac(0, wrapper);
+	var realbody = document.getElementsByTagName('body')[0];
+	realbody.appendChild(wrapper);
+	
+	if ( aclDisableTransitionFX )
+	{
+		domObjChangeOpac(100, wrapper);
+	}
+	else
+	{
+		fly_in_top(wrapper, true, true);
+		
+		setTimeout(function()
+			{
+				domObjChangeOpac(100, wrapper);
+			}, 40);
+	}
+	
+	// set the darkener's onclick to refocus/shake the miniprompt
+	darkener.miniprompt = wrapper;
+	darkener.onclick = function()
+	{
+		if ( !aclDisableTransitionFX )
+		{
+			$(this.miniprompt).effect("pulsate", { times: 2 }, 200);
+		}
+	}
+	
+	return wrapper;
 }
 
 /**
@@ -387,19 +387,19 @@
 
 function miniPromptGetParent(obj)
 {
-  while ( true )
-  {
-    // prevent infinite loops
-    if ( !obj || obj.tagName == 'BODY' )
-      return false;
-    
-    if ( $dynano(obj).hasClass('miniprompt') )
-    {
-      return obj;
-    }
-    obj = obj.parentNode;
-  }
-  return false;
+	while ( true )
+	{
+		// prevent infinite loops
+		if ( !obj || obj.tagName == 'BODY' )
+			return false;
+		
+		if ( $dynano(obj).hasClass('miniprompt') )
+		{
+			return obj;
+		}
+		obj = obj.parentNode;
+	}
+	return false;
 }
 
 /**
@@ -411,27 +411,27 @@
 
 function miniPromptDestroy(obj, nofade)
 {
-  obj = miniPromptGetParent(obj);
-  if ( !obj )
-    return false;
-  
-  // found it
-  var parent = obj.parentNode;
-  // if ( !nofade )
-  //   enlighten(aclDisableTransitionFX);
-  enlighten((aclDisableTransitionFX || nofade), 'miniprompt_darkener');
-  if ( aclDisableTransitionFX || nofade )
-  {
-    parent.removeChild(obj);
-  }
-  else
-  {
-    var timeout = fly_out_top(obj, true, true);
-    setTimeout(function()
-      {
-        parent.removeChild(obj);
-      }, timeout);
-  }
+	obj = miniPromptGetParent(obj);
+	if ( !obj )
+		return false;
+	
+	// found it
+	var parent = obj.parentNode;
+	// if ( !nofade )
+	//   enlighten(aclDisableTransitionFX);
+	enlighten((aclDisableTransitionFX || nofade), 'miniprompt_darkener');
+	if ( aclDisableTransitionFX || nofade )
+	{
+		parent.removeChild(obj);
+	}
+	else
+	{
+		var timeout = fly_out_top(obj, true, true);
+		setTimeout(function()
+			{
+				parent.removeChild(obj);
+			}, timeout);
+	}
 }
 
 /**
@@ -440,7 +440,7 @@
 
 function miniPromptTest()
 {
-  miniPrompt(function(div) { div.innerHTML = 'hello world! <a href="#" onclick="miniPromptDestroy(this); return false;">destroy me</a>'; });
+	miniPrompt(function(div) { div.innerHTML = 'hello world! <a href="#" onclick="miniPromptDestroy(this); return false;">destroy me</a>'; });
 }
 
 /**
@@ -448,127 +448,127 @@
  * @example
  <code>
  miniPromptMessage({
-   title: 'Delete page',
-   message: 'Do you really want to delete this page? This is reversible unless you clear the page logs.',
-   buttons: [
-     {
-       text: 'Delete',
-       color: 'red',
-       style: {
-         fontWeight: 'bold'
-       },
-       onclick: function() {
-         ajaxDeletePage();
-         miniPromptDestroy(this);
-       }
-     },
-     {
-       text: 'cancel',
-       onclick: function() {
-         miniPromptDestroy(this);
-       }
-     }
-   ]
+ 	title: 'Delete page',
+ 	message: 'Do you really want to delete this page? This is reversible unless you clear the page logs.',
+ 	buttons: [
+ 		{
+ 			text: 'Delete',
+ 			color: 'red',
+ 			style: {
+ 				fontWeight: 'bold'
+ 			},
+ 			onclick: function() {
+ 				ajaxDeletePage();
+ 				miniPromptDestroy(this);
+ 			}
+ 		},
+ 		{
+ 			text: 'cancel',
+ 			onclick: function() {
+ 				miniPromptDestroy(this);
+ 			}
+ 		}
+ 	]
  });
  </code>
  */
 
 function miniPromptMessage(parms)
 {
-  if ( ( !parms.title && !parms.message ) || !parms.buttons )
-    return false;
-  
-  return miniPrompt(function(parent)
-    {
-      try
-      {
-        if ( parms.title )
-        {
-          var h3 = document.createElement('h3');
-          h3.appendChild(document.createTextNode(parms.title));
-        }
-        if ( parms.message )
-        {
-          var body = document.createElement('p');
-          var message = parms.message.split(unescape('%0A'));
-          for ( var i = 0; i < message.length; i++ )
-          {
-            body.appendChild(document.createTextNode(message[i]));
-            if ( i + 1 < message.length )
-              body.appendChild(document.createElement('br'));
-          }
-        }
-        
-        parent.style.textAlign = 'center';
-        
-        if ( parms.title )
-          parent.appendChild(h3);
-        if ( parms.message )
-          parent.appendChild(body);
-        parent.appendChild(document.createElement('br'));
-        
-        // construct buttons
-        for ( var i = 0; i < parms.buttons.length; i++ )
-        {
-          var button = parms.buttons[i];
-          button.input = document.createElement('a');
-          button.input.href = '#';
-          button.input.clickAction = button.onclick;
-          button.input.className = 'abutton';
-          if ( button.color )
-          {
-            button.input.className += ' abutton_' + button.color;
-          }
-          button.input.appendChild(document.createTextNode(button.text));
-          if ( button.style )
-          {
-            for ( var j in button.style )
-            {
-              button.input.style[j] = button.style[j];
-            }
-          }
-          if ( button.sprite )
-          {
-            var sprite = gen_sprite(button.sprite[0], button.sprite[1], button.sprite[2], button.sprite[3], button.sprite[4]);
-            sprite.style.position = 'relative';
-            sprite.style.top = '3px';
-            button.input.insertBefore(sprite, button.input.firstChild);
-            insertAfter(button.input, document.createTextNode(' '), sprite);
-          }
-          else if ( button.image )
-          {
-            button.input.className += ' icon';
-            button.input.style.backgroundImage = 'url(' + button.image + ')';
-          }
-          button.input.onclick = function(e)
-          {
-            try
-            {
-              this.clickAction(e);
-            }
-            catch(e)
-            {
-              console.error(e);
-            }
-            return false;
-          }
-          parent.appendChild(button.input);
-        }
-        // don't focus this in opera - it looks kinda ugly
-        if ( parms.buttons[0] && !window.opera )
-        {
-          var timeout = ( aclDisableTransitionFX ) ? 10 : 1000;
-          setTimeout(function()
-            {
-              parms.buttons[0].input.focus();
-            }, timeout);
-        }
-      }
-      catch ( e )
-      {
-        console.error(e);
-      }
-    });
+	if ( ( !parms.title && !parms.message ) || !parms.buttons )
+		return false;
+	
+	return miniPrompt(function(parent)
+		{
+			try
+			{
+				if ( parms.title )
+				{
+					var h3 = document.createElement('h3');
+					h3.appendChild(document.createTextNode(parms.title));
+				}
+				if ( parms.message )
+				{
+					var body = document.createElement('p');
+					var message = parms.message.split(unescape('%0A'));
+					for ( var i = 0; i < message.length; i++ )
+					{
+						body.appendChild(document.createTextNode(message[i]));
+						if ( i + 1 < message.length )
+							body.appendChild(document.createElement('br'));
+					}
+				}
+				
+				parent.style.textAlign = 'center';
+				
+				if ( parms.title )
+					parent.appendChild(h3);
+				if ( parms.message )
+					parent.appendChild(body);
+				parent.appendChild(document.createElement('br'));
+				
+				// construct buttons
+				for ( var i = 0; i < parms.buttons.length; i++ )
+				{
+					var button = parms.buttons[i];
+					button.input = document.createElement('a');
+					button.input.href = '#';
+					button.input.clickAction = button.onclick;
+					button.input.className = 'abutton';
+					if ( button.color )
+					{
+						button.input.className += ' abutton_' + button.color;
+					}
+					button.input.appendChild(document.createTextNode(button.text));
+					if ( button.style )
+					{
+						for ( var j in button.style )
+						{
+							button.input.style[j] = button.style[j];
+						}
+					}
+					if ( button.sprite )
+					{
+						var sprite = gen_sprite(button.sprite[0], button.sprite[1], button.sprite[2], button.sprite[3], button.sprite[4]);
+						sprite.style.position = 'relative';
+						sprite.style.top = '3px';
+						button.input.insertBefore(sprite, button.input.firstChild);
+						insertAfter(button.input, document.createTextNode(' '), sprite);
+					}
+					else if ( button.image )
+					{
+						button.input.className += ' icon';
+						button.input.style.backgroundImage = 'url(' + button.image + ')';
+					}
+					button.input.onclick = function(e)
+					{
+						try
+						{
+							this.clickAction(e);
+						}
+						catch(e)
+						{
+							console.error(e);
+						}
+						return false;
+					}
+					parent.appendChild(button.input);
+				}
+				// don't focus this in opera - it looks kinda ugly
+				if ( parms.buttons[0] && !window.opera )
+				{
+					var timeout = ( aclDisableTransitionFX ) ? 10 : 1000;
+					setTimeout(function()
+						{
+							parms.buttons[0].input.focus();
+						}, timeout);
+				}
+			}
+			catch ( e )
+			{
+				console.error(e);
+			}
+		});
 }
 
 /**
@@ -577,105 +577,105 @@
 
 function whiteOutMiniPrompt(el)
 {
-  el = miniPromptGetParent(el);
-  var width = 320;
-  var height = $dynano(el).Height() - 58;
-  var topoffset = 27;
-  
-  var container = document.createElement('div');
-  container.style.padding = topoffset + 'px 0 0 0';
+	el = miniPromptGetParent(el);
+	var width = 320;
+	var height = $dynano(el).Height() - 58;
+	var topoffset = 27;
+	
+	var container = document.createElement('div');
+	container.style.padding = topoffset + 'px 0 0 0';
 
-  var top = getScrollOffset();
-  var left = getWidth() / 2 - width / 2;
-  
-  // using fixed here allows modal windows to be blacked out
-  container.style.position = 'absolute';
-  container.style.top = ( top - topoffset ) + 'px';
-  container.style.left = left + 'px';
-  container.style.zIndex = 1000;
-  
-  var blackout = document.createElement('div');
-  blackout.style.backgroundColor = '#ffffff';
-  blackout.style.width = width + 'px';
-  blackout.style.height = height + 'px';
-  domObjChangeOpac(60, blackout);
-  var background = ( $dynano(el).Height() < 48 ) ? 'url(' + scriptPath + '/images/loading.gif)' : 'url(' + scriptPath + '/includes/clientside/tinymce/themes/advanced/skins/default/img/progress.gif)';
-  blackout.style.backgroundImage = background;
-  blackout.style.backgroundPosition = 'center center';
-  blackout.style.backgroundRepeat = 'no-repeat';
-  blackout.isMiniPrompt = true;
-  blackout.miniPromptObj = el;
-  
-  container.appendChild(blackout);
-  var body = document.getElementsByTagName('body')[0];
-  body.appendChild(container);
-  
-  return blackout;
+	var top = getScrollOffset();
+	var left = getWidth() / 2 - width / 2;
+	
+	// using fixed here allows modal windows to be blacked out
+	container.style.position = 'absolute';
+	container.style.top = ( top - topoffset ) + 'px';
+	container.style.left = left + 'px';
+	container.style.zIndex = 1000;
+	
+	var blackout = document.createElement('div');
+	blackout.style.backgroundColor = '#ffffff';
+	blackout.style.width = width + 'px';
+	blackout.style.height = height + 'px';
+	domObjChangeOpac(60, blackout);
+	var background = ( $dynano(el).Height() < 48 ) ? 'url(' + scriptPath + '/images/loading.gif)' : 'url(' + scriptPath + '/includes/clientside/tinymce/themes/advanced/skins/default/img/progress.gif)';
+	blackout.style.backgroundImage = background;
+	blackout.style.backgroundPosition = 'center center';
+	blackout.style.backgroundRepeat = 'no-repeat';
+	blackout.isMiniPrompt = true;
+	blackout.miniPromptObj = el;
+	
+	container.appendChild(blackout);
+	var body = document.getElementsByTagName('body')[0];
+	body.appendChild(container);
+	
+	return blackout;
 }
 
 function whiteOutDestroyOnMiniPrompt(whitey)
 {
-  var body = document.getElementsByTagName('body')[0];
-  var parent = whitey.miniPromptObj;
-  fly_out_top([parent, whitey.parentNode], true, true);
-  setTimeout(function()
-    {
-      body.removeChild(parent);
-      body.removeChild(whitey.parentNode);
-    }, 1000 * FI_MULTIPLIER);
-  enlighten(true, 'miniprompt_darkener');
+	var body = document.getElementsByTagName('body')[0];
+	var parent = whitey.miniPromptObj;
+	fly_out_top([parent, whitey.parentNode], true, true);
+	setTimeout(function()
+		{
+			body.removeChild(parent);
+			body.removeChild(whitey.parentNode);
+		}, 1000 * FI_MULTIPLIER);
+	enlighten(true, 'miniprompt_darkener');
 }
 
 function testMPMessageBox()
 {
-  miniPromptMessage({
-    title: 'The Game of LIFE question #73',
-    message: 'You just got your girlfriend pregnant. Please select an option:',
-    buttons: [
-      {
-        text: 'Abort',
-        color: 'red',
-        style: {
-          fontWeight: 'bold'
-        },
-        sprite: [ cdnPath + '/images/icons/abortretryignore-sprite.png', 16, 16, 0, 0 ],
-        onclick: function() {
-          var w = whiteOutMiniPrompt(this);
-          var me = this;
-          setTimeout(function()
-            {
-              whiteOutReportSuccess(w, true);
-              void(me);
-              setTimeout(function()
-                {
-                  miniPromptDestroy(me);
-                }, 1250);
-            }, 500);
-          return false;
-        }
-      },
-      {
-        text: 'Retry',
-        color: 'blue',
-        sprite: [ cdnPath + '/images/icons/abortretryignore-sprite.png', 16, 16, 0, 16 ],
-        onclick: function() {
-          var w = whiteOutMiniPrompt(this);
-          setTimeout(function()
-            {
-              whiteOutReportSuccess(w);
-            }, 1500);
-          return false;
-        }
-      },
-      {
-        text: 'Ignore',
-        color: 'green',
-        sprite: [ cdnPath + '/images/icons/abortretryignore-sprite.png', 16, 16, 0, 32 ],
-        onclick: function() {
-          miniPromptDestroy(this);
-        }
-      }
-    ]
-  });
+	miniPromptMessage({
+		title: 'The Game of LIFE question #73',
+		message: 'You just got your girlfriend pregnant. Please select an option:',
+		buttons: [
+			{
+				text: 'Abort',
+				color: 'red',
+				style: {
+					fontWeight: 'bold'
+				},
+				sprite: [ cdnPath + '/images/icons/abortretryignore-sprite.png', 16, 16, 0, 0 ],
+				onclick: function() {
+					var w = whiteOutMiniPrompt(this);
+					var me = this;
+					setTimeout(function()
+						{
+							whiteOutReportSuccess(w, true);
+							void(me);
+							setTimeout(function()
+								{
+									miniPromptDestroy(me);
+								}, 1250);
+						}, 500);
+					return false;
+				}
+			},
+			{
+				text: 'Retry',
+				color: 'blue',
+				sprite: [ cdnPath + '/images/icons/abortretryignore-sprite.png', 16, 16, 0, 16 ],
+				onclick: function() {
+					var w = whiteOutMiniPrompt(this);
+					setTimeout(function()
+						{
+							whiteOutReportSuccess(w);
+						}, 1500);
+					return false;
+				}
+			},
+			{
+				text: 'Ignore',
+				color: 'green',
+				sprite: [ cdnPath + '/images/icons/abortretryignore-sprite.png', 16, 16, 0, 32 ],
+				onclick: function() {
+					miniPromptDestroy(this);
+				}
+			}
+		]
+	});
 }
 
--- a/includes/clientside/static/paginate.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/paginate.js	Sun Mar 28 23:10:46 2010 -0400
@@ -13,69 +13,69 @@
 
 window.paginator = function(data, callback, offset, perpage, passer, ov_num_pages, ov_flip_func)
 {
-  load_component('flyin');
-  if ( !perpage || typeof(perpage) != 'number' || ( typeof(perpage) == 'number' && perpage < 1 ) )
-  {
-    this.perpage = 10;
-  }
-  else
-  {
-    this.perpage = perpage;
-  }
-  if ( typeof(offset) != 'number' )
-    this.offset = 0;
-  else
-    this.offset = offset;
-  if ( typeof(passer) != 'undefined' )
-    this.passer = passer;
-  else
-    this.passer = false;
-  if ( ov_num_pages )
-  {
-    this.num_pages = ov_num_pages;
-    this.flip_func = ov_flip_func;
-  }
-  else
-  {
-    this.num_pages = Math.ceil(data.length / perpage);
-    this.flip_func = false;
-  }
-  this.random_id = 'autopagin_' + Math.floor(Math.random() * 1000000);
-  this._build_control = _build_paginator;
-  this.set_page = function(number)
-  {
-    this.offset = number * this.perpage;
-    var html = this._build_control(number);
-    var elements = getElementsByClassName(document.body, 'div', this.random_id + '_control');
-    for ( var i = 0; i < elements.length; i++ )
-      elements[i].innerHTML = html;
-  }
-  if ( this.num_pages > 1 )
-  {
-    var pg_control = '<div class="'+this.random_id+'_control">'+this._build_control(0)+'</div>';
-  }
-  else
-  {
-    var pg_control = '';
-  }
-  this.html = pg_control;
-  var i = 0;
-  while ( i < data.length )
-  {
-    if ( i % this.perpage == 0 )
-    {
-      if ( i > 0 )
-        this.html += '</div>';
-      var display = ( ( i * this.perpage ) == this.offset ) ? '' : 'display: none;';
-      var thispage = Math.floor(i / this.perpage);
-      this.html += '<div id="' + this.random_id + '_' + thispage + '" style="' + display + '">';
-    }
-    this.html += callback(data[i], this.passer);
-    i++;
-  }
-  this.html += '</div>';
-  this.html += pg_control;
-  pagin_objects[this.random_id] = this;
+	load_component('flyin');
+	if ( !perpage || typeof(perpage) != 'number' || ( typeof(perpage) == 'number' && perpage < 1 ) )
+	{
+		this.perpage = 10;
+	}
+	else
+	{
+		this.perpage = perpage;
+	}
+	if ( typeof(offset) != 'number' )
+		this.offset = 0;
+	else
+		this.offset = offset;
+	if ( typeof(passer) != 'undefined' )
+		this.passer = passer;
+	else
+		this.passer = false;
+	if ( ov_num_pages )
+	{
+		this.num_pages = ov_num_pages;
+		this.flip_func = ov_flip_func;
+	}
+	else
+	{
+		this.num_pages = Math.ceil(data.length / perpage);
+		this.flip_func = false;
+	}
+	this.random_id = 'autopagin_' + Math.floor(Math.random() * 1000000);
+	this._build_control = _build_paginator;
+	this.set_page = function(number)
+	{
+		this.offset = number * this.perpage;
+		var html = this._build_control(number);
+		var elements = getElementsByClassName(document.body, 'div', this.random_id + '_control');
+		for ( var i = 0; i < elements.length; i++ )
+			elements[i].innerHTML = html;
+	}
+	if ( this.num_pages > 1 )
+	{
+		var pg_control = '<div class="'+this.random_id+'_control">'+this._build_control(0)+'</div>';
+	}
+	else
+	{
+		var pg_control = '';
+	}
+	this.html = pg_control;
+	var i = 0;
+	while ( i < data.length )
+	{
+		if ( i % this.perpage == 0 )
+		{
+			if ( i > 0 )
+				this.html += '</div>';
+			var display = ( ( i * this.perpage ) == this.offset ) ? '' : 'display: none;';
+			var thispage = Math.floor(i / this.perpage);
+			this.html += '<div id="' + this.random_id + '_' + thispage + '" style="' + display + '">';
+		}
+		this.html += callback(data[i], this.passer);
+		i++;
+	}
+	this.html += '</div>';
+	this.html += pg_control;
+	pagin_objects[this.random_id] = this;
 }
 
 /**
@@ -85,334 +85,334 @@
 
 window._build_paginator = function(this_page)
 {
-  var div_styling = ( IE ) ? 'width: 1px; margin: 10px auto 10px 0;' : 'display: table; margin: 10px 0 10px auto;';
-  var begin = '<div class="tblholder" style="'+div_styling+'"><table border="0" cellspacing="1" cellpadding="4"><tr><th>' + $lang.get('paginate_lbl_page') + '</th>';
-  var block = '<td class="row1" style="text-align: center; white-space: nowrap;">{LINK}</td>';
-  var end = '</tr></table></div>';
-  var blk = new templateParser(block);
-  var inner = '';
-  var cls = 'row2';
-  
-  if ( this_page > 0 )
-  {
-    var url = '#page_'+(this_page);
-    var link = "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+(this_page-1)+"); return false;\" style='text-decoration: none;'>&laquo; " + $lang.get('paginate_btn_prev') + "</a>";
-    cls = ( cls == 'row1' ) ? 'row2' : 'row1';
-    blk.assign_vars({
-        CLASS: cls,
-        LINK: link
-      });
-    inner += blk.run();
-  }
-  if ( this.num_pages < 5 )
-  {
-    for ( var i = 0; i < this.num_pages; i++ )
-    {
-      cls = ( cls == 'row1' ) ? 'row2' : 'row1';
-      var j = i + 1;
-      var url = '#page_'+j;
-      var link = ( i == this_page ) ? "<b>"+j+"</b>" : "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+i+"); return false;\" style='text-decoration: none;'>"+j+"</a>";
-      blk.assign_vars({
-          CLASS: cls,
-          LINK: link
-        });
-      inner += blk.run();
-    }
-  }
-  else
-  {
-    if ( this_page + 5 > this.num_pages )
-    {
-      var list = new Array();
-      var tp = this_page;                    // The vectors below used to be 3, 2, and 1
-      if ( this_page + 0 == this.num_pages ) tp = tp - 2;
-      if ( this_page + 1 == this.num_pages ) tp = tp - 1;
-      if ( this_page + 2 == this.num_pages ) tp = tp - 0;
-      for ( var i = tp - 1; i <= tp + 1; i++ )
-      {
-        list.push(i);
-      }
-    }
-    else
-    {
-      var list = new Array();
-      var current = this_page;
-      var lower = ( current < 3 ) ? 1 : current - 1;
-      for ( var i = 0; i < 3; i++ )
-      {
-        list.push(lower + i);
-      }
-    }
-    var url = '#page_1';
-    var link = ( 0 == this_page ) ? "<b>" + $lang.get('paginate_btn_first') + "</b>" : "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', 0); return false;\" style='text-decoration: none;'>&laquo; " + $lang.get('paginate_btn_first') + "</a>";
-    blk.assign_vars({
-        CLASS: cls,
-        LINK: link
-      });
-    inner += blk.run();
+	var div_styling = ( IE ) ? 'width: 1px; margin: 10px auto 10px 0;' : 'display: table; margin: 10px 0 10px auto;';
+	var begin = '<div class="tblholder" style="'+div_styling+'"><table border="0" cellspacing="1" cellpadding="4"><tr><th>' + $lang.get('paginate_lbl_page') + '</th>';
+	var block = '<td class="row1" style="text-align: center; white-space: nowrap;">{LINK}</td>';
+	var end = '</tr></table></div>';
+	var blk = new templateParser(block);
+	var inner = '';
+	var cls = 'row2';
+	
+	if ( this_page > 0 )
+	{
+		var url = '#page_'+(this_page);
+		var link = "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+(this_page-1)+"); return false;\" style='text-decoration: none;'>&laquo; " + $lang.get('paginate_btn_prev') + "</a>";
+		cls = ( cls == 'row1' ) ? 'row2' : 'row1';
+		blk.assign_vars({
+				CLASS: cls,
+				LINK: link
+			});
+		inner += blk.run();
+	}
+	if ( this.num_pages < 5 )
+	{
+		for ( var i = 0; i < this.num_pages; i++ )
+		{
+			cls = ( cls == 'row1' ) ? 'row2' : 'row1';
+			var j = i + 1;
+			var url = '#page_'+j;
+			var link = ( i == this_page ) ? "<b>"+j+"</b>" : "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+i+"); return false;\" style='text-decoration: none;'>"+j+"</a>";
+			blk.assign_vars({
+					CLASS: cls,
+					LINK: link
+				});
+			inner += blk.run();
+		}
+	}
+	else
+	{
+		if ( this_page + 5 > this.num_pages )
+		{
+			var list = new Array();
+			var tp = this_page;                    // The vectors below used to be 3, 2, and 1
+			if ( this_page + 0 == this.num_pages ) tp = tp - 2;
+			if ( this_page + 1 == this.num_pages ) tp = tp - 1;
+			if ( this_page + 2 == this.num_pages ) tp = tp - 0;
+			for ( var i = tp - 1; i <= tp + 1; i++ )
+			{
+				list.push(i);
+			}
+		}
+		else
+		{
+			var list = new Array();
+			var current = this_page;
+			var lower = ( current < 3 ) ? 1 : current - 1;
+			for ( var i = 0; i < 3; i++ )
+			{
+				list.push(lower + i);
+			}
+		}
+		var url = '#page_1';
+		var link = ( 0 == this_page ) ? "<b>" + $lang.get('paginate_btn_first') + "</b>" : "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', 0); return false;\" style='text-decoration: none;'>&laquo; " + $lang.get('paginate_btn_first') + "</a>";
+		blk.assign_vars({
+				CLASS: cls,
+				LINK: link
+			});
+		inner += blk.run();
 
-    // if ( !in_array(1, $list) )
-    // {
-    //   $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-    //   $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...'));
-    //   $inner .= $blk->run();
-    // }
+		// if ( !in_array(1, $list) )
+		// {
+		//   $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+		//   $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...'));
+		//   $inner .= $blk->run();
+		// }
 
-    for ( var k in list )
-    {
-      var i = list[k];
-      if ( i == this.num_pages )
-        break;
-      cls = ( cls == 'row1' ) ? 'row2' : 'row1';
-      var j = i + 1;
-      var url = '#page_'+j;
-      var link = ( i == this_page ) ? "<b>"+j+"</b>" : "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+i+"); return false;\" style='text-decoration: none;'>"+j+"</a>";
-      blk.assign_vars({
-          CLASS: cls,
-          LINK: link
-        });
-      inner += blk.run();
-    }
+		for ( var k in list )
+		{
+			var i = list[k];
+			if ( i == this.num_pages )
+				break;
+			cls = ( cls == 'row1' ) ? 'row2' : 'row1';
+			var j = i + 1;
+			var url = '#page_'+j;
+			var link = ( i == this_page ) ? "<b>"+j+"</b>" : "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+i+"); return false;\" style='text-decoration: none;'>"+j+"</a>";
+			blk.assign_vars({
+					CLASS: cls,
+					LINK: link
+				});
+			inner += blk.run();
+		}
 
-    if ( this_page < this.num_pages )
-    {
-      // $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-      // $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...'));
-      // $inner .= $blk->run();
+		if ( this_page < this.num_pages )
+		{
+			// $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+			// $blk->assign_vars(array('CLASS'=>$cls,'LINK'=>'...'));
+			// $inner .= $blk->run();
 
-      cls = ( cls == 'row1' ) ? 'row2' : 'row1';
-      var url = '#page_' + String( this.num_pages-1 );
-      var link = ( ( this.num_pages - 1 ) == this_page ) ? "<b>" + $lang.get('paginate_btn_last') + "</b>" : "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+(this.num_pages-1)+"); return false;\" style='text-decoration: none;'>" + $lang.get('paginate_btn_last') + " &raquo;</a>";
-      blk.assign_vars({
-          CLASS: cls,
-          LINK: link
-        });
-      inner += blk.run();
-    }
+			cls = ( cls == 'row1' ) ? 'row2' : 'row1';
+			var url = '#page_' + String( this.num_pages-1 );
+			var link = ( ( this.num_pages - 1 ) == this_page ) ? "<b>" + $lang.get('paginate_btn_last') + "</b>" : "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+(this.num_pages-1)+"); return false;\" style='text-decoration: none;'>" + $lang.get('paginate_btn_last') + " &raquo;</a>";
+			blk.assign_vars({
+					CLASS: cls,
+					LINK: link
+				});
+			inner += blk.run();
+		}
 
-  }
+	}
 
-  if ( this_page < ( this.num_pages - 1 ) )
-  {
-    var url = '#page_' + String(this_page + 2);
-    var link = "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+(this_page+1)+"); return false;\" style='text-decoration: none;'>" + $lang.get('paginate_btn_next') + " &raquo;</a>";
-    cls = ( cls == 'row1' ) ? 'row2' : 'row1';
-    blk.assign_vars({
-          CLASS: cls,
-          LINK: link
-        });
-      inner += blk.run();
-  }
+	if ( this_page < ( this.num_pages - 1 ) )
+	{
+		var url = '#page_' + String(this_page + 2);
+		var link = "<a href=\""+url+"\" onclick=\"jspaginator_goto('"+this.random_id+"', "+(this_page+1)+"); return false;\" style='text-decoration: none;'>" + $lang.get('paginate_btn_next') + " &raquo;</a>";
+		cls = ( cls == 'row1' ) ? 'row2' : 'row1';
+		blk.assign_vars({
+					CLASS: cls,
+					LINK: link
+				});
+			inner += blk.run();
+	}
 
-  inner += '<td class="row2" style="cursor: pointer;" onclick="paginator_goto(this, '+this_page+', '+this.num_pages+', '+this.perpage+', {js: true, random_id: \''+this.random_id+'\'});">&darr;</td>';
+	inner += '<td class="row2" style="cursor: pointer;" onclick="paginator_goto(this, '+this_page+', '+this.num_pages+', '+this.perpage+', {js: true, random_id: \''+this.random_id+'\'});">&darr;</td>';
 
-  var paginator = "\n"+begin+inner+end+"\n";
-  return paginator;
-  
+	var paginator = "\n"+begin+inner+end+"\n";
+	return paginator;
+	
 }
 
 var __paginateLock = false;
 
 window.jspaginator_goto = function(pagin_id, jump_to)
 {
-  if ( __paginateLock )
-    return false;
-  var theobj = pagin_objects[pagin_id];
-  if ( theobj.flip_func )
-  {
-    theobj.flip_func(theobj, jump_to);
-    __paginateLock = false;
-    return true;
-  }
-  var current_div = false;
-  var new_div = false;
-  for ( var i = 0; i < theobj.num_pages; i++ )
-  {
-    var thediv = document.getElementById(pagin_id + '_' + i);
-    if ( !thediv )
-    {
-      // if ( window.console )
-        // window.console.error('jspaginator_goto(): got a bad DOM object in loop');
-      return false;
-    }
-    // window.console.debug("Div "+i+' of '+(theobj.num_pages-1)+': ', thediv);
-    if ( thediv.style.display != 'none' )
-      current_div = thediv;
-    else if ( i == jump_to )
-      new_div = thediv;
-  }
-  
-  if ( !new_div )
-  {
-    // if ( window.console )
-      // window.console.error('jspaginator_goto(): didn\'t get new div');
-    return false;
-  }
-  if ( !current_div )
-  {
-    // if ( window.console )
-      // window.console.error('jspaginator_goto(): didn\'t get current div');
-    return false;
-  }
-  
-  // window.console.debug(current_div);
-  // window.console.debug(new_div);
-  
-  // White-out the old div and fade in the new one
-  
-  if ( IE || is_Safari || aclDisableTransitionFX )
-  {
-    current_div.style.display = 'none';
-    new_div.style.display = 'block';
-  }
-  else
-  {
-    __paginateLock = true;
-    var fade_time = 375;
-    var code = 'var old = \'' + current_div.id + '\';';
-    code    += 'var newer = \'' + new_div.id + '\';';
-    code    += 'document.getElementById(old).style.display = "none";';
-    code    += 'changeOpac(0, newer);';
-    code    += 'document.getElementById(newer).style.display = "block";';
-    code    += 'opacity(newer, 0, 100, '+fade_time+');';
-    code    += '__paginateLock = false;';
-    // if ( window.console )
-      // window.console.debug('metacode for fader: ', code);
-    opacity(current_div.id, 100, 0, fade_time);
-    setTimeout(code, (fade_time + 50));
-  }
-  
-  
-  var pg_control = theobj._build_control(jump_to);
-  var divs = getElementsByClassName(document, 'div', pagin_id + '_control');
-  for ( var i = 0; i < divs.length; i++ )
-  {
-    divs[i].innerHTML = pg_control;
-  }
+	if ( __paginateLock )
+		return false;
+	var theobj = pagin_objects[pagin_id];
+	if ( theobj.flip_func )
+	{
+		theobj.flip_func(theobj, jump_to);
+		__paginateLock = false;
+		return true;
+	}
+	var current_div = false;
+	var new_div = false;
+	for ( var i = 0; i < theobj.num_pages; i++ )
+	{
+		var thediv = document.getElementById(pagin_id + '_' + i);
+		if ( !thediv )
+		{
+			// if ( window.console )
+				// window.console.error('jspaginator_goto(): got a bad DOM object in loop');
+			return false;
+		}
+		// window.console.debug("Div "+i+' of '+(theobj.num_pages-1)+': ', thediv);
+		if ( thediv.style.display != 'none' )
+			current_div = thediv;
+		else if ( i == jump_to )
+			new_div = thediv;
+	}
+	
+	if ( !new_div )
+	{
+		// if ( window.console )
+			// window.console.error('jspaginator_goto(): didn\'t get new div');
+		return false;
+	}
+	if ( !current_div )
+	{
+		// if ( window.console )
+			// window.console.error('jspaginator_goto(): didn\'t get current div');
+		return false;
+	}
+	
+	// window.console.debug(current_div);
+	// window.console.debug(new_div);
+	
+	// White-out the old div and fade in the new one
+	
+	if ( IE || is_Safari || aclDisableTransitionFX )
+	{
+		current_div.style.display = 'none';
+		new_div.style.display = 'block';
+	}
+	else
+	{
+		__paginateLock = true;
+		var fade_time = 375;
+		var code = 'var old = \'' + current_div.id + '\';';
+		code    += 'var newer = \'' + new_div.id + '\';';
+		code    += 'document.getElementById(old).style.display = "none";';
+		code    += 'changeOpac(0, newer);';
+		code    += 'document.getElementById(newer).style.display = "block";';
+		code    += 'opacity(newer, 0, 100, '+fade_time+');';
+		code    += '__paginateLock = false;';
+		// if ( window.console )
+			// window.console.debug('metacode for fader: ', code);
+		opacity(current_div.id, 100, 0, fade_time);
+		setTimeout(code, (fade_time + 50));
+	}
+	
+	
+	var pg_control = theobj._build_control(jump_to);
+	var divs = getElementsByClassName(document, 'div', pagin_id + '_control');
+	for ( var i = 0; i < divs.length; i++ )
+	{
+		divs[i].innerHTML = pg_control;
+	}
 }
 
 window.paginator_goto = function(parentobj, this_page, num_pages, perpage, additive, url_string)
 {
-  load_component('flyin');
-  
-  var height = $dynano(parentobj).Height();
-  var width  = $dynano(parentobj).Width();
-  var left   = $dynano(parentobj).Left();
-  var top    = $dynano(parentobj).Top();
-  var left_pos = left + width ;
-  var top_pos = height + top;
-  var div = document.createElement('div');
-  div.style.position = 'absolute';
-  div.style.top = top_pos + 'px';
-  div.className = 'question-box';
-  div.style.margin = '1px 0 0 2px';
-  var vtmp = 'input_' + Math.floor(Math.random() * 1000000);
-  var regex = new RegExp('\"', 'g');
-  var submit_target = ( typeof(url_string) == 'object' ) ? ( toJSONString(url_string) ).replace(regex, '\'') : 'unescape(\'' + escape(url_string) + '\')';
-  var onclick = 'paginator_submit(this, '+num_pages+', '+perpage+', '+additive+', '+submit_target+'); return false;';
-  div.innerHTML = $lang.get('paginate_lbl_goto_page') + '<br /><input type="text" size="2" style="padding: 1px; font-size: 8pt;" value="'+(parseInt(this_page)+1)+'" id="'+vtmp+'" />&emsp;<a href="#" onclick="'+onclick+'" style="font-size: 14pt; text-decoration: none;">&raquo;</a>&emsp;<a href="#" onclick="var _pn = this.parentNode; setTimeout(function() { _pn.parentNode.removeChild(_pn); }, 2000); fly_out_top(this.parentNode, false, true); return false;" style="font-size: 14pt; text-decoration: none;">&times;</a>';
-  
-  var body = document.getElementsByTagName('body')[0];
-  domObjChangeOpac(0, div);
-  
-  body.appendChild(div);
-  
-  document.getElementById(vtmp).onkeypress = function(e)
-    {
-      if ( e.keyCode == 13 )
-        this.nextSibling.nextSibling.onclick();
-    };
-  document.getElementById(vtmp).focus();
-  
-  // fade the div
-  /*
-  if(!div.id) div.id = 'autofade_'+Math.floor(Math.random() * 100000);
-  var from = '#33FF33';
-  Fat.fade_element(div.id,30,2000,from,Fat.get_bgcolor(div.id));
-  */
-  
-  fly_in_bottom(div, false, true);
-  
-  var divh = $dynano(div).Width();
-  left_pos = left_pos - divh;
-  div.style.left = left_pos + 'px';
+	load_component('flyin');
+	
+	var height = $dynano(parentobj).Height();
+	var width  = $dynano(parentobj).Width();
+	var left   = $dynano(parentobj).Left();
+	var top    = $dynano(parentobj).Top();
+	var left_pos = left + width ;
+	var top_pos = height + top;
+	var div = document.createElement('div');
+	div.style.position = 'absolute';
+	div.style.top = top_pos + 'px';
+	div.className = 'question-box';
+	div.style.margin = '1px 0 0 2px';
+	var vtmp = 'input_' + Math.floor(Math.random() * 1000000);
+	var regex = new RegExp('\"', 'g');
+	var submit_target = ( typeof(url_string) == 'object' ) ? ( toJSONString(url_string) ).replace(regex, '\'') : 'unescape(\'' + escape(url_string) + '\')';
+	var onclick = 'paginator_submit(this, '+num_pages+', '+perpage+', '+additive+', '+submit_target+'); return false;';
+	div.innerHTML = $lang.get('paginate_lbl_goto_page') + '<br /><input type="text" size="2" style="padding: 1px; font-size: 8pt;" value="'+(parseInt(this_page)+1)+'" id="'+vtmp+'" />&emsp;<a href="#" onclick="'+onclick+'" style="font-size: 14pt; text-decoration: none;">&raquo;</a>&emsp;<a href="#" onclick="var _pn = this.parentNode; setTimeout(function() { _pn.parentNode.removeChild(_pn); }, 2000); fly_out_top(this.parentNode, false, true); return false;" style="font-size: 14pt; text-decoration: none;">&times;</a>';
+	
+	var body = document.getElementsByTagName('body')[0];
+	domObjChangeOpac(0, div);
+	
+	body.appendChild(div);
+	
+	document.getElementById(vtmp).onkeypress = function(e)
+		{
+			if ( e.keyCode == 13 )
+				this.nextSibling.nextSibling.onclick();
+		};
+	document.getElementById(vtmp).focus();
+	
+	// fade the div
+	/*
+	if(!div.id) div.id = 'autofade_'+Math.floor(Math.random() * 100000);
+	var from = '#33FF33';
+	Fat.fade_element(div.id,30,2000,from,Fat.get_bgcolor(div.id));
+	*/
+	
+	fly_in_bottom(div, false, true);
+	
+	var divh = $dynano(div).Width();
+	left_pos = left_pos - divh;
+	div.style.left = left_pos + 'px';
 }
 
 window.paginator_submit = function(obj, max, perpage, additive, formatstring)
 {
-  var userinput = obj.previousSibling.previousSibling.value;
-  userinput = parseInt(userinput);
-  var offset = (( userinput - 1 ) * perpage) + additive;
-  if ( userinput > max || isNaN(userinput) || userinput < 1 )
-  {
-    load_component(['messagebox', 'fadefilter', 'flyin']);
-    new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('paginate_err_bad_page_title'), $lang.get('paginate_err_bad_page_body', { max: max }));
-    return false;
-  }
-  if ( typeof(formatstring) == 'object' )
-  {
-    fly_out_top(obj.parentNode, false, true);
-    jspaginator_goto(formatstring.random_id, ( offset / perpage ));
-  }
-  else
-  {
-    var url = sprintf(formatstring, String(offset));
-    fly_out_top(obj.parentNode, false, true);
-    window.location = url;
-  }
+	var userinput = obj.previousSibling.previousSibling.value;
+	userinput = parseInt(userinput);
+	var offset = (( userinput - 1 ) * perpage) + additive;
+	if ( userinput > max || isNaN(userinput) || userinput < 1 )
+	{
+		load_component(['messagebox', 'fadefilter', 'flyin']);
+		new MessageBox(MB_OK|MB_ICONSTOP, $lang.get('paginate_err_bad_page_title'), $lang.get('paginate_err_bad_page_body', { max: max }));
+		return false;
+	}
+	if ( typeof(formatstring) == 'object' )
+	{
+		fly_out_top(obj.parentNode, false, true);
+		jspaginator_goto(formatstring.random_id, ( offset / perpage ));
+	}
+	else
+	{
+		var url = sprintf(formatstring, String(offset));
+		fly_out_top(obj.parentNode, false, true);
+		window.location = url;
+	}
 }
 
 // This code is in the public domain. Feel free to link back to http://jan.moesen.nu/
 function sprintf()
 {
-  if (!arguments || arguments.length < 1 || !RegExp)
-  {
-    return;
-  }
-  var str = arguments[0];
-  var re = /([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/;
-  var a = b = [], numSubstitutions = 0, numMatches = 0;
-  while (a = re.exec(str))
-  {
-    var leftpart = a[1], pPad = a[2], pJustify = a[3], pMinLength = a[4];
-    var pPrecision = a[5], pType = a[6], rightPart = a[7];
-    
-    //alert(a + '\n' + [a[0], leftpart, pPad, pJustify, pMinLength, pPrecision);
+	if (!arguments || arguments.length < 1 || !RegExp)
+	{
+		return;
+	}
+	var str = arguments[0];
+	var re = /([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X)(.*)/;
+	var a = b = [], numSubstitutions = 0, numMatches = 0;
+	while (a = re.exec(str))
+	{
+		var leftpart = a[1], pPad = a[2], pJustify = a[3], pMinLength = a[4];
+		var pPrecision = a[5], pType = a[6], rightPart = a[7];
+		
+		//alert(a + '\n' + [a[0], leftpart, pPad, pJustify, pMinLength, pPrecision);
 
-    numMatches++;
-    if (pType == '%')
-    {
-      subst = '%';
-    }
-    else
-    {
-      numSubstitutions++;
-      if (numSubstitutions >= arguments.length)
-      {
-        alert('Error! Not enough function arguments (' + (arguments.length - 1) + ', excluding the string)\nfor the number of substitution parameters in string (' + numSubstitutions + ' so far).');
-      }
-      var param = arguments[numSubstitutions];
-      var pad = '';
-             if (pPad && pPad.substr(0,1) == "'") pad = leftpart.substr(1,1);
-        else if (pPad) pad = pPad;
-      var justifyRight = true;
-             if (pJustify && pJustify === "-") justifyRight = false;
-      var minLength = -1;
-             if (pMinLength) minLength = parseInt(pMinLength);
-      var precision = -1;
-             if (pPrecision && pType == 'f') precision = parseInt(pPrecision.substring(1));
-      var subst = param;
-             if (pType == 'b') subst = parseInt(param).toString(2);
-        else if (pType == 'c') subst = String.fromCharCode(parseInt(param));
-        else if (pType == 'd') subst = parseInt(param) ? parseInt(param) : 0;
-        else if (pType == 'u') subst = Math.abs(param);
-        else if (pType == 'f') subst = (precision > -1) ? Math.round(parseFloat(param) * Math.pow(10, precision)) / Math.pow(10, precision): parseFloat(param);
-        else if (pType == 'o') subst = parseInt(param).toString(8);
-        else if (pType == 's') subst = param;
-        else if (pType == 'x') subst = ('' + parseInt(param).toString(16)).toLowerCase();
-        else if (pType == 'X') subst = ('' + parseInt(param).toString(16)).toUpperCase();
-    }
-    str = leftpart + subst + rightPart;
-  }
-  return str;
+		numMatches++;
+		if (pType == '%')
+		{
+			subst = '%';
+		}
+		else
+		{
+			numSubstitutions++;
+			if (numSubstitutions >= arguments.length)
+			{
+				alert('Error! Not enough function arguments (' + (arguments.length - 1) + ', excluding the string)\nfor the number of substitution parameters in string (' + numSubstitutions + ' so far).');
+			}
+			var param = arguments[numSubstitutions];
+			var pad = '';
+ 						if (pPad && pPad.substr(0,1) == "'") pad = leftpart.substr(1,1);
+				else if (pPad) pad = pPad;
+			var justifyRight = true;
+ 						if (pJustify && pJustify === "-") justifyRight = false;
+			var minLength = -1;
+ 						if (pMinLength) minLength = parseInt(pMinLength);
+			var precision = -1;
+ 						if (pPrecision && pType == 'f') precision = parseInt(pPrecision.substring(1));
+			var subst = param;
+ 						if (pType == 'b') subst = parseInt(param).toString(2);
+				else if (pType == 'c') subst = String.fromCharCode(parseInt(param));
+				else if (pType == 'd') subst = parseInt(param) ? parseInt(param) : 0;
+				else if (pType == 'u') subst = Math.abs(param);
+				else if (pType == 'f') subst = (precision > -1) ? Math.round(parseFloat(param) * Math.pow(10, precision)) / Math.pow(10, precision): parseFloat(param);
+				else if (pType == 'o') subst = parseInt(param).toString(8);
+				else if (pType == 's') subst = param;
+				else if (pType == 'x') subst = ('' + parseInt(param).toString(16)).toLowerCase();
+				else if (pType == 'X') subst = ('' + parseInt(param).toString(16)).toUpperCase();
+		}
+		str = leftpart + subst + rightPart;
+	}
+	return str;
 }
--- a/includes/clientside/static/pwstrength.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/pwstrength.js	Sun Mar 28 23:10:46 2010 -0400
@@ -12,281 +12,281 @@
 
 function password_score_len(password)
 {
-  if ( typeof(password) != "string" )
-  {
-    return -10;
-  }
-  var len = password.length;
-  var score = len - 7;
-  return score;
+	if ( typeof(password) != "string" )
+	{
+		return -10;
+	}
+	var len = password.length;
+	var score = len - 7;
+	return score;
 }
 
 function password_score(password)
 {
-  if ( typeof(password) != "string" )
-  {
-    return -10;
-  }
-  var score = 0;
-  var debug = [];
-  // length check
-  var lenscore = password_score_len(password);
-  
-  debug.push(''+lenscore+' points for length');
-  
-  score += lenscore;
-    
-  var has_upper_lower = false;
-  var has_symbols     = false;
-  var has_numbers     = false;
-  
-  // contains uppercase and lowercase
-  if ( password.match(/[A-z]+/) && password.toLowerCase() != password )
-  {
-    score += 1;
-    has_upper_lower = true;
-    debug.push('1 point for having uppercase and lowercase');
-  }
-  
-  // contains symbols
-  if ( password.match(/[^A-z0-9]+/) )
-  {
-    score += 1;
-    has_symbols = true;
-    debug.push('1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)');
-  }
-  
-  // contains numbers
-  if ( password.match(/[0-9]+/) )
-  {
-    score += 1;
-    has_numbers = true;
-    debug.push('1 point for having numbers');
-  }
-  
-  if ( has_upper_lower && has_symbols && has_numbers && password.length >= 9 )
-  {
-    // if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points
-    score += 4;
-    debug.push('4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters');
-  }
-  else if ( has_upper_lower && has_symbols && has_numbers && password.length >= 6 )
-  {
-    // still give some points for passing complexity check
-    score += 2;
-    debug.push('2 points for having uppercase and lowercase, numbers, and nonalphanumeric');
-  }
-  else if(( ( has_upper_lower && has_symbols ) ||
-            ( has_upper_lower && has_numbers ) ||
-            ( has_symbols && has_numbers ) ) && password.length >= 6 )
-  {
-    // if 2 of the three main complexity checks passed, add a point
-    score += 1;
-    debug.push('1 point for having 2 of 3 complexity checks');
-  }
-  else if ( ( !has_upper_lower && !has_numbers && has_symbols ) ||
-            ( !has_upper_lower && !has_symbols && has_numbers ) ||
-            ( !has_numbers && !has_symbols && has_upper_lower ) )
-  {
-    score += -2;
-    debug.push('-2 points for only meeting 1 complexity check');
-  }
-  else if ( password.match(/^[0-9]*?([a-z]+)[0-9]?$/) )
-  {
-    // password is something like magnum1 which will be cracked in seconds
-    score += -4;
-    debug.push('-4 points for being of the form [number][word][number], which is easily cracked');
-  }
-  else if ( !has_upper_lower && !has_numbers && !has_symbols )
-  {
-    // this is if somehow the user inputs a password that doesn't match the rule above, but still doesn't contain upper and lowercase, numbers, or symbols
-    debug.push('-3 points for not meeting any complexity checks');
-    score += -3;
-  }
-  
-  //
-  // Repetition
-  // Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points
-  // None of the positive ones kick in unless the length is at least 8
-  //
-  
-  if ( password.match(/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/) )
-  {
-    debug.push('-2 points for having more than 4 letters of the same case in a row');
-    score += -2;
-  }
-  else if ( password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) )
-  {
-    debug.push('-1 points for having more than 3 letters of the same case in a row');
-    score += -1;
-  }
-  else if ( password.match(/[A-z]/) && !password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) && password.length >= 8 )
-  {
-    debug.push('1 point for never having more than 2 letters of the same case in a row');
-    score += 1;
-  }
-  
-  if ( password.match(/[0-9][0-9][0-9][0-9]/) )
-  {
-    debug.push('-2 points for having 4 or more numbers in a row');
-    score += -2;
-  }
-  else if ( password.match(/[0-9][0-9][0-9]/) )
-  {
-    debug.push('-1 points for having 3 or more numbers in a row');
-    score += -1;
-  }
-  else if ( has_numbers && !password.match(/[0-9][0-9][0-9]/) && password.length >= 8 )
-  {
-    debug.push('1 point for never more than 2 numbers in a row');
-    score += -1;
-  }
-  
-  // make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row
-  var prev_char = '';
-  var warn = false;
-  var loss = 0;
-  for ( var i = 0; i < password.length; i++ )
-  {
-    var chr = password.substr(i, 1);
-    if ( chr == prev_char && warn )
-    {
-      loss += -1;
-    }
-    else if ( chr == prev_char && !warn )
-    {
-      warn = true;
-    }
-    else if ( chr != prev_char && warn )
-    {
-      warn = false;
-    }
-    prev_char = chr;
-  }
-  if ( loss < 0 )
-  {
-    debug.push(''+loss+' points for immediate character repetition');
-    score += loss;
-    // this can bring the score below -10 sometimes
-    if ( score < -10 )
-    {
-      debug.push('Score set to -10 because it went below that floor');
-      score = -10;
-    }
-  }
-  
-  var debug_txt = "<b>How this score was calculated</b>\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n\n";
-  for ( var i = 0; i < debug.length; i++ )
-  {
-    debug_txt += debug[i] + "\n";
-  }
-  
-  // For users that really want to know why their password sucks.
-  // Not localized because the feature is really only used for debugging the algorithm.
-  if ( document.getElementById('passdebug') )
-    document.getElementById('passdebug').innerHTML = debug_txt;
-  
-  return score;
+	if ( typeof(password) != "string" )
+	{
+		return -10;
+	}
+	var score = 0;
+	var debug = [];
+	// length check
+	var lenscore = password_score_len(password);
+	
+	debug.push(''+lenscore+' points for length');
+	
+	score += lenscore;
+		
+	var has_upper_lower = false;
+	var has_symbols     = false;
+	var has_numbers     = false;
+	
+	// contains uppercase and lowercase
+	if ( password.match(/[A-z]+/) && password.toLowerCase() != password )
+	{
+		score += 1;
+		has_upper_lower = true;
+		debug.push('1 point for having uppercase and lowercase');
+	}
+	
+	// contains symbols
+	if ( password.match(/[^A-z0-9]+/) )
+	{
+		score += 1;
+		has_symbols = true;
+		debug.push('1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)');
+	}
+	
+	// contains numbers
+	if ( password.match(/[0-9]+/) )
+	{
+		score += 1;
+		has_numbers = true;
+		debug.push('1 point for having numbers');
+	}
+	
+	if ( has_upper_lower && has_symbols && has_numbers && password.length >= 9 )
+	{
+		// if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points
+		score += 4;
+		debug.push('4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters');
+	}
+	else if ( has_upper_lower && has_symbols && has_numbers && password.length >= 6 )
+	{
+		// still give some points for passing complexity check
+		score += 2;
+		debug.push('2 points for having uppercase and lowercase, numbers, and nonalphanumeric');
+	}
+	else if(( ( has_upper_lower && has_symbols ) ||
+						( has_upper_lower && has_numbers ) ||
+						( has_symbols && has_numbers ) ) && password.length >= 6 )
+	{
+		// if 2 of the three main complexity checks passed, add a point
+		score += 1;
+		debug.push('1 point for having 2 of 3 complexity checks');
+	}
+	else if ( ( !has_upper_lower && !has_numbers && has_symbols ) ||
+						( !has_upper_lower && !has_symbols && has_numbers ) ||
+						( !has_numbers && !has_symbols && has_upper_lower ) )
+	{
+		score += -2;
+		debug.push('-2 points for only meeting 1 complexity check');
+	}
+	else if ( password.match(/^[0-9]*?([a-z]+)[0-9]?$/) )
+	{
+		// password is something like magnum1 which will be cracked in seconds
+		score += -4;
+		debug.push('-4 points for being of the form [number][word][number], which is easily cracked');
+	}
+	else if ( !has_upper_lower && !has_numbers && !has_symbols )
+	{
+		// this is if somehow the user inputs a password that doesn't match the rule above, but still doesn't contain upper and lowercase, numbers, or symbols
+		debug.push('-3 points for not meeting any complexity checks');
+		score += -3;
+	}
+	
+	//
+	// Repetition
+	// Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points
+	// None of the positive ones kick in unless the length is at least 8
+	//
+	
+	if ( password.match(/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/) )
+	{
+		debug.push('-2 points for having more than 4 letters of the same case in a row');
+		score += -2;
+	}
+	else if ( password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) )
+	{
+		debug.push('-1 points for having more than 3 letters of the same case in a row');
+		score += -1;
+	}
+	else if ( password.match(/[A-z]/) && !password.match(/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/) && password.length >= 8 )
+	{
+		debug.push('1 point for never having more than 2 letters of the same case in a row');
+		score += 1;
+	}
+	
+	if ( password.match(/[0-9][0-9][0-9][0-9]/) )
+	{
+		debug.push('-2 points for having 4 or more numbers in a row');
+		score += -2;
+	}
+	else if ( password.match(/[0-9][0-9][0-9]/) )
+	{
+		debug.push('-1 points for having 3 or more numbers in a row');
+		score += -1;
+	}
+	else if ( has_numbers && !password.match(/[0-9][0-9][0-9]/) && password.length >= 8 )
+	{
+		debug.push('1 point for never more than 2 numbers in a row');
+		score += -1;
+	}
+	
+	// make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row
+	var prev_char = '';
+	var warn = false;
+	var loss = 0;
+	for ( var i = 0; i < password.length; i++ )
+	{
+		var chr = password.substr(i, 1);
+		if ( chr == prev_char && warn )
+		{
+			loss += -1;
+		}
+		else if ( chr == prev_char && !warn )
+		{
+			warn = true;
+		}
+		else if ( chr != prev_char && warn )
+		{
+			warn = false;
+		}
+		prev_char = chr;
+	}
+	if ( loss < 0 )
+	{
+		debug.push(''+loss+' points for immediate character repetition');
+		score += loss;
+		// this can bring the score below -10 sometimes
+		if ( score < -10 )
+		{
+			debug.push('Score set to -10 because it went below that floor');
+			score = -10;
+		}
+	}
+	
+	var debug_txt = "<b>How this score was calculated</b>\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n\n";
+	for ( var i = 0; i < debug.length; i++ )
+	{
+		debug_txt += debug[i] + "\n";
+	}
+	
+	// For users that really want to know why their password sucks.
+	// Not localized because the feature is really only used for debugging the algorithm.
+	if ( document.getElementById('passdebug') )
+		document.getElementById('passdebug').innerHTML = debug_txt;
+	
+	return score;
 }
 
 function password_score_draw(score)
 {
-  if ( !$lang )
-  {
-    // $lang isn't initted yet, this happens sometimes on the usercp/emailpassword form.
-    // Try to init it if we have ENANO_LANG_ID and enano_lang; if not, report an error.
-    load_component('l10n');
-    if ( typeof(enano_lang) == 'object' && typeof(ENANO_LANG_ID) == 'number' )
-    {
-      language_onload();
-    }
-    else
-    {      
-      return {
-        'color' : '#000000',
-        'fgcolor' : '#666666',
-        'str' : 'Language init failed'
-      };
-    }
-  }
-  // some colors are from the Gmail sign-up form
-  if ( score >= 10 )
-  {
-    var color = '#010101';
-    var fgcolor = '#666666';
-    var str = $lang.get('usercp_pwstrength_score_verystrong', { score: score });
-  }
-  else if ( score > 3 )
-  {
-    var color = '#008000';
-    var fgcolor = '#004000';
-    var str = $lang.get('usercp_pwstrength_score_strong', { score: score });
-  }
-  else if ( score >= 1 )
-  {
-    var color = '#6699cc';
-    var fgcolor = '#4477aa';
-    var str = $lang.get('usercp_pwstrength_score_good', { score: score });
-  }
-  else if ( score >= -3 )
-  {
-    var color = '#f5ac00';
-    var fgcolor = '#ffcc33';
-    var str = $lang.get('usercp_pwstrength_score_fair', { score: score });
-  }
-  else
-  {
-    var color = '#aa0033';
-    var fgcolor = '#FF6060';
-    var str = $lang.get('usercp_pwstrength_score_weak', { score: score });
-  }
-  var ret = {
-    color: color,
-    fgcolor: fgcolor,
-    str: str
-  };
-  return ret;
+	if ( !$lang )
+	{
+		// $lang isn't initted yet, this happens sometimes on the usercp/emailpassword form.
+		// Try to init it if we have ENANO_LANG_ID and enano_lang; if not, report an error.
+		load_component('l10n');
+		if ( typeof(enano_lang) == 'object' && typeof(ENANO_LANG_ID) == 'number' )
+		{
+			language_onload();
+		}
+		else
+		{      
+			return {
+				'color' : '#000000',
+				'fgcolor' : '#666666',
+				'str' : 'Language init failed'
+			};
+		}
+	}
+	// some colors are from the Gmail sign-up form
+	if ( score >= 10 )
+	{
+		var color = '#010101';
+		var fgcolor = '#666666';
+		var str = $lang.get('usercp_pwstrength_score_verystrong', { score: score });
+	}
+	else if ( score > 3 )
+	{
+		var color = '#008000';
+		var fgcolor = '#004000';
+		var str = $lang.get('usercp_pwstrength_score_strong', { score: score });
+	}
+	else if ( score >= 1 )
+	{
+		var color = '#6699cc';
+		var fgcolor = '#4477aa';
+		var str = $lang.get('usercp_pwstrength_score_good', { score: score });
+	}
+	else if ( score >= -3 )
+	{
+		var color = '#f5ac00';
+		var fgcolor = '#ffcc33';
+		var str = $lang.get('usercp_pwstrength_score_fair', { score: score });
+	}
+	else
+	{
+		var color = '#aa0033';
+		var fgcolor = '#FF6060';
+		var str = $lang.get('usercp_pwstrength_score_weak', { score: score });
+	}
+	var ret = {
+		color: color,
+		fgcolor: fgcolor,
+		str: str
+	};
+	return ret;
 }
 
 function password_score_field(field)
 {
-  var indicator = false;
-  if ( field.nextSibling )
-  {
-    if ( field.nextSibling.className == 'password-checker' )
-    {
-      indicator = field.nextSibling;
-    }
-  }
-  if ( !indicator )
-  {
-    var indicator = document.createElement('span');
-    indicator.className = 'password-checker';
-    if ( field.nextSibling )
-    {
-      field.parentNode.insertBefore(indicator, field.nextSibling);
-    }
-    else
-    {
-      field.parentNode.appendChild(indicator);
-    }
-  }
-  var score = password_score(field.value);
-  var data = password_score_draw(score);
-  indicator.style.color = data.color;
-  indicator.style.fontWeight = 'bold';
-  indicator.innerHTML = ' ' + data.str;
-  
-  if ( document.getElementById('pwmeter') )
-  {
-    var div = document.getElementById('pwmeter');
-    div.style.width = '250px';
-    score += 10;
-    if ( score > 25 )
-      score = 25;
-    div.style.backgroundColor = data.color;
-    var width = Math.round( score * (250 / 25) );
-    div.innerHTML = '<div style="width: '+width+'px; background-color: '+data.fgcolor+'; height: 8px;"></div>';
-  }
+	var indicator = false;
+	if ( field.nextSibling )
+	{
+		if ( field.nextSibling.className == 'password-checker' )
+		{
+			indicator = field.nextSibling;
+		}
+	}
+	if ( !indicator )
+	{
+		var indicator = document.createElement('span');
+		indicator.className = 'password-checker';
+		if ( field.nextSibling )
+		{
+			field.parentNode.insertBefore(indicator, field.nextSibling);
+		}
+		else
+		{
+			field.parentNode.appendChild(indicator);
+		}
+	}
+	var score = password_score(field.value);
+	var data = password_score_draw(score);
+	indicator.style.color = data.color;
+	indicator.style.fontWeight = 'bold';
+	indicator.innerHTML = ' ' + data.str;
+	
+	if ( document.getElementById('pwmeter') )
+	{
+		var div = document.getElementById('pwmeter');
+		div.style.width = '250px';
+		score += 10;
+		if ( score > 25 )
+			score = 25;
+		div.style.backgroundColor = data.color;
+		var width = Math.round( score * (250 / 25) );
+		div.innerHTML = '<div style="width: '+width+'px; background-color: '+data.fgcolor+'; height: 8px;"></div>';
+	}
 }
 
--- a/includes/clientside/static/rank-manager.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/rank-manager.js	Sun Mar 28 23:10:46 2010 -0400
@@ -4,477 +4,477 @@
 
 var RankEditorControl = function(rankdata)
 {
-  this.rankdata = ( typeof(rankdata) == 'object' ) ? rankdata : {};
-  if ( !this.rankdata.rank_style )
-  {
-    this.rankdata.rank_style = '';
-  }
-  
-  // have the browser parse CSS for us and use an anchor to be as close
-  // as possible in calculating CSS
-  
-  // this is kind of a hack as it relies on setAttribute/getAttribute in
-  // order to obtain stringified versions of CSS data
-  var cssobj = document.createElement('a');
-  cssobj.setAttribute('style', this.rankdata.rank_style);
-  
-  this.style_sim_obj = cssobj;
-  
-  // figure out if we're editing or creating
-  this.editing = ( typeof(this.rankdata.rank_id) == 'number' );
-  
-  this.render = function()
-  {
-    var editor = document.createElement('div');
-    editor.className = 'tblholder';
-    // stash this editor instance in the parent div for later function calls
-    editor.editor = this;
-    this.wrapperdiv = editor;
-    editor.style.width = '100%';
-    
-    // tables suck.
-    var table = document.createElement('table');
-    table.setAttribute('cellspacing', '1');
-    table.setAttribute('cellpadding', '4');
-    table.setAttribute('width', '100%');
-    
-    // heading: "Edit rank: foo" or "Create a new rank"
-    var tr_head = document.createElement('tr');
-    var th_head = document.createElement('th');
-    th_head.setAttribute('colspan', '2');
-    if ( this.editing )
-    {
-      var th_head_string = 'acpur_th_edit_rank';
-      var th_head_data = { rank_title: $lang.get(this.rankdata.rank_title) };
-    }
-    else
-    {
-      var th_head_string = 'acpur_th_create_rank';
-      var th_head_data = { };
-    }
-    th_head.appendChild(document.createTextNode($lang.get(th_head_string, th_head_data)));
-    tr_head.appendChild(th_head);
-    this.th_head = th_head;
-    table.appendChild(tr_head);
-    
-    // row: rank title
-    var tr_title = document.createElement('tr');
-    var td_title_l = document.createElement('td');
-    var td_title_f = document.createElement('td');
-    
-    td_title_l.className = td_title_f.className = 'row1';
-    
-    td_title_l.appendChild(document.createTextNode($lang.get('acpur_field_rank_title')));
-    
-    // field: rank title
-    var f_rank_title = document.createElement('input');
-    f_rank_title.type = 'text';
-    f_rank_title.size = '30';
-    f_rank_title.value = ( this.editing ) ? this.rankdata.rank_title : '';
-    f_rank_title.editor = this;
-    f_rank_title.onkeyup = function()
-    {
-      this.editor.renderPreview();
-    }
-    this.f_rank_title = f_rank_title;
-    td_title_f.appendChild(f_rank_title);
-    
-    tr_title.appendChild(td_title_l);
-    tr_title.appendChild(td_title_f);
-    table.appendChild(tr_title);
-    
-    // row: basic style options
-    var tr_basic = document.createElement('tr');
-    var td_basic_l = document.createElement('td');
-    var td_basic_f = document.createElement('td');
-    
-    td_basic_l.className = td_basic_f.className = 'row2';
-    
-    td_basic_l.appendChild(document.createTextNode($lang.get('acpur_field_style_basic')));
-    
-    // fieldset: basic style options
-    // field: bold
-    var l_basic_bold = document.createElement('label');
-    var f_basic_bold = document.createElement('input');
-    f_basic_bold.type = 'checkbox';
-    f_basic_bold.checked = ( this.style_sim_obj.style.fontWeight == 'bold' ) ? true : false;
-    f_basic_bold.editor = this;
-    f_basic_bold.onclick = function()
-    {
-      this.editor.style_sim_obj.style.fontWeight = ( this.checked ) ? 'bold' : null;
-      this.editor.renderPreview();
-    }
-    l_basic_bold.style.fontWeight = 'bold';
-    l_basic_bold.appendChild(f_basic_bold);
-    l_basic_bold.appendChild(document.createTextNode(' '));
-    l_basic_bold.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_bold')));
-    
-    // field: italic
-    var l_basic_italic = document.createElement('label');
-    var f_basic_italic = document.createElement('input');
-    f_basic_italic.type = 'checkbox';
-    f_basic_italic.checked = ( this.style_sim_obj.style.fontStyle == 'italic' ) ? true : false;
-    f_basic_italic.editor = this;
-    f_basic_italic.onclick = function()
-    {
-      this.editor.style_sim_obj.style.fontStyle = ( this.checked ) ? 'italic' : null;
-      this.editor.renderPreview();
-    }
-    l_basic_italic.style.fontStyle = 'italic';
-    l_basic_italic.appendChild(f_basic_italic);
-    l_basic_italic.appendChild(document.createTextNode(' '));
-    l_basic_italic.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_italic')));
-    
-    // field: underline
-    var l_basic_underline = document.createElement('label');
-    var f_basic_underline = document.createElement('input');
-    f_basic_underline.type = 'checkbox';
-    f_basic_underline.checked = ( this.style_sim_obj.style.textDecoration == 'underline' ) ? true : false;
-    f_basic_underline.editor = this;
-    f_basic_underline.onclick = function()
-    {
-      this.editor.style_sim_obj.style.textDecoration = ( this.checked ) ? 'underline' : null;
-      this.editor.renderPreview();
-    }
-    l_basic_underline.style.textDecoration = 'underline';
-    l_basic_underline.appendChild(f_basic_underline);
-    l_basic_underline.appendChild(document.createTextNode(' '));
-    l_basic_underline.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_underline')));
-    
-    // finish up formatting row#1
-    td_basic_f.appendChild(l_basic_bold);
-    td_basic_f.appendChild(document.createTextNode(' '));
-    td_basic_f.appendChild(l_basic_italic);
-    td_basic_f.appendChild(document.createTextNode(' '));
-    td_basic_f.appendChild(l_basic_underline);
-    
-    tr_basic.appendChild(td_basic_l);
-    tr_basic.appendChild(td_basic_f);
-    table.appendChild(tr_basic);
-    
-    // row: rank color
-    var tr_color = document.createElement('tr');
-    var td_color_l = document.createElement('td');
-    var td_color_f = document.createElement('td');
-    
-    td_color_l.className = td_color_f.className = 'row1';
-    
-    td_color_l.appendChild(document.createTextNode($lang.get('acpur_field_style_color')));
-    
-    // field: rank color
-    var f_rank_color = document.createElement('input');
-    f_rank_color.type = 'text';
-    f_rank_color.size = '7';
-    f_rank_color.value = ( this.editing ) ? this.rgb2hex(this.style_sim_obj.style.color) : '';
-    f_rank_color.style.backgroundColor = this.style_sim_obj.style.color;
-    f_rank_color.editor = this;
-    this.f_rank_color = f_rank_color;
-    f_rank_color.onkeyup = function(e)
-    {
-      if ( !e.keyCode )
-        e = window.event;
-      if ( !e )
-        return false;
-      var chr = (String.fromCharCode(e.keyCode)).toLowerCase();
-      this.value = this.value.replace(/[^a-fA-F0-9]/g, '');
-      if ( this.value.length > 6 )
-      {
-        this.value = this.value.substr(0, 6);
-      }
-      if ( this.value.length == 6 || this.value.length == 3 )
-      {
-        this.style.backgroundColor = '#' + this.value;
-        this.editor.style_sim_obj.style.color = '#' + this.value;
-        this.style.color = '#' + this.editor.determineLightness(this.value);
-        this.editor.renderPreview();
-      }
-      else if ( this.value.length == 0 )
-      {
-        this.style.backgroundColor = null;
-        this.editor.style_sim_obj.style.color = null;
-        this.editor.renderPreview();
-      }
-    }
-    td_color_f.appendChild(f_rank_color);
-    
-    tr_color.appendChild(td_color_l);
-    tr_color.appendChild(td_color_f);
-    table.appendChild(tr_color);
-    
-    // field: additional CSS
-    var tr_css = document.createElement('tr');
-    
-    var td_css_l = document.createElement('td');
-    td_css_l.className = 'row2';
-    td_css_l.appendChild(document.createTextNode($lang.get('acpur_field_style_css')));
-    tr_css.appendChild(td_css_l);
-    
-    var td_css_f = document.createElement('td');
-    td_css_f.className = 'row2';
-    var f_css = document.createElement('input');
-    f_css.type = 'text';
-    f_css.value = this.stripBasicCSSAttributes(this.rankdata.rank_style);
-    f_css.style.width = '98%';
-    f_css.editor = this;
-    f_css.onkeyup = function()
-    {
-      if ( !(trim(this.value)).match(/^((([a-z-]+):(.+?);)+)?$/) )
-        return;
-      var newcss = this.editor.stripExtendedCSSAttributes(String(this.editor.style_sim_obj.getAttribute('style'))) + ' ' + this.value;
-      this.editor.preview_div.setAttribute('style', 'font-size: x-large; ' + newcss);
-      this.editor.style_sim_obj.setAttribute('style', newcss);
-    }
-    this.f_css = f_css;
-    td_css_f.appendChild(f_css);
-    tr_css.appendChild(td_css_f);
-    table.appendChild(tr_css);
-    
-    // "field": preview
-    var tr_preview = document.createElement('tr');
-    var td_preview_l = document.createElement('td');
-    td_preview_l.className = 'row1';
-    td_preview_l.appendChild(document.createTextNode($lang.get('acpur_field_preview')));
-    tr_preview.appendChild(td_preview_l);
-    
-    var td_preview_f = document.createElement('td');
-    td_preview_f.className = 'row1';
-    var div_preview = document.createElement('a');
-    this.preview_div = div_preview;
-    div_preview.style.fontSize = 'x-large';
-    div_preview.appendChild(document.createTextNode(''));
-    div_preview.firstChild.nodeValue = ( this.editing ) ? this.rankdata.rank_title : '';
-    td_preview_f.appendChild(div_preview);
-    tr_preview.appendChild(td_preview_f);
-    
-    table.appendChild(tr_preview);
-    
-    // submit button
-    var tr_submit = document.createElement('tr');
-    var th_submit = document.createElement('th');
-    th_submit.className = 'subhead';
-    th_submit.setAttribute('colspan', '2');
-    var btn_submit = document.createElement('input');
-    btn_submit.type = 'submit';
-    btn_submit.value = ( this.editing ) ? $lang.get('acpur_btn_save') : $lang.get('acpur_btn_create_submit');
-    btn_submit.editor = this;
-    btn_submit.style.fontWeight = 'bold';
-    btn_submit.onclick = function(e)
-    {
-      this.editor.submitEvent(e);
-    }
-    this.btn_submit = btn_submit;
-    th_submit.appendChild(btn_submit);
-    
-    // delete button
-    if ( this.editing )
-    {
-      var btn_delete = document.createElement('input');
-      btn_delete.type = 'button';
-      btn_delete.value = $lang.get('acpur_btn_delete');
-      btn_delete.editor = this;
-      btn_delete.onclick = function(e)
-      {
-        this.editor.deleteEvent(e);
-      }
-      th_submit.appendChild(document.createTextNode(' '));
-      th_submit.appendChild(btn_delete);
-    }
-    
-    tr_submit.appendChild(th_submit);
-    
-    table.appendChild(tr_submit);
-    
-    // render preview
-    this.renderPreview();
-    
-    // finalize the editor table
-    editor.appendChild(table);
-    
-    // stash rendered editor
-    this.editordiv = editor;
-    
-    // send output
-    return editor;
-  }
-  
-  /**
-   * Takes the existing editor div and transforms the necessary elements so that it goes from "create" mode to "edit" mode
-   * @param object Edit data - same format as the rankdata parameter to the constructor, but we should only need rank_id
-   */
-  
-  this.transformToEditor = function(rankdata)
-  {
-    // we need a rank ID
-    if ( typeof(rankdata.rank_id) != 'number' )
-      return false;
-    
-    if ( this.editing )
-      return false;
-    
-    this.editing = true;
-    
-    this.rankdata = rankdata;
-    this.rankdata.rank_title = this.f_rank_title.value;
-    this.rankdata.rank_style = this.getCSS();
-    
-    // transform various controls
-    this.th_head.firstChild.nodeValue = $lang.get('acpur_th_edit_rank', {
-        rank_title: $lang.get(this.rankdata.rank_title)
-      });
-    this.btn_submit.value = $lang.get('acpur_btn_save');
-    
-    // add the delete button
-    var th_submit = this.btn_submit.parentNode;
-    
-    var btn_delete = document.createElement('input');
-    btn_delete.type = 'button';
-    btn_delete.value = $lang.get('acpur_btn_delete');
-    btn_delete.editor = this;
-    btn_delete.onclick = function(e)
-    {
-      this.editor.deleteEvent(e);
-    }
-    th_submit.appendChild(document.createTextNode(' '));
-    th_submit.appendChild(btn_delete);
-    
-    return true;
-  }
-  
-  /**
-   * Takes a hex color, averages the three channels, and returns either 'ffffff' or '000000' depending on the luminosity of the color.
-   * @param string
-   * @return string
-   */
-  
-  this.determineLightness = function(hexval)
-  {
-    var rgb = this.hex2rgb(hexval);
-    var lumin = ( rgb[0] + rgb[1] + rgb[2] ) / 3;
-    return ( lumin > 60 ) ? '000000' : 'ffffff';
-  }
-  
-  /**
-   * Strips out basic CSS attributes (color, font-weight, font-style, text-decoration) from a snippet of CSS.
-   * @param string
-   * @return string
-   */
-  
-  this.stripBasicCSSAttributes = function(css)
-  {
-    return trim(css.replace(/(color|font-weight|font-style|text-decoration): ?([A-z0-9# ,\(\)]+);/g, ''));
-  }
-  
-  /**
-   * Strips out all but basic CSS attributes.
-   * @param string
-   * @return string
-   */
-  
-  this.stripExtendedCSSAttributes = function(css)
-  {
-    var match;
-    var final_css = '';
-    var basics = ['color', 'font-weight', 'font-style', 'text-decoration'];
-    while ( match = css.match(/([a-z-]+):(.+?);/) )
-    {
-      if ( in_array(match[1], basics) )
-      {
-        final_css += ' ' + match[0] + ' ';
-      }
-      css = css.replace(match[0], '');
-    }
-    final_css = trim(final_css);
-    return final_css;
-  }
-  
-  this.getCSS = function()
-  {
-    return this.style_sim_obj.getAttribute('style');
-  }
-  
-  this.renderPreview = function()
-  {
-    if ( !this.preview_div )
-      return false;
-    var color = ( this.style_sim_obj.style.color ) ? '#' + this.rgb2hex(this.style_sim_obj.style.color) : null;
-    this.preview_div.style.color = color;
-    this.preview_div.style.fontWeight = this.style_sim_obj.style.fontWeight;
-    this.preview_div.style.fontStyle = this.style_sim_obj.style.fontStyle;
-    this.preview_div.style.textDecoration = this.style_sim_obj.style.textDecoration;
-    this.preview_div.firstChild.nodeValue = $lang.get(this.f_rank_title.value);
-  }
-  
-  this.submitEvent = function(e)
-  {
-    if ( this.onsubmit )
-    {
-      this.onsubmit(e);
-    }
-    else
-    {
-      window.console.error('RankEditorControl: no onsubmit event specified');
-    }
-  }
-  
-  this.deleteEvent = function(e)
-  {
-    if ( this.ondelete )
-    {
-      this.ondelete(e);
-    }
-    else
-    {
-      window.console.error('RankEditorControl: no ondelete event specified');
-    }
-  }
-  
-  /**
-   * Converts a parenthetical color specification (rgb(x, y, z)) to hex form (xxyyzz)
-   * @param string
-   * @return string
-   */
-  
-  this.rgb2hex = function(rgb)
-  {
-    var p = rgb.match(/^rgb\(([0-9]+), ([0-9]+), ([0-9]+)\)$/);
-    if ( !p )
-      return rgb.replace(/^#/, '');
-    
-    var r = parseInt(p[1]).toString(16), g = parseInt(p[2]).toString(16), b = parseInt(p[3]).toString(16);
-    if ( r.length < 2 )
-      r = '0' + r;
-    if ( g.length < 2 )
-      g = '0' + g;
-    if ( b.length < 2 )
-      b = '0' + b;
-    
-    return r + g + b;
-  }
-  
-  /**
-   * Get red, green, and blue values for the given hex color
-   * @param string
-   * @return array (numbered, e.g. not an object
-   */
-  
-  this.hex2rgb = function(hex)
-  {
-    hex = hex.replace(/^#/, '');
-    if ( hex.length != 3 && hex.length != 6 )
-    {
-      return hex;
-    }
-    if ( hex.length == 3 )
-    {
-      // is there a better way to do this?
-      hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
-    }
-    hex = [ hex.substr(0, 2), hex.substr(2, 2), hex.substr(4, 2) ];
-    var red = parseInt(hex[0], 16);
-    var green = parseInt(hex[1], 16);
-    var blue = parseInt(hex[2], 16);
-    return [red, green, blue];
-  }
+	this.rankdata = ( typeof(rankdata) == 'object' ) ? rankdata : {};
+	if ( !this.rankdata.rank_style )
+	{
+		this.rankdata.rank_style = '';
+	}
+	
+	// have the browser parse CSS for us and use an anchor to be as close
+	// as possible in calculating CSS
+	
+	// this is kind of a hack as it relies on setAttribute/getAttribute in
+	// order to obtain stringified versions of CSS data
+	var cssobj = document.createElement('a');
+	cssobj.setAttribute('style', this.rankdata.rank_style);
+	
+	this.style_sim_obj = cssobj;
+	
+	// figure out if we're editing or creating
+	this.editing = ( typeof(this.rankdata.rank_id) == 'number' );
+	
+	this.render = function()
+	{
+		var editor = document.createElement('div');
+		editor.className = 'tblholder';
+		// stash this editor instance in the parent div for later function calls
+		editor.editor = this;
+		this.wrapperdiv = editor;
+		editor.style.width = '100%';
+		
+		// tables suck.
+		var table = document.createElement('table');
+		table.setAttribute('cellspacing', '1');
+		table.setAttribute('cellpadding', '4');
+		table.setAttribute('width', '100%');
+		
+		// heading: "Edit rank: foo" or "Create a new rank"
+		var tr_head = document.createElement('tr');
+		var th_head = document.createElement('th');
+		th_head.setAttribute('colspan', '2');
+		if ( this.editing )
+		{
+			var th_head_string = 'acpur_th_edit_rank';
+			var th_head_data = { rank_title: $lang.get(this.rankdata.rank_title) };
+		}
+		else
+		{
+			var th_head_string = 'acpur_th_create_rank';
+			var th_head_data = { };
+		}
+		th_head.appendChild(document.createTextNode($lang.get(th_head_string, th_head_data)));
+		tr_head.appendChild(th_head);
+		this.th_head = th_head;
+		table.appendChild(tr_head);
+		
+		// row: rank title
+		var tr_title = document.createElement('tr');
+		var td_title_l = document.createElement('td');
+		var td_title_f = document.createElement('td');
+		
+		td_title_l.className = td_title_f.className = 'row1';
+		
+		td_title_l.appendChild(document.createTextNode($lang.get('acpur_field_rank_title')));
+		
+		// field: rank title
+		var f_rank_title = document.createElement('input');
+		f_rank_title.type = 'text';
+		f_rank_title.size = '30';
+		f_rank_title.value = ( this.editing ) ? this.rankdata.rank_title : '';
+		f_rank_title.editor = this;
+		f_rank_title.onkeyup = function()
+		{
+			this.editor.renderPreview();
+		}
+		this.f_rank_title = f_rank_title;
+		td_title_f.appendChild(f_rank_title);
+		
+		tr_title.appendChild(td_title_l);
+		tr_title.appendChild(td_title_f);
+		table.appendChild(tr_title);
+		
+		// row: basic style options
+		var tr_basic = document.createElement('tr');
+		var td_basic_l = document.createElement('td');
+		var td_basic_f = document.createElement('td');
+		
+		td_basic_l.className = td_basic_f.className = 'row2';
+		
+		td_basic_l.appendChild(document.createTextNode($lang.get('acpur_field_style_basic')));
+		
+		// fieldset: basic style options
+		// field: bold
+		var l_basic_bold = document.createElement('label');
+		var f_basic_bold = document.createElement('input');
+		f_basic_bold.type = 'checkbox';
+		f_basic_bold.checked = ( this.style_sim_obj.style.fontWeight == 'bold' ) ? true : false;
+		f_basic_bold.editor = this;
+		f_basic_bold.onclick = function()
+		{
+			this.editor.style_sim_obj.style.fontWeight = ( this.checked ) ? 'bold' : null;
+			this.editor.renderPreview();
+		}
+		l_basic_bold.style.fontWeight = 'bold';
+		l_basic_bold.appendChild(f_basic_bold);
+		l_basic_bold.appendChild(document.createTextNode(' '));
+		l_basic_bold.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_bold')));
+		
+		// field: italic
+		var l_basic_italic = document.createElement('label');
+		var f_basic_italic = document.createElement('input');
+		f_basic_italic.type = 'checkbox';
+		f_basic_italic.checked = ( this.style_sim_obj.style.fontStyle == 'italic' ) ? true : false;
+		f_basic_italic.editor = this;
+		f_basic_italic.onclick = function()
+		{
+			this.editor.style_sim_obj.style.fontStyle = ( this.checked ) ? 'italic' : null;
+			this.editor.renderPreview();
+		}
+		l_basic_italic.style.fontStyle = 'italic';
+		l_basic_italic.appendChild(f_basic_italic);
+		l_basic_italic.appendChild(document.createTextNode(' '));
+		l_basic_italic.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_italic')));
+		
+		// field: underline
+		var l_basic_underline = document.createElement('label');
+		var f_basic_underline = document.createElement('input');
+		f_basic_underline.type = 'checkbox';
+		f_basic_underline.checked = ( this.style_sim_obj.style.textDecoration == 'underline' ) ? true : false;
+		f_basic_underline.editor = this;
+		f_basic_underline.onclick = function()
+		{
+			this.editor.style_sim_obj.style.textDecoration = ( this.checked ) ? 'underline' : null;
+			this.editor.renderPreview();
+		}
+		l_basic_underline.style.textDecoration = 'underline';
+		l_basic_underline.appendChild(f_basic_underline);
+		l_basic_underline.appendChild(document.createTextNode(' '));
+		l_basic_underline.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_underline')));
+		
+		// finish up formatting row#1
+		td_basic_f.appendChild(l_basic_bold);
+		td_basic_f.appendChild(document.createTextNode(' '));
+		td_basic_f.appendChild(l_basic_italic);
+		td_basic_f.appendChild(document.createTextNode(' '));
+		td_basic_f.appendChild(l_basic_underline);
+		
+		tr_basic.appendChild(td_basic_l);
+		tr_basic.appendChild(td_basic_f);
+		table.appendChild(tr_basic);
+		
+		// row: rank color
+		var tr_color = document.createElement('tr');
+		var td_color_l = document.createElement('td');
+		var td_color_f = document.createElement('td');
+		
+		td_color_l.className = td_color_f.className = 'row1';
+		
+		td_color_l.appendChild(document.createTextNode($lang.get('acpur_field_style_color')));
+		
+		// field: rank color
+		var f_rank_color = document.createElement('input');
+		f_rank_color.type = 'text';
+		f_rank_color.size = '7';
+		f_rank_color.value = ( this.editing ) ? this.rgb2hex(this.style_sim_obj.style.color) : '';
+		f_rank_color.style.backgroundColor = this.style_sim_obj.style.color;
+		f_rank_color.editor = this;
+		this.f_rank_color = f_rank_color;
+		f_rank_color.onkeyup = function(e)
+		{
+			if ( !e.keyCode )
+				e = window.event;
+			if ( !e )
+				return false;
+			var chr = (String.fromCharCode(e.keyCode)).toLowerCase();
+			this.value = this.value.replace(/[^a-fA-F0-9]/g, '');
+			if ( this.value.length > 6 )
+			{
+				this.value = this.value.substr(0, 6);
+			}
+			if ( this.value.length == 6 || this.value.length == 3 )
+			{
+				this.style.backgroundColor = '#' + this.value;
+				this.editor.style_sim_obj.style.color = '#' + this.value;
+				this.style.color = '#' + this.editor.determineLightness(this.value);
+				this.editor.renderPreview();
+			}
+			else if ( this.value.length == 0 )
+			{
+				this.style.backgroundColor = null;
+				this.editor.style_sim_obj.style.color = null;
+				this.editor.renderPreview();
+			}
+		}
+		td_color_f.appendChild(f_rank_color);
+		
+		tr_color.appendChild(td_color_l);
+		tr_color.appendChild(td_color_f);
+		table.appendChild(tr_color);
+		
+		// field: additional CSS
+		var tr_css = document.createElement('tr');
+		
+		var td_css_l = document.createElement('td');
+		td_css_l.className = 'row2';
+		td_css_l.appendChild(document.createTextNode($lang.get('acpur_field_style_css')));
+		tr_css.appendChild(td_css_l);
+		
+		var td_css_f = document.createElement('td');
+		td_css_f.className = 'row2';
+		var f_css = document.createElement('input');
+		f_css.type = 'text';
+		f_css.value = this.stripBasicCSSAttributes(this.rankdata.rank_style);
+		f_css.style.width = '98%';
+		f_css.editor = this;
+		f_css.onkeyup = function()
+		{
+			if ( !(trim(this.value)).match(/^((([a-z-]+):(.+?);)+)?$/) )
+				return;
+			var newcss = this.editor.stripExtendedCSSAttributes(String(this.editor.style_sim_obj.getAttribute('style'))) + ' ' + this.value;
+			this.editor.preview_div.setAttribute('style', 'font-size: x-large; ' + newcss);
+			this.editor.style_sim_obj.setAttribute('style', newcss);
+		}
+		this.f_css = f_css;
+		td_css_f.appendChild(f_css);
+		tr_css.appendChild(td_css_f);
+		table.appendChild(tr_css);
+		
+		// "field": preview
+		var tr_preview = document.createElement('tr');
+		var td_preview_l = document.createElement('td');
+		td_preview_l.className = 'row1';
+		td_preview_l.appendChild(document.createTextNode($lang.get('acpur_field_preview')));
+		tr_preview.appendChild(td_preview_l);
+		
+		var td_preview_f = document.createElement('td');
+		td_preview_f.className = 'row1';
+		var div_preview = document.createElement('a');
+		this.preview_div = div_preview;
+		div_preview.style.fontSize = 'x-large';
+		div_preview.appendChild(document.createTextNode(''));
+		div_preview.firstChild.nodeValue = ( this.editing ) ? this.rankdata.rank_title : '';
+		td_preview_f.appendChild(div_preview);
+		tr_preview.appendChild(td_preview_f);
+		
+		table.appendChild(tr_preview);
+		
+		// submit button
+		var tr_submit = document.createElement('tr');
+		var th_submit = document.createElement('th');
+		th_submit.className = 'subhead';
+		th_submit.setAttribute('colspan', '2');
+		var btn_submit = document.createElement('input');
+		btn_submit.type = 'submit';
+		btn_submit.value = ( this.editing ) ? $lang.get('acpur_btn_save') : $lang.get('acpur_btn_create_submit');
+		btn_submit.editor = this;
+		btn_submit.style.fontWeight = 'bold';
+		btn_submit.onclick = function(e)
+		{
+			this.editor.submitEvent(e);
+		}
+		this.btn_submit = btn_submit;
+		th_submit.appendChild(btn_submit);
+		
+		// delete button
+		if ( this.editing )
+		{
+			var btn_delete = document.createElement('input');
+			btn_delete.type = 'button';
+			btn_delete.value = $lang.get('acpur_btn_delete');
+			btn_delete.editor = this;
+			btn_delete.onclick = function(e)
+			{
+				this.editor.deleteEvent(e);
+			}
+			th_submit.appendChild(document.createTextNode(' '));
+			th_submit.appendChild(btn_delete);
+		}
+		
+		tr_submit.appendChild(th_submit);
+		
+		table.appendChild(tr_submit);
+		
+		// render preview
+		this.renderPreview();
+		
+		// finalize the editor table
+		editor.appendChild(table);
+		
+		// stash rendered editor
+		this.editordiv = editor;
+		
+		// send output
+		return editor;
+	}
+	
+	/**
+ 	* Takes the existing editor div and transforms the necessary elements so that it goes from "create" mode to "edit" mode
+ 	* @param object Edit data - same format as the rankdata parameter to the constructor, but we should only need rank_id
+ 	*/
+	
+	this.transformToEditor = function(rankdata)
+	{
+		// we need a rank ID
+		if ( typeof(rankdata.rank_id) != 'number' )
+			return false;
+		
+		if ( this.editing )
+			return false;
+		
+		this.editing = true;
+		
+		this.rankdata = rankdata;
+		this.rankdata.rank_title = this.f_rank_title.value;
+		this.rankdata.rank_style = this.getCSS();
+		
+		// transform various controls
+		this.th_head.firstChild.nodeValue = $lang.get('acpur_th_edit_rank', {
+				rank_title: $lang.get(this.rankdata.rank_title)
+			});
+		this.btn_submit.value = $lang.get('acpur_btn_save');
+		
+		// add the delete button
+		var th_submit = this.btn_submit.parentNode;
+		
+		var btn_delete = document.createElement('input');
+		btn_delete.type = 'button';
+		btn_delete.value = $lang.get('acpur_btn_delete');
+		btn_delete.editor = this;
+		btn_delete.onclick = function(e)
+		{
+			this.editor.deleteEvent(e);
+		}
+		th_submit.appendChild(document.createTextNode(' '));
+		th_submit.appendChild(btn_delete);
+		
+		return true;
+	}
+	
+	/**
+ 	* Takes a hex color, averages the three channels, and returns either 'ffffff' or '000000' depending on the luminosity of the color.
+ 	* @param string
+ 	* @return string
+ 	*/
+	
+	this.determineLightness = function(hexval)
+	{
+		var rgb = this.hex2rgb(hexval);
+		var lumin = ( rgb[0] + rgb[1] + rgb[2] ) / 3;
+		return ( lumin > 60 ) ? '000000' : 'ffffff';
+	}
+	
+	/**
+ 	* Strips out basic CSS attributes (color, font-weight, font-style, text-decoration) from a snippet of CSS.
+ 	* @param string
+ 	* @return string
+ 	*/
+	
+	this.stripBasicCSSAttributes = function(css)
+	{
+		return trim(css.replace(/(color|font-weight|font-style|text-decoration): ?([A-z0-9# ,\(\)]+);/g, ''));
+	}
+	
+	/**
+ 	* Strips out all but basic CSS attributes.
+ 	* @param string
+ 	* @return string
+ 	*/
+	
+	this.stripExtendedCSSAttributes = function(css)
+	{
+		var match;
+		var final_css = '';
+		var basics = ['color', 'font-weight', 'font-style', 'text-decoration'];
+		while ( match = css.match(/([a-z-]+):(.+?);/) )
+		{
+			if ( in_array(match[1], basics) )
+			{
+				final_css += ' ' + match[0] + ' ';
+			}
+			css = css.replace(match[0], '');
+		}
+		final_css = trim(final_css);
+		return final_css;
+	}
+	
+	this.getCSS = function()
+	{
+		return this.style_sim_obj.getAttribute('style');
+	}
+	
+	this.renderPreview = function()
+	{
+		if ( !this.preview_div )
+			return false;
+		var color = ( this.style_sim_obj.style.color ) ? '#' + this.rgb2hex(this.style_sim_obj.style.color) : null;
+		this.preview_div.style.color = color;
+		this.preview_div.style.fontWeight = this.style_sim_obj.style.fontWeight;
+		this.preview_div.style.fontStyle = this.style_sim_obj.style.fontStyle;
+		this.preview_div.style.textDecoration = this.style_sim_obj.style.textDecoration;
+		this.preview_div.firstChild.nodeValue = $lang.get(this.f_rank_title.value);
+	}
+	
+	this.submitEvent = function(e)
+	{
+		if ( this.onsubmit )
+		{
+			this.onsubmit(e);
+		}
+		else
+		{
+			window.console.error('RankEditorControl: no onsubmit event specified');
+		}
+	}
+	
+	this.deleteEvent = function(e)
+	{
+		if ( this.ondelete )
+		{
+			this.ondelete(e);
+		}
+		else
+		{
+			window.console.error('RankEditorControl: no ondelete event specified');
+		}
+	}
+	
+	/**
+ 	* Converts a parenthetical color specification (rgb(x, y, z)) to hex form (xxyyzz)
+ 	* @param string
+ 	* @return string
+ 	*/
+	
+	this.rgb2hex = function(rgb)
+	{
+		var p = rgb.match(/^rgb\(([0-9]+), ([0-9]+), ([0-9]+)\)$/);
+		if ( !p )
+			return rgb.replace(/^#/, '');
+		
+		var r = parseInt(p[1]).toString(16), g = parseInt(p[2]).toString(16), b = parseInt(p[3]).toString(16);
+		if ( r.length < 2 )
+			r = '0' + r;
+		if ( g.length < 2 )
+			g = '0' + g;
+		if ( b.length < 2 )
+			b = '0' + b;
+		
+		return r + g + b;
+	}
+	
+	/**
+ 	* Get red, green, and blue values for the given hex color
+ 	* @param string
+ 	* @return array (numbered, e.g. not an object
+ 	*/
+	
+	this.hex2rgb = function(hex)
+	{
+		hex = hex.replace(/^#/, '');
+		if ( hex.length != 3 && hex.length != 6 )
+		{
+			return hex;
+		}
+		if ( hex.length == 3 )
+		{
+			// is there a better way to do this?
+			hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
+		}
+		hex = [ hex.substr(0, 2), hex.substr(2, 2), hex.substr(4, 2) ];
+		var red = parseInt(hex[0], 16);
+		var green = parseInt(hex[1], 16);
+		var blue = parseInt(hex[2], 16);
+		return [red, green, blue];
+	}
 }
 
 /**
@@ -483,313 +483,313 @@
 
 function ajaxInitRankEdit(rank_id)
 {
-  load_component('messagebox');
-  var json_packet = {
-    mode: 'get_rank',
-    rank_id: rank_id
-  };
-  json_packet = ajaxEscape(toJSONString(json_packet));
-  ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(ajax.responseText);
-          return false;
-        }
-        try
-        {
-          var response = parseJSON(ajax.responseText);
-        }
-        catch(e)
-        {
-          handle_invalid_json(ajax.responseText);
-        }
-        if ( response.error )
-        {
-          if ( response.error == 'need_auth_to_admin' )
-          {
-            load_component('login');
-            var rid = rank_id;
-            ajaxDynamicReauth(function()
-              {
-                ajaxInitRankEdit(rid);
-              });
-          }
-          else
-          {
-            alert(response.error);
-          }
-          return false;
-        }
-        var editor = new RankEditorControl(response);
-        editor.onsubmit = ajaxRankEditHandleSaveExisting;
-        editor.ondelete = ajaxRankEditHandleDelete;
-        var container = document.getElementById('admin_ranks_container_right');
-        container.innerHTML = '';
-        container.appendChild(editor.render());
-      }
-    }, true);
+	load_component('messagebox');
+	var json_packet = {
+		mode: 'get_rank',
+		rank_id: rank_id
+	};
+	json_packet = ajaxEscape(toJSONString(json_packet));
+	ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(ajax.responseText);
+					return false;
+				}
+				try
+				{
+					var response = parseJSON(ajax.responseText);
+				}
+				catch(e)
+				{
+					handle_invalid_json(ajax.responseText);
+				}
+				if ( response.error )
+				{
+					if ( response.error == 'need_auth_to_admin' )
+					{
+						load_component('login');
+						var rid = rank_id;
+						ajaxDynamicReauth(function()
+							{
+								ajaxInitRankEdit(rid);
+							});
+					}
+					else
+					{
+						alert(response.error);
+					}
+					return false;
+				}
+				var editor = new RankEditorControl(response);
+				editor.onsubmit = ajaxRankEditHandleSaveExisting;
+				editor.ondelete = ajaxRankEditHandleDelete;
+				var container = document.getElementById('admin_ranks_container_right');
+				container.innerHTML = '';
+				container.appendChild(editor.render());
+			}
+		}, true);
 }
 
 function ajaxInitRankCreate()
 {
-  load_component('messagebox');
-  var editor = new RankEditorControl();
-  editor.onsubmit = ajaxRankEditHandleSaveNew;
-  var container = document.getElementById('admin_ranks_container_right');
-  container.innerHTML = '';
-  container.appendChild(editor.render());
+	load_component('messagebox');
+	var editor = new RankEditorControl();
+	editor.onsubmit = ajaxRankEditHandleSaveNew;
+	var container = document.getElementById('admin_ranks_container_right');
+	container.innerHTML = '';
+	container.appendChild(editor.render());
 }
 
 function ajaxRankEditHandleSave(editor, switch_new)
 {
-  var whitey = whiteOutElement(editor.wrapperdiv);
-  
-  // pack it up, ...
-  var json_packet = {
-    mode: ( switch_new ) ? 'create_rank' : 'save_rank',
-    rank_title: editor.f_rank_title.value,
-    rank_style: editor.getCSS()
-  }
-  if ( !switch_new )
-  {
-    json_packet.rank_id = editor.rankdata.rank_id;
-  }
-  /// ... pack it in
-  var json_packet = ajaxEscape(toJSONString(json_packet));
-  
-  ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(ajax.responseText);
-          return false;
-        }
-        try
-        {
-          var response = parseJSON(ajax.responseText);
-        }
-        catch(e)
-        {
-          handle_invalid_json(ajax.responseText);
-        }
-        if ( response.mode == 'success' )
-        {
-          whiteOutReportSuccess(whitey);
-          if ( switch_new )
-          {
-            //
-            // we have a few more things to do with a newly created rank.
-            //
-            
-            // 1. transform editor
-            editor.transformToEditor(response);
-            editor.onsubmit = ajaxRankEditHandleSaveExisting;
-            editor.ondelete = ajaxRankEditHandleDelete;
-            
-            // 2. append the new rank to the list
-            var create_link = document.getElementById('rankadmin_createlink');
-            if ( create_link )
-            {
-              var parent = create_link.parentNode;
-              var edit_link = document.createElement('a');
-              edit_link.href = '#rank_edit:' + response.rank_id;
-              edit_link.className = 'rankadmin-editlink';
-              edit_link.setAttribute('style', editor.getCSS());
-              edit_link.id = 'rankadmin_editlink_' + response.rank_id;
-              edit_link.rank_id = response.rank_id;
-              edit_link.appendChild(document.createTextNode($lang.get(editor.f_rank_title.value)));
-              parent.insertBefore(edit_link, create_link);
-              edit_link.onclick = function()
-              {
-                ajaxInitRankEdit(this.rank_id);
-              }
-            }
-          }
-          else
-          {
-            // update the rank title on the left
-            var edit_link = document.getElementById('rankadmin_editlink_' + editor.rankdata.rank_id);
-            if ( edit_link )
-            {
-              edit_link.firstChild.nodeValue = $lang.get(editor.f_rank_title.value);
-              edit_link.setAttribute('style', editor.getCSS());
-            }
-          }
-        }
-        else
-        {
-          whitey.parentNode.removeChild(whitey);
-          if ( response.error == 'need_auth_to_admin' )
-          {
-            load_component('login');
-            ajaxDynamicReauth(function()
-              {
-                ajaxRankEditHandleSave(editor, switch_new);
-              });
-          }
-          else
-          {
-            miniPromptMessage({
-                title: $lang.get('acpur_err_save_failed_title'),
-                message: response.error,
-                buttons: [
-                  {
-                    text: $lang.get('etc_ok'),
-                    color: 'red',
-                    style: {
-                      fontWeight: 'bold'
-                    },
-                    onclick: function()
-                    {
-                      miniPromptDestroy(this);
-                    }
-                  }
-                ]
-            });
-          }
-        }
-      }
-    }, true);
+	var whitey = whiteOutElement(editor.wrapperdiv);
+	
+	// pack it up, ...
+	var json_packet = {
+		mode: ( switch_new ) ? 'create_rank' : 'save_rank',
+		rank_title: editor.f_rank_title.value,
+		rank_style: editor.getCSS()
+	}
+	if ( !switch_new )
+	{
+		json_packet.rank_id = editor.rankdata.rank_id;
+	}
+	/// ... pack it in
+	var json_packet = ajaxEscape(toJSONString(json_packet));
+	
+	ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(ajax.responseText);
+					return false;
+				}
+				try
+				{
+					var response = parseJSON(ajax.responseText);
+				}
+				catch(e)
+				{
+					handle_invalid_json(ajax.responseText);
+				}
+				if ( response.mode == 'success' )
+				{
+					whiteOutReportSuccess(whitey);
+					if ( switch_new )
+					{
+						//
+						// we have a few more things to do with a newly created rank.
+						//
+						
+						// 1. transform editor
+						editor.transformToEditor(response);
+						editor.onsubmit = ajaxRankEditHandleSaveExisting;
+						editor.ondelete = ajaxRankEditHandleDelete;
+						
+						// 2. append the new rank to the list
+						var create_link = document.getElementById('rankadmin_createlink');
+						if ( create_link )
+						{
+							var parent = create_link.parentNode;
+							var edit_link = document.createElement('a');
+							edit_link.href = '#rank_edit:' + response.rank_id;
+							edit_link.className = 'rankadmin-editlink';
+							edit_link.setAttribute('style', editor.getCSS());
+							edit_link.id = 'rankadmin_editlink_' + response.rank_id;
+							edit_link.rank_id = response.rank_id;
+							edit_link.appendChild(document.createTextNode($lang.get(editor.f_rank_title.value)));
+							parent.insertBefore(edit_link, create_link);
+							edit_link.onclick = function()
+							{
+								ajaxInitRankEdit(this.rank_id);
+							}
+						}
+					}
+					else
+					{
+						// update the rank title on the left
+						var edit_link = document.getElementById('rankadmin_editlink_' + editor.rankdata.rank_id);
+						if ( edit_link )
+						{
+							edit_link.firstChild.nodeValue = $lang.get(editor.f_rank_title.value);
+							edit_link.setAttribute('style', editor.getCSS());
+						}
+					}
+				}
+				else
+				{
+					whitey.parentNode.removeChild(whitey);
+					if ( response.error == 'need_auth_to_admin' )
+					{
+						load_component('login');
+						ajaxDynamicReauth(function()
+							{
+								ajaxRankEditHandleSave(editor, switch_new);
+							});
+					}
+					else
+					{
+						miniPromptMessage({
+								title: $lang.get('acpur_err_save_failed_title'),
+								message: response.error,
+								buttons: [
+									{
+										text: $lang.get('etc_ok'),
+										color: 'red',
+										style: {
+											fontWeight: 'bold'
+										},
+										onclick: function()
+										{
+											miniPromptDestroy(this);
+										}
+									}
+								]
+						});
+					}
+				}
+			}
+		}, true);
 }
 
 var ajaxRankEditHandleSaveExisting = function()
 {
-  ajaxRankEditHandleSave(this, false);
+	ajaxRankEditHandleSave(this, false);
 }
 
 var ajaxRankEditHandleSaveNew = function()
 {
-  ajaxRankEditHandleSave(this, true);
+	ajaxRankEditHandleSave(this, true);
 }
 
 var ajaxRankEditHandleDelete = function()
 {
-  var mp = miniPromptMessage({
-      title: $lang.get('acpur_msg_rank_delete_confirm_title'),
-      message: $lang.get('acpur_msg_rank_delete_confirm_body'),
-      buttons: [
-        {
-          text: $lang.get('acpur_btn_delete'),
-          color: 'red',
-          style: {
-            fontWeight: 'bold'
-          },
-          onclick: function()
-          {
-            var parent = miniPromptGetParent(this);
-            var editor = parent.editor;
-            setTimeout(function()
-              {
-                ajaxRankEditDeleteConfirmed(editor);
-              }, 1000);
-            miniPromptDestroy(parent);
-          }
-        },
-        {
-          text: $lang.get('etc_cancel'),
-          onclick: function()
-          {
-            miniPromptDestroy(this);
-          }
-        }
-      ]
-    });
-  console.debug(mp);
-  mp.editor = this;
+	var mp = miniPromptMessage({
+			title: $lang.get('acpur_msg_rank_delete_confirm_title'),
+			message: $lang.get('acpur_msg_rank_delete_confirm_body'),
+			buttons: [
+				{
+					text: $lang.get('acpur_btn_delete'),
+					color: 'red',
+					style: {
+						fontWeight: 'bold'
+					},
+					onclick: function()
+					{
+						var parent = miniPromptGetParent(this);
+						var editor = parent.editor;
+						setTimeout(function()
+							{
+								ajaxRankEditDeleteConfirmed(editor);
+							}, 1000);
+						miniPromptDestroy(parent);
+					}
+				},
+				{
+					text: $lang.get('etc_cancel'),
+					onclick: function()
+					{
+						miniPromptDestroy(this);
+					}
+				}
+			]
+		});
+	console.debug(mp);
+	mp.editor = this;
 }
 
 function ajaxRankEditDeleteConfirmed(editor)
 {
-  var whitey = whiteOutElement(editor.wrapperdiv);
-  
-  load_component(['jquery', 'jquery-ui']);
-  
-  var json_packet = {
-    mode: 'delete_rank',
-    rank_id: editor.rankdata.rank_id
-  };
-  var rank_id = editor.rankdata.rank_id;
-  
-  json_packet = ajaxEscape(toJSONString(json_packet));
-  ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          handle_invalid_json(ajax.responseText);
-          return false;
-        }
-        try
-        {
-          var response = parseJSON(ajax.responseText);
-        }
-        catch(e)
-        {
-          handle_invalid_json(ajax.responseText);
-        }
-        if ( response.mode == 'success' )
-        {
-          // the deletion was successful, report success and kill off the editor
-          whiteOutReportSuccess(whitey);
-          setTimeout(function()
-            {
-              // nuke the rank title on the left
-              var edit_link = document.getElementById('rankadmin_editlink_' + editor.rankdata.rank_id);
-              if ( edit_link )
-              {
-                edit_link.parentNode.removeChild(edit_link);
-              }
-              // collapse and destroy the editor
-              $(editor.wrapperdiv).hide("blind", {}, 500, function()
-                  {
-                    // when the animation finishes, nuke the whole thing
-                    var container = document.getElementById('admin_ranks_container_right');
-                    container.innerHTML = $lang.get('acpur_msg_select_rank');
-                  }
-                );
-            }, 1500);
-        }
-        else
-        {
-          whitey.parentNode.removeChild(whitey);
-          if ( response.error == 'need_auth_to_admin' )
-          {
-            load_component('login');
-            ajaxDynamicReauth(function()
-              {
-                ajaxRankEditDeleteConfirmed(editor);
-              });
-          }
-          else
-          {
-            miniPromptMessage({
-                title: $lang.get('acpur_err_delete_failed_title'),
-                message: response.error,
-                buttons: [
-                  {
-                    text: $lang.get('etc_ok'),
-                    color: 'red',
-                    style: {
-                      fontWeight: 'bold'
-                    },
-                    onclick: function()
-                    {
-                      miniPromptDestroy(this);
-                    }
-                  }
-                ]
-            });
-          }
-        }
-      }
-    }, true);
+	var whitey = whiteOutElement(editor.wrapperdiv);
+	
+	load_component(['jquery', 'jquery-ui']);
+	
+	var json_packet = {
+		mode: 'delete_rank',
+		rank_id: editor.rankdata.rank_id
+	};
+	var rank_id = editor.rankdata.rank_id;
+	
+	json_packet = ajaxEscape(toJSONString(json_packet));
+	ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					handle_invalid_json(ajax.responseText);
+					return false;
+				}
+				try
+				{
+					var response = parseJSON(ajax.responseText);
+				}
+				catch(e)
+				{
+					handle_invalid_json(ajax.responseText);
+				}
+				if ( response.mode == 'success' )
+				{
+					// the deletion was successful, report success and kill off the editor
+					whiteOutReportSuccess(whitey);
+					setTimeout(function()
+						{
+							// nuke the rank title on the left
+							var edit_link = document.getElementById('rankadmin_editlink_' + editor.rankdata.rank_id);
+							if ( edit_link )
+							{
+								edit_link.parentNode.removeChild(edit_link);
+							}
+							// collapse and destroy the editor
+							$(editor.wrapperdiv).hide("blind", {}, 500, function()
+									{
+										// when the animation finishes, nuke the whole thing
+										var container = document.getElementById('admin_ranks_container_right');
+										container.innerHTML = $lang.get('acpur_msg_select_rank');
+									}
+								);
+						}, 1500);
+				}
+				else
+				{
+					whitey.parentNode.removeChild(whitey);
+					if ( response.error == 'need_auth_to_admin' )
+					{
+						load_component('login');
+						ajaxDynamicReauth(function()
+							{
+								ajaxRankEditDeleteConfirmed(editor);
+							});
+					}
+					else
+					{
+						miniPromptMessage({
+								title: $lang.get('acpur_err_delete_failed_title'),
+								message: response.error,
+								buttons: [
+									{
+										text: $lang.get('etc_ok'),
+										color: 'red',
+										style: {
+											fontWeight: 'bold'
+										},
+										onclick: function()
+										{
+											miniPromptDestroy(this);
+										}
+									}
+								]
+						});
+					}
+				}
+			}
+		}, true);
 }
--- a/includes/clientside/static/sliders.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/sliders.js	Sun Mar 28 23:10:46 2010 -0400
@@ -5,92 +5,92 @@
 
 /*
 pseudocode:
-  oninit():
-    i = 0
-    for every div with class "slideblock", do
-      if ( cookie['mdgSliderState_' || i] == 'closed' )
-        div.hide()
-        
-      div.trigger.addEvent onclick():
-        if ( div.hidden )
-          div.show()
-          cookie['mdgSliderState_' || i] = 'open'
-        else
-          div.hide()
-          cookie['mdgSliderState_' || i] = 'closed
-          
-      i++
-    
+	oninit():
+		i = 0
+		for every div with class "slideblock", do
+			if ( cookie['mdgSliderState_' || i] == 'closed' )
+				div.hide()
+				
+			div.trigger.addEvent onclick():
+				if ( div.hidden )
+					div.show()
+					cookie['mdgSliderState_' || i] = 'open'
+				else
+					div.hide()
+					cookie['mdgSliderState_' || i] = 'closed
+					
+			i++
+		
 */
 
 
 var sliders_initted = false;
-      
+			
 var initSliders = function()
 {
-  if ( KILL_SWITCH || IE )
-    return false;
-  
-  var divs = getElementsByClassName(document, "div", "slideblock");
-  var divs2 = getElementsByClassName(document, "div", "slideblock2");
-  for ( var i = 0; i < divs2.length; i++ )
-  {
-    divs.push(divs2[i]);
-  }
-  delete divs2;
-  
-  if ( divs.length < 1 )
-    return false;
-  
-  for ( var i = 0; i < divs.length; i++ )
-  {
-    var div = divs[i];
-    // set a unique id for this slider
-    div.metaid = i;
-    
-    var cookiename = 'mdgSliderState_' + i;
-    if ( readCookie(cookiename) == 'closed' )
-    {
-      div.style.display = 'none';
-    }
-    
-    var el = div.previousSibling;
-    if ( !el )
-      continue;
-    while ( el.tagName == undefined )
-    {
-      el = el.previousSibling;
-      if ( !el )
-        break;
-    }
-    if ( !el )
-      continue;
-    var toggler = el.getElementsByTagName('a')[0];
-    if ( !toggler )
-      continue;
-    toggler.onclick = function()
-    {
-      load_component(['jquery', 'jquery-ui']);
-      
-      var mydiv = this.parentNode.nextSibling;
-      while ( mydiv.tagName != 'DIV' )
-        mydiv = mydiv.nextSibling;
-      if ( mydiv.style.display == 'none' )
-      {
-        $(mydiv).show('blind');
-        var cookiename = 'mdgSliderState_' + mydiv.metaid;
-        createCookie(cookiename, 'open', 365);
-      }
-      else
-      {
-        $(mydiv).hide('blind');
-        var cookiename = 'mdgSliderState_' + mydiv.metaid;
-        createCookie(cookiename, 'closed', 365);
-      }
-      
-      return false;
-    }
-  }
+	if ( KILL_SWITCH || IE )
+		return false;
+	
+	var divs = getElementsByClassName(document, "div", "slideblock");
+	var divs2 = getElementsByClassName(document, "div", "slideblock2");
+	for ( var i = 0; i < divs2.length; i++ )
+	{
+		divs.push(divs2[i]);
+	}
+	delete divs2;
+	
+	if ( divs.length < 1 )
+		return false;
+	
+	for ( var i = 0; i < divs.length; i++ )
+	{
+		var div = divs[i];
+		// set a unique id for this slider
+		div.metaid = i;
+		
+		var cookiename = 'mdgSliderState_' + i;
+		if ( readCookie(cookiename) == 'closed' )
+		{
+			div.style.display = 'none';
+		}
+		
+		var el = div.previousSibling;
+		if ( !el )
+			continue;
+		while ( el.tagName == undefined )
+		{
+			el = el.previousSibling;
+			if ( !el )
+				break;
+		}
+		if ( !el )
+			continue;
+		var toggler = el.getElementsByTagName('a')[0];
+		if ( !toggler )
+			continue;
+		toggler.onclick = function()
+		{
+			load_component(['jquery', 'jquery-ui']);
+			
+			var mydiv = this.parentNode.nextSibling;
+			while ( mydiv.tagName != 'DIV' )
+				mydiv = mydiv.nextSibling;
+			if ( mydiv.style.display == 'none' )
+			{
+				$(mydiv).show('blind');
+				var cookiename = 'mdgSliderState_' + mydiv.metaid;
+				createCookie(cookiename, 'open', 365);
+			}
+			else
+			{
+				$(mydiv).hide('blind');
+				var cookiename = 'mdgSliderState_' + mydiv.metaid;
+				createCookie(cookiename, 'closed', 365);
+			}
+			
+			return false;
+		}
+	}
 }
 
 addOnloadHook(initSliders);
--- a/includes/clientside/static/template-compiler.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/template-compiler.js	Sun Mar 28 23:10:46 2010 -0400
@@ -3,79 +3,79 @@
 
 window.templateParser = function(text)
 {
-  this.tpl_code    = text;
-  this.tpl_strings = new Object();
-  this.tpl_bool    = new Object();
-  this.assign_vars = __tpAssignVars;
-  this.assign_bool = __tpAssignBool;
-  this.run         = __tpRun;
+	this.tpl_code    = text;
+	this.tpl_strings = new Object();
+	this.tpl_bool    = new Object();
+	this.assign_vars = __tpAssignVars;
+	this.assign_bool = __tpAssignBool;
+	this.run         = __tpRun;
 }
 
 window.__tpAssignVars = function(vars)
 {
-  for(var i in vars)
-  {
-    this.tpl_strings[i] = vars[i];
-  }
+	for(var i in vars)
+	{
+		this.tpl_strings[i] = vars[i];
+	}
 }
 
 window.__tpAssignBool = function(vars)
 {
-  for(var i in vars)
-  {
-    this.tpl_bool[i] = ( vars[i] ) ? true : false; 
-  }
+	for(var i in vars)
+	{
+		this.tpl_bool[i] = ( vars[i] ) ? true : false; 
+	}
 }
 
 window.__tpRun = function()
 {
-  if(typeof(this.tpl_code) == 'string')
-  {
-    tpl_code = __tpCompileTemplate(this.tpl_code);
-    try {
-      compiled = eval(tpl_code);
-    }
-    catch(e)
-    {
-      alert(e);
-      aclDebug(tpl_code);
-    }
-    return compiled;
-  }
-  return false;
+	if(typeof(this.tpl_code) == 'string')
+	{
+		tpl_code = __tpCompileTemplate(this.tpl_code);
+		try {
+			compiled = eval(tpl_code);
+		}
+		catch(e)
+		{
+			alert(e);
+			aclDebug(tpl_code);
+		}
+		return compiled;
+	}
+	return false;
 }
 
 window.__tpCompileTemplate = function(code)
 {
-  // Compile plaintext/template code to javascript code
-  code = code.replace(/\\/g, "\\\\");
-  code = code.replace(/\'/g,  "\\'");
-  code = code.replace(/\"/g,  '\\"');
-  code = code.replace(new RegExp(unescape('%0A'), 'g'), '\\n');
-  code = "'" + code + "'";
-  code = code.replace(/\{([A-z0-9_-]+)\}/ig, "' + this.tpl_strings['$1'] + '");
-  code = code.replace(/\{lang:([a-z0-9_]+)\}/g, "' + $lang.get('$1') + '");
-  code = code.replace(/\<!-- IFSET ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- BEGINELSE \1 --\>([\s\S]*?)\<!-- END \1 --\>/ig, "' + ( ( typeof(this.tpl_strings['$1']) == 'string' ) ? '$2' : '$3' ) + '");
-  code = code.replace(/\<!-- IFSET ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- END \1 --\>/ig, "' + ( ( typeof(this.tpl_strings['$1']) == 'string' ) ? '$2' : '' ) + '");
-  code = code.replace(/\<!-- BEGIN ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- BEGINELSE \1 --\>([\s\S]*?)\<!-- END \1 --\>/ig, "' + ( ( this.tpl_bool['$1'] == true ) ? '$2' : '$3' ) + '");
-  code = code.replace(/\<!-- BEGIN ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- END \1 --\>/ig, "' + ( ( this.tpl_bool['$1'] == true ) ? '$2' : '' ) + '");
-  return code;
+	// Compile plaintext/template code to javascript code
+	code = code.replace(/\\/g, "\\\\");
+	code = code.replace(/\'/g,  "\\'");
+	code = code.replace(/\"/g,  '\\"');
+	code = code.replace(new RegExp(unescape('%0A'), 'g'), '\\n');
+	code = "'" + code + "'";
+	code = code.replace(/\{([A-z0-9_-]+)\}/ig, "' + this.tpl_strings['$1'] + '");
+	code = code.replace(/\{lang:([a-z0-9_]+)\}/g, "' + $lang.get('$1') + '");
+	code = code.replace(/\<!-- IFSET ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- BEGINELSE \1 --\>([\s\S]*?)\<!-- END \1 --\>/ig, "' + ( ( typeof(this.tpl_strings['$1']) == 'string' ) ? '$2' : '$3' ) + '");
+	code = code.replace(/\<!-- IFSET ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- END \1 --\>/ig, "' + ( ( typeof(this.tpl_strings['$1']) == 'string' ) ? '$2' : '' ) + '");
+	code = code.replace(/\<!-- BEGIN ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- BEGINELSE \1 --\>([\s\S]*?)\<!-- END \1 --\>/ig, "' + ( ( this.tpl_bool['$1'] == true ) ? '$2' : '$3' ) + '");
+	code = code.replace(/\<!-- BEGIN ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- END \1 --\>/ig, "' + ( ( this.tpl_bool['$1'] == true ) ? '$2' : '' ) + '");
+	return code;
 }
 
 window.__tpExtractVars = function(code)
 {
-  code = code.replace('\\', "\\\\");
-  code = code.replace("'",  "\\'");
-  code = code.replace('"',  '\\"');
-  code = code.replace(new RegExp(unescape('%0A'), 'g'), "\\n");
-  code = code.match(/\<!-- VAR ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- ENDVAR \1 -->/g);
-  code2 = '';
-  for(var i in code)
-    if(typeof(code[i]) == 'string')
-      code2 = code2 + code[i];
-  code = code2.replace(/\<!-- VAR ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- ENDVAR \1 -->/g, "'$1' : \"$2\",");
-  code = '( { ' + code + ' "________null________" : false } )';
-  vars = eval(code);
-  return vars;
+	code = code.replace('\\', "\\\\");
+	code = code.replace("'",  "\\'");
+	code = code.replace('"',  '\\"');
+	code = code.replace(new RegExp(unescape('%0A'), 'g'), "\\n");
+	code = code.match(/\<!-- VAR ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- ENDVAR \1 -->/g);
+	code2 = '';
+	for(var i in code)
+		if(typeof(code[i]) == 'string')
+			code2 = code2 + code[i];
+	code = code2.replace(/\<!-- VAR ([A-z0-9_-]+) --\>([\s\S]*?)\<!-- ENDVAR \1 -->/g, "'$1' : \"$2\",");
+	code = '( { ' + code + ' "________null________" : false } )';
+	vars = eval(code);
+	return vars;
 }
 
--- a/includes/clientside/static/theme-manager.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/theme-manager.js	Sun Mar 28 23:10:46 2010 -0400
@@ -1,661 +1,661 @@
 function ajaxToggleSystemThemes()
 {
-  var theme_list = document.getElementById('theme_list_edit');
-  var mode = ( theme_list.sys_shown ) ? 'hide' : 'show';
-  for ( var i = 0; i < theme_list.childNodes.length; i++ )
-  {
-    var child = theme_list.childNodes[i];
-    if ( child.tagName == 'DIV' )
-    {
-      if ( $dynano(child).hasClass('themebutton_theme_system') )
-      {
-        if ( $dynano(child).hasClass('themebutton_theme_disabled') )
-        {
-          $dynano(child).rmClass('themebutton_theme_disabled')
-        }
-        if ( mode == 'show' )
-        {
-          domObjChangeOpac(0, child);
-          child.style.display = 'block';
-          domOpacity(child, 0, 100, 1000);
-        }
-        else
-        {
-          domOpacity(child, 100, 0, 1000);
-          setTimeout("document.getElementById('" + child.id + "').style.display = 'none';", 1050);
-        }
-      }
-    }
-  }
-  theme_list.sys_shown = ( mode == 'show' );
-  document.getElementById('systheme_toggler').innerHTML = ( mode == 'hide' ) ? $lang.get('acptm_btn_system_themes_show') : $lang.get('acptm_btn_system_themes_hide');
+	var theme_list = document.getElementById('theme_list_edit');
+	var mode = ( theme_list.sys_shown ) ? 'hide' : 'show';
+	for ( var i = 0; i < theme_list.childNodes.length; i++ )
+	{
+		var child = theme_list.childNodes[i];
+		if ( child.tagName == 'DIV' )
+		{
+			if ( $dynano(child).hasClass('themebutton_theme_system') )
+			{
+				if ( $dynano(child).hasClass('themebutton_theme_disabled') )
+				{
+					$dynano(child).rmClass('themebutton_theme_disabled')
+				}
+				if ( mode == 'show' )
+				{
+					domObjChangeOpac(0, child);
+					child.style.display = 'block';
+					domOpacity(child, 0, 100, 1000);
+				}
+				else
+				{
+					domOpacity(child, 100, 0, 1000);
+					setTimeout("document.getElementById('" + child.id + "').style.display = 'none';", 1050);
+				}
+			}
+		}
+	}
+	theme_list.sys_shown = ( mode == 'show' );
+	document.getElementById('systheme_toggler').innerHTML = ( mode == 'hide' ) ? $lang.get('acptm_btn_system_themes_show') : $lang.get('acptm_btn_system_themes_hide');
 }
 
 function ajaxInstallTheme(theme_id)
 {
-  var thediv = document.getElementById('themebtn_install_' + theme_id);
-  if ( !thediv )
-    return false;
-  thediv.removeChild(thediv.getElementsByTagName('a')[0]);
-  var status = document.createElement('div');
-  status.className = 'status';
-  thediv.appendChild(status);
-  
-  var req = toJSONString({
-      mode: 'install',
-      theme_id: theme_id
-    });
-  // we've finished nukeing the existing interface, request editor data
-  ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + ajaxEscape(req), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var response = String(ajax.responseText + '');
-        if ( response.substr(0, 1) == '{' )
-        {
-          response = parseJSON(response);
-          if ( response.mode == 'error' )
-          {
-            alert(response.error);
-            return false;
-          }
-        }
-        
-        var theme_list = document.getElementById('theme_list_edit');
-  
-        var btn = document.createElement('div');
-        btn.className = 'themebutton';
-        btn.style.backgroundImage = thediv.style.backgroundImage;
-        btn.id = 'themebtn_edit_' + theme_id;
-        
-        var a = document.createElement('a');
-        a.className = 'tb-inner';
-        a.appendChild(document.createTextNode($lang.get('acptm_btn_theme_edit')));
-        a.appendChild(document.createTextNode("\n"));
-        a.theme_id = theme_id;
-        a.onclick = function()
-        {
-          ajaxEditTheme(this.theme_id);
-          return false;
-        }
-        a.href = '#';
-        var span = document.createElement('span');
-        span.className = 'themename';
-        span.appendChild(document.createTextNode(thediv.getAttribute('enano:themename')));
-        a.appendChild(span);
-        btn.appendChild(a);
-        btn.setAttribute('enano:themename', thediv.getAttribute('enano:themename'));
-        theme_list.appendChild(btn);
-        
-        thediv.parentNode.removeChild(thediv);
-      }
-    });
+	var thediv = document.getElementById('themebtn_install_' + theme_id);
+	if ( !thediv )
+		return false;
+	thediv.removeChild(thediv.getElementsByTagName('a')[0]);
+	var status = document.createElement('div');
+	status.className = 'status';
+	thediv.appendChild(status);
+	
+	var req = toJSONString({
+			mode: 'install',
+			theme_id: theme_id
+		});
+	// we've finished nukeing the existing interface, request editor data
+	ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + ajaxEscape(req), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var response = String(ajax.responseText + '');
+				if ( response.substr(0, 1) == '{' )
+				{
+					response = parseJSON(response);
+					if ( response.mode == 'error' )
+					{
+						alert(response.error);
+						return false;
+					}
+				}
+				
+				var theme_list = document.getElementById('theme_list_edit');
+	
+				var btn = document.createElement('div');
+				btn.className = 'themebutton';
+				btn.style.backgroundImage = thediv.style.backgroundImage;
+				btn.id = 'themebtn_edit_' + theme_id;
+				
+				var a = document.createElement('a');
+				a.className = 'tb-inner';
+				a.appendChild(document.createTextNode($lang.get('acptm_btn_theme_edit')));
+				a.appendChild(document.createTextNode("\n"));
+				a.theme_id = theme_id;
+				a.onclick = function()
+				{
+					ajaxEditTheme(this.theme_id);
+					return false;
+				}
+				a.href = '#';
+				var span = document.createElement('span');
+				span.className = 'themename';
+				span.appendChild(document.createTextNode(thediv.getAttribute('enano:themename')));
+				a.appendChild(span);
+				btn.appendChild(a);
+				btn.setAttribute('enano:themename', thediv.getAttribute('enano:themename'));
+				theme_list.appendChild(btn);
+				
+				thediv.parentNode.removeChild(thediv);
+			}
+		});
 }
 
 function ajaxEditTheme(theme_id)
 {
-  // Fade out and subsequently destroy the entire list, then make an
-  // ajax request to the theme manager for the theme info via JSON
-  var theme_list = document.getElementById('theme_list_edit').parentNode;
-  var backgroundImage = document.getElementById('themebtn_edit_' + theme_id).style.backgroundImage;
-  /*
-  for ( var i = 0; i < theme_list.childNodes.length; i++ )
-  {
-    var el = theme_list.childNodes[i];
-    if ( el.tagName )
-      domOpacity(el, 100, 0, 1000);
-  }
-  */
-  var thediv = document.getElementById('themebtn_edit_' + theme_id);
-  if ( !thediv )
-    return false;
-  thediv.removeChild(thediv.getElementsByTagName('a')[0]);
-  var status = document.createElement('div');
-  status.className = 'status';
-  thediv.appendChild(status);
-  
-  setTimeout(function()
-    {
-      var req = toJSONString({
-          mode: 'fetch_theme',
-          theme_id: theme_id
-        });
-      // we've finished nukeing the existing interface, request editor data
-      ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + ajaxEscape(req), function(ajax)
-        {
-          if ( ajax.readyState == 4 && ajax.status == 200 )
-          {
-            theme_list.innerHTML = '';
-            var response = String(ajax.responseText + '');
-            if ( !check_json_response(response) )
-            {
-              alert(response);
-              return false;
-            }
-            response = parseJSON(response);
-            if ( response.mode == 'error' )
-            {
-              alert(response.error);
-              return false;
-            }
-            response.background_image = backgroundImage;
-            ajaxBuildThemeEditor(response, theme_list);
-          }
-        });
-    }, 200);
+	// Fade out and subsequently destroy the entire list, then make an
+	// ajax request to the theme manager for the theme info via JSON
+	var theme_list = document.getElementById('theme_list_edit').parentNode;
+	var backgroundImage = document.getElementById('themebtn_edit_' + theme_id).style.backgroundImage;
+	/*
+	for ( var i = 0; i < theme_list.childNodes.length; i++ )
+	{
+		var el = theme_list.childNodes[i];
+		if ( el.tagName )
+			domOpacity(el, 100, 0, 1000);
+	}
+	*/
+	var thediv = document.getElementById('themebtn_edit_' + theme_id);
+	if ( !thediv )
+		return false;
+	thediv.removeChild(thediv.getElementsByTagName('a')[0]);
+	var status = document.createElement('div');
+	status.className = 'status';
+	thediv.appendChild(status);
+	
+	setTimeout(function()
+		{
+			var req = toJSONString({
+					mode: 'fetch_theme',
+					theme_id: theme_id
+				});
+			// we've finished nukeing the existing interface, request editor data
+			ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + ajaxEscape(req), function(ajax)
+				{
+					if ( ajax.readyState == 4 && ajax.status == 200 )
+					{
+						theme_list.innerHTML = '';
+						var response = String(ajax.responseText + '');
+						if ( !check_json_response(response) )
+						{
+							alert(response);
+							return false;
+						}
+						response = parseJSON(response);
+						if ( response.mode == 'error' )
+						{
+							alert(response.error);
+							return false;
+						}
+						response.background_image = backgroundImage;
+						ajaxBuildThemeEditor(response, theme_list);
+					}
+				});
+		}, 200);
 }
 
 function ajaxBuildThemeEditor(data, target)
 {
-  // Build the theme editor interface
-  // Init opacity
-  domObjChangeOpac(0, target);
-  
-  // Theme preview
-  var preview = document.createElement('div');
-  preview.style.border = '1px solid #F0F0F0';
-  preview.style.padding = '5px';
-  preview.style.width = '216px';
-  preview.style.height = '150px';
-  preview.style.backgroundImage = data.background_image;
-  preview.style.backgroundRepeat = 'no-repeat';
-  preview.style.backgroundPosition = 'center center';
-  preview.style.cssFloat = 'right';
-  preview.style.styleFloat = 'right';
-  
-  target.appendChild(preview);
-  
-  // Heading
-  var h3 = document.createElement('h3');
-  h3.appendChild(document.createTextNode($lang.get('acptm_heading_theme_edit', { theme_name: data.theme_name })));
-  target.appendChild(h3);
-  
-  // Field: Theme name
-  var l_name = document.createElement('label');
-  l_name.appendChild(document.createTextNode($lang.get('acptm_field_theme_name') + ' '));
-  var f_name = document.createElement('input');
-  f_name.type = 'text';
-  f_name.id = 'themeed_field_name';
-  f_name.value = data.theme_name;
-  f_name.size = '40';
-  l_name.appendChild(f_name);
-  target.appendChild(l_name);
-  
-  target.appendChild(document.createElement('br'));
-  target.appendChild(document.createElement('br'));
-  
-  // Field: default style
-  var l_style = document.createElement('label');
-  l_style.appendChild(document.createTextNode($lang.get('acptm_field_default_style') + ' '));
-  var f_style = document.createElement('select');
-  f_style.id = 'themeed_field_style';
-  var opts = [];
-  for ( var i = 0; i < data.css.length; i++ )
-  {
-    if ( data.css[i] == '_printable' )
-      continue;
-    
-    opts[i] = document.createElement('option');
-    opts[i].value = data.css[i];
-    opts[i].appendChild(document.createTextNode(data.css[i]));
-    if ( data.default_style == data.css[i] )
-    {
-      opts[i].selected = true;
-    }
-    f_style.appendChild(opts[i]);
-  }
-  l_style.appendChild(f_style);
-  target.appendChild(l_style);
-  
-  target.appendChild(document.createElement('br'));
-  target.appendChild(document.createElement('br'));
-  
-  // Default theme
-  target.appendChild(document.createTextNode($lang.get('acptm_field_default_theme') + ' '));
-  if ( data.is_default )
-  {
-    var l_default = document.createElement('b');
-    l_default.appendChild(document.createTextNode($lang.get('acptm_field_default_msg_current')));
-  }
-  else
-  {
-    var l_default = document.createElement('label');
-    var f_default = document.createElement('input');
-    f_default.type = 'checkbox';
-    f_default.id = 'themeed_field_default';
-    l_default.appendChild(f_default);
-    l_default.appendChild(document.createTextNode($lang.get('acptm_field_default_btn_make_default')));
-  }
-  target.appendChild(l_default);
-  
-  target.appendChild(document.createElement('br'));
-  target.appendChild(document.createElement('br'));
-  
-  // Disable theme
-  var disable_span = document.createElement('span');
-  disable_span.appendChild(document.createTextNode($lang.get('acptm_field_disable_title') + ' '));
-  target.appendChild(disable_span);
-  var l_disable = document.createElement('label');
-  var f_disable = document.createElement('input');
-  f_disable.type = 'checkbox';
-  f_disable.id = 'themeed_field_disable';
-  if ( !data.enabled )
-    f_disable.setAttribute('checked', 'checked');
-  l_disable.style.fontWeight = 'bold';
-  l_disable.appendChild(f_disable);
-  l_disable.appendChild(document.createTextNode($lang.get('acptm_field_disable')));
-  target.appendChild(l_disable);
-  
-  // Availability policy
-  var h3 = document.createElement('h3');
-  h3.appendChild(document.createTextNode($lang.get('acptm_heading_theme_groups')));
-  target.appendChild(h3);
-  
-  // Label for the whole field
-  var p_d_policy = document.createElement('p');
-  p_d_policy.style.fontWeight = 'bold';
-  p_d_policy.appendChild(document.createTextNode($lang.get('acptm_field_policy')));
-  target.appendChild(p_d_policy);
-  
-  // Wrapper for options
-  var p_f_policy = document.createElement('p');
-  
-  // Option: allow all
-  var l_policy_allow_all = document.createElement('label');
-  var f_policy_allow_all = document.createElement('input');
-  f_policy_allow_all.type = 'radio';
-  f_policy_allow_all.id = 'themeed_field_policy_allow_all';
-  f_policy_allow_all.name = 'themeed_field_policy';
-  f_policy_allow_all.value = 'allow_all';
-  l_policy_allow_all.appendChild(f_policy_allow_all);
-  l_policy_allow_all.appendChild(document.createTextNode(' ' + $lang.get('acptm_field_policy_allow_all')));
-  if ( data.group_policy == 'allow_all' )
-  {
-    f_policy_allow_all.setAttribute('checked', 'checked');
-  }
-  
-  // Option: whitelist
-  var l_policy_whitelist = document.createElement('label');
-  var f_policy_whitelist = document.createElement('input');
-  f_policy_whitelist.type = 'radio';
-  f_policy_whitelist.id = 'themeed_field_policy_whitelist';
-  f_policy_whitelist.name = 'themeed_field_policy';
-  f_policy_whitelist.value = 'whitelist';
-  l_policy_whitelist.appendChild(f_policy_whitelist);
-  l_policy_whitelist.appendChild(document.createTextNode(' ' + $lang.get('acptm_field_policy_whitelist')));
-  if ( data.group_policy == 'whitelist' )
-  {
-    f_policy_whitelist.setAttribute('checked', 'checked');
-  }
-  
-  // Option: blacklist
-  var l_policy_blacklist = document.createElement('label');
-  var f_policy_blacklist = document.createElement('input');
-  f_policy_blacklist.type = 'radio';
-  f_policy_blacklist.id = 'themeed_field_policy_blacklist';
-  f_policy_blacklist.name = 'themeed_field_policy';
-  f_policy_blacklist.value = 'blacklist';
-  l_policy_blacklist.appendChild(f_policy_blacklist);
-  l_policy_blacklist.appendChild(document.createTextNode(' ' + $lang.get('acptm_field_policy_blacklist')));
-  if ( data.group_policy == 'blacklist' )
-  {
-    f_policy_blacklist.setAttribute('checked', 'checked');
-  }
-  f_policy_allow_all.onclick = ajaxThemeManagerHandlePolicyClick;
-  f_policy_whitelist.onclick = ajaxThemeManagerHandlePolicyClick;
-  f_policy_blacklist.onclick = ajaxThemeManagerHandlePolicyClick;
-  
-  p_f_policy.appendChild(l_policy_allow_all);
-  p_f_policy.appendChild(document.createElement('br'));
-  p_f_policy.appendChild(l_policy_whitelist);
-  p_f_policy.appendChild(document.createElement('br'));
-  p_f_policy.appendChild(l_policy_blacklist);
-  
-  target.appendChild(p_d_policy);
-  target.appendChild(p_f_policy);
-  
-  var div_acl = document.createElement('div');
-  div_acl.id = 'themeed_acl_box';
-  div_acl.style.margin = '0 0 10px 30px';
-  
-  var h3_g = document.createElement('h3');
-  h3_g.appendChild(document.createTextNode($lang.get('acptm_field_acl_heading_groups')));
-  div_acl.appendChild(h3_g);
-  
-  var div_groups = document.createElement('div');
-  div_groups.style.border = '1px solid #E8E8E8';
-  div_groups.id = 'themeed_group_list';
-  
-  // Group list
-  for ( var i in data.group_names )
-  {
-    var g_name = data.group_names[i];
-    var check = document.createElement('input');
-    check.type = 'checkbox';
-    if ( in_array("g:" + i, data.group_list) )
-    {
-      check.setAttribute('checked', 'checked');
-    }
-    check.group_id = parseInt(i);
-    var lbl_g_acl = document.createElement('label');
-    lbl_g_acl.appendChild(check);
-    var str = 'groupcp_grp_' + g_name.toLowerCase();
-    var g_name_l10n = ( $lang.get(str) != str ) ? $lang.get(str) : g_name;
-    lbl_g_acl.appendChild(document.createTextNode(g_name_l10n));
-    div_groups.appendChild(lbl_g_acl);
-    div_groups.appendChild(document.createElement('br'));
-  }
-  div_acl.appendChild(div_groups);
-  
-  var h3_u = document.createElement('h3');
-  h3_u.appendChild(document.createTextNode($lang.get('acptm_field_acl_heading_users')));
-  div_acl.appendChild(h3_u);
-  
-  // User addition field
-  var frm = document.createElement('form');
-  frm.action = 'javascript:ajaxThemeManagerHandleUserAdd();';
-  frm.appendChild(document.createTextNode($lang.get('acptm_field_acl_add_user')));
-  var f_useradd = document.createElement('input');
-  f_useradd.type = 'text';
-  f_useradd.id = 'themeed_field_adduser';
-  f_useradd.onkeyup = function(e)
-  {
-    new AutofillUsername(this, e, false);
-  }
-  
-  frm.appendChild(f_useradd);
-  div_acl.appendChild(frm);
-  
-  div_acl.appendChild(document.createElement('br'));
-  
-  // User list
-  var div_users = document.createElement('div');
-  div_users.style.border = '1px solid #E8E8E8';
-  div_users.style.padding = '4px';
-  div_users.id = 'themeed_user_list';
-  for ( var i = 0; i < data.group_list.length; i++ )
-  {
-    var id = data.group_list[i];
-    if ( id.substr(0, 2) != 'u:' )
-      continue;
-    var uid = id.substr(2);
-    var username = data.usernames[uid];
-    
-    var useritem = document.createElement('span');
-    useritem.appendChild(document.createTextNode(username + ' '));
-    useritem.userid = parseInt(uid);
-    var deleter = document.createElement('a');
-    deleter.href = '#';
-    deleter.onclick = function()
-    {
-      ajaxThemeManagerHandleUserRemoval(this);
-      return false;
-    }
-    deleter.appendChild(document.createTextNode('[X]'));
-    useritem.appendChild(deleter);
-    div_users.appendChild(useritem);
-    div_users.appendChild(document.createElement('br'));
-  }
-  div_acl.appendChild(div_users);
-  
-  target.appendChild(div_acl);
-  
-  ajaxThemeManagerHandlePolicyClick();
-  
-  var clearer = document.createElement('span');
-  clearer.className = 'menuclear';
-  target.appendChild(clearer);
-  
-  // Theme ID
-  var tid = document.createElement('input');
-  tid.type = 'hidden';
-  tid.id = 'themeed_theme_id';
-  tid.value = data.theme_id;
-  target.appendChild(tid);
-  
-  // Save button
-  var raquo = unescape('%BB');
-  var savebtn = document.createElement('input');
-  savebtn.type = 'button';
-  savebtn.style.fontWeight = 'bold';
-  savebtn.value = $lang.get('etc_save_changes') + ' ' + raquo;
-  savebtn.onclick = function()
-  {
-    ajaxThemeManagerHandleSaveRequest();
-  }
-  target.appendChild(savebtn);
-  
-  target.appendChild(document.createTextNode(' '));
-  
-  // Cancel button
-  var savebtn = document.createElement('input');
-  savebtn.type = 'button';
-  savebtn.value = $lang.get('etc_cancel');
-  savebtn.onclick = function()
-  {
-    ajaxPage(namespace_list['Admin'] + 'ThemeManager');
-  }
-  target.appendChild(savebtn);
-  
-  target.appendChild(document.createTextNode(' '));
-  
-  // Uninstall button
-  var savebtn = document.createElement('input');
-  savebtn.type = 'button';
-  savebtn.value = $lang.get('acptm_btn_uninstall_theme');
-  savebtn.style.color = '#D84308';
-  savebtn.onclick = function()
-  {
-    if ( !confirm($lang.get('acptm_msg_uninstall_confirm')) )
-      return false;
-    ajaxThemeManagerHandleUninstallClick();
-  }
-  target.appendChild(savebtn);
-  
-  // Fade it all in
-  domOpacity(target, 0, 100, 500);
-  f_name.focus();
+	// Build the theme editor interface
+	// Init opacity
+	domObjChangeOpac(0, target);
+	
+	// Theme preview
+	var preview = document.createElement('div');
+	preview.style.border = '1px solid #F0F0F0';
+	preview.style.padding = '5px';
+	preview.style.width = '216px';
+	preview.style.height = '150px';
+	preview.style.backgroundImage = data.background_image;
+	preview.style.backgroundRepeat = 'no-repeat';
+	preview.style.backgroundPosition = 'center center';
+	preview.style.cssFloat = 'right';
+	preview.style.styleFloat = 'right';
+	
+	target.appendChild(preview);
+	
+	// Heading
+	var h3 = document.createElement('h3');
+	h3.appendChild(document.createTextNode($lang.get('acptm_heading_theme_edit', { theme_name: data.theme_name })));
+	target.appendChild(h3);
+	
+	// Field: Theme name
+	var l_name = document.createElement('label');
+	l_name.appendChild(document.createTextNode($lang.get('acptm_field_theme_name') + ' '));
+	var f_name = document.createElement('input');
+	f_name.type = 'text';
+	f_name.id = 'themeed_field_name';
+	f_name.value = data.theme_name;
+	f_name.size = '40';
+	l_name.appendChild(f_name);
+	target.appendChild(l_name);
+	
+	target.appendChild(document.createElement('br'));
+	target.appendChild(document.createElement('br'));
+	
+	// Field: default style
+	var l_style = document.createElement('label');
+	l_style.appendChild(document.createTextNode($lang.get('acptm_field_default_style') + ' '));
+	var f_style = document.createElement('select');
+	f_style.id = 'themeed_field_style';
+	var opts = [];
+	for ( var i = 0; i < data.css.length; i++ )
+	{
+		if ( data.css[i] == '_printable' )
+			continue;
+		
+		opts[i] = document.createElement('option');
+		opts[i].value = data.css[i];
+		opts[i].appendChild(document.createTextNode(data.css[i]));
+		if ( data.default_style == data.css[i] )
+		{
+			opts[i].selected = true;
+		}
+		f_style.appendChild(opts[i]);
+	}
+	l_style.appendChild(f_style);
+	target.appendChild(l_style);
+	
+	target.appendChild(document.createElement('br'));
+	target.appendChild(document.createElement('br'));
+	
+	// Default theme
+	target.appendChild(document.createTextNode($lang.get('acptm_field_default_theme') + ' '));
+	if ( data.is_default )
+	{
+		var l_default = document.createElement('b');
+		l_default.appendChild(document.createTextNode($lang.get('acptm_field_default_msg_current')));
+	}
+	else
+	{
+		var l_default = document.createElement('label');
+		var f_default = document.createElement('input');
+		f_default.type = 'checkbox';
+		f_default.id = 'themeed_field_default';
+		l_default.appendChild(f_default);
+		l_default.appendChild(document.createTextNode($lang.get('acptm_field_default_btn_make_default')));
+	}
+	target.appendChild(l_default);
+	
+	target.appendChild(document.createElement('br'));
+	target.appendChild(document.createElement('br'));
+	
+	// Disable theme
+	var disable_span = document.createElement('span');
+	disable_span.appendChild(document.createTextNode($lang.get('acptm_field_disable_title') + ' '));
+	target.appendChild(disable_span);
+	var l_disable = document.createElement('label');
+	var f_disable = document.createElement('input');
+	f_disable.type = 'checkbox';
+	f_disable.id = 'themeed_field_disable';
+	if ( !data.enabled )
+		f_disable.setAttribute('checked', 'checked');
+	l_disable.style.fontWeight = 'bold';
+	l_disable.appendChild(f_disable);
+	l_disable.appendChild(document.createTextNode($lang.get('acptm_field_disable')));
+	target.appendChild(l_disable);
+	
+	// Availability policy
+	var h3 = document.createElement('h3');
+	h3.appendChild(document.createTextNode($lang.get('acptm_heading_theme_groups')));
+	target.appendChild(h3);
+	
+	// Label for the whole field
+	var p_d_policy = document.createElement('p');
+	p_d_policy.style.fontWeight = 'bold';
+	p_d_policy.appendChild(document.createTextNode($lang.get('acptm_field_policy')));
+	target.appendChild(p_d_policy);
+	
+	// Wrapper for options
+	var p_f_policy = document.createElement('p');
+	
+	// Option: allow all
+	var l_policy_allow_all = document.createElement('label');
+	var f_policy_allow_all = document.createElement('input');
+	f_policy_allow_all.type = 'radio';
+	f_policy_allow_all.id = 'themeed_field_policy_allow_all';
+	f_policy_allow_all.name = 'themeed_field_policy';
+	f_policy_allow_all.value = 'allow_all';
+	l_policy_allow_all.appendChild(f_policy_allow_all);
+	l_policy_allow_all.appendChild(document.createTextNode(' ' + $lang.get('acptm_field_policy_allow_all')));
+	if ( data.group_policy == 'allow_all' )
+	{
+		f_policy_allow_all.setAttribute('checked', 'checked');
+	}
+	
+	// Option: whitelist
+	var l_policy_whitelist = document.createElement('label');
+	var f_policy_whitelist = document.createElement('input');
+	f_policy_whitelist.type = 'radio';
+	f_policy_whitelist.id = 'themeed_field_policy_whitelist';
+	f_policy_whitelist.name = 'themeed_field_policy';
+	f_policy_whitelist.value = 'whitelist';
+	l_policy_whitelist.appendChild(f_policy_whitelist);
+	l_policy_whitelist.appendChild(document.createTextNode(' ' + $lang.get('acptm_field_policy_whitelist')));
+	if ( data.group_policy == 'whitelist' )
+	{
+		f_policy_whitelist.setAttribute('checked', 'checked');
+	}
+	
+	// Option: blacklist
+	var l_policy_blacklist = document.createElement('label');
+	var f_policy_blacklist = document.createElement('input');
+	f_policy_blacklist.type = 'radio';
+	f_policy_blacklist.id = 'themeed_field_policy_blacklist';
+	f_policy_blacklist.name = 'themeed_field_policy';
+	f_policy_blacklist.value = 'blacklist';
+	l_policy_blacklist.appendChild(f_policy_blacklist);
+	l_policy_blacklist.appendChild(document.createTextNode(' ' + $lang.get('acptm_field_policy_blacklist')));
+	if ( data.group_policy == 'blacklist' )
+	{
+		f_policy_blacklist.setAttribute('checked', 'checked');
+	}
+	f_policy_allow_all.onclick = ajaxThemeManagerHandlePolicyClick;
+	f_policy_whitelist.onclick = ajaxThemeManagerHandlePolicyClick;
+	f_policy_blacklist.onclick = ajaxThemeManagerHandlePolicyClick;
+	
+	p_f_policy.appendChild(l_policy_allow_all);
+	p_f_policy.appendChild(document.createElement('br'));
+	p_f_policy.appendChild(l_policy_whitelist);
+	p_f_policy.appendChild(document.createElement('br'));
+	p_f_policy.appendChild(l_policy_blacklist);
+	
+	target.appendChild(p_d_policy);
+	target.appendChild(p_f_policy);
+	
+	var div_acl = document.createElement('div');
+	div_acl.id = 'themeed_acl_box';
+	div_acl.style.margin = '0 0 10px 30px';
+	
+	var h3_g = document.createElement('h3');
+	h3_g.appendChild(document.createTextNode($lang.get('acptm_field_acl_heading_groups')));
+	div_acl.appendChild(h3_g);
+	
+	var div_groups = document.createElement('div');
+	div_groups.style.border = '1px solid #E8E8E8';
+	div_groups.id = 'themeed_group_list';
+	
+	// Group list
+	for ( var i in data.group_names )
+	{
+		var g_name = data.group_names[i];
+		var check = document.createElement('input');
+		check.type = 'checkbox';
+		if ( in_array("g:" + i, data.group_list) )
+		{
+			check.setAttribute('checked', 'checked');
+		}
+		check.group_id = parseInt(i);
+		var lbl_g_acl = document.createElement('label');
+		lbl_g_acl.appendChild(check);
+		var str = 'groupcp_grp_' + g_name.toLowerCase();
+		var g_name_l10n = ( $lang.get(str) != str ) ? $lang.get(str) : g_name;
+		lbl_g_acl.appendChild(document.createTextNode(g_name_l10n));
+		div_groups.appendChild(lbl_g_acl);
+		div_groups.appendChild(document.createElement('br'));
+	}
+	div_acl.appendChild(div_groups);
+	
+	var h3_u = document.createElement('h3');
+	h3_u.appendChild(document.createTextNode($lang.get('acptm_field_acl_heading_users')));
+	div_acl.appendChild(h3_u);
+	
+	// User addition field
+	var frm = document.createElement('form');
+	frm.action = 'javascript:ajaxThemeManagerHandleUserAdd();';
+	frm.appendChild(document.createTextNode($lang.get('acptm_field_acl_add_user')));
+	var f_useradd = document.createElement('input');
+	f_useradd.type = 'text';
+	f_useradd.id = 'themeed_field_adduser';
+	f_useradd.onkeyup = function(e)
+	{
+		new AutofillUsername(this, e, false);
+	}
+	
+	frm.appendChild(f_useradd);
+	div_acl.appendChild(frm);
+	
+	div_acl.appendChild(document.createElement('br'));
+	
+	// User list
+	var div_users = document.createElement('div');
+	div_users.style.border = '1px solid #E8E8E8';
+	div_users.style.padding = '4px';
+	div_users.id = 'themeed_user_list';
+	for ( var i = 0; i < data.group_list.length; i++ )
+	{
+		var id = data.group_list[i];
+		if ( id.substr(0, 2) != 'u:' )
+			continue;
+		var uid = id.substr(2);
+		var username = data.usernames[uid];
+		
+		var useritem = document.createElement('span');
+		useritem.appendChild(document.createTextNode(username + ' '));
+		useritem.userid = parseInt(uid);
+		var deleter = document.createElement('a');
+		deleter.href = '#';
+		deleter.onclick = function()
+		{
+			ajaxThemeManagerHandleUserRemoval(this);
+			return false;
+		}
+		deleter.appendChild(document.createTextNode('[X]'));
+		useritem.appendChild(deleter);
+		div_users.appendChild(useritem);
+		div_users.appendChild(document.createElement('br'));
+	}
+	div_acl.appendChild(div_users);
+	
+	target.appendChild(div_acl);
+	
+	ajaxThemeManagerHandlePolicyClick();
+	
+	var clearer = document.createElement('span');
+	clearer.className = 'menuclear';
+	target.appendChild(clearer);
+	
+	// Theme ID
+	var tid = document.createElement('input');
+	tid.type = 'hidden';
+	tid.id = 'themeed_theme_id';
+	tid.value = data.theme_id;
+	target.appendChild(tid);
+	
+	// Save button
+	var raquo = unescape('%BB');
+	var savebtn = document.createElement('input');
+	savebtn.type = 'button';
+	savebtn.style.fontWeight = 'bold';
+	savebtn.value = $lang.get('etc_save_changes') + ' ' + raquo;
+	savebtn.onclick = function()
+	{
+		ajaxThemeManagerHandleSaveRequest();
+	}
+	target.appendChild(savebtn);
+	
+	target.appendChild(document.createTextNode(' '));
+	
+	// Cancel button
+	var savebtn = document.createElement('input');
+	savebtn.type = 'button';
+	savebtn.value = $lang.get('etc_cancel');
+	savebtn.onclick = function()
+	{
+		ajaxPage(namespace_list['Admin'] + 'ThemeManager');
+	}
+	target.appendChild(savebtn);
+	
+	target.appendChild(document.createTextNode(' '));
+	
+	// Uninstall button
+	var savebtn = document.createElement('input');
+	savebtn.type = 'button';
+	savebtn.value = $lang.get('acptm_btn_uninstall_theme');
+	savebtn.style.color = '#D84308';
+	savebtn.onclick = function()
+	{
+		if ( !confirm($lang.get('acptm_msg_uninstall_confirm')) )
+			return false;
+		ajaxThemeManagerHandleUninstallClick();
+	}
+	target.appendChild(savebtn);
+	
+	// Fade it all in
+	domOpacity(target, 0, 100, 500);
+	f_name.focus();
 }
 
 function ajaxThemeManagerHandlePolicyClick()
 {
-  if ( document.getElementById('themeed_field_policy_allow_all').checked )
-  {
-    document.getElementById('themeed_acl_box').style.display = 'none';
-  }
-  else if ( document.getElementById('themeed_field_policy_whitelist').checked || document.getElementById('themeed_field_policy_blacklist').checked )
-  {
-    document.getElementById('themeed_acl_box').style.display = 'block';
-  }
+	if ( document.getElementById('themeed_field_policy_allow_all').checked )
+	{
+		document.getElementById('themeed_acl_box').style.display = 'none';
+	}
+	else if ( document.getElementById('themeed_field_policy_whitelist').checked || document.getElementById('themeed_field_policy_blacklist').checked )
+	{
+		document.getElementById('themeed_acl_box').style.display = 'block';
+	}
 }
 
 function ajaxThemeManagerHandleUserAdd()
 {
-  var f_useradd = document.getElementById('themeed_field_adduser');
-  f_useradd.setAttribute('disabled', 'disabled');
-  var parent = f_useradd.parentNode;
-  var img = document.createElement('img');
-  img.src = ajax_load_icon;
-  img.id = 'themeed_useradd_status';
-  img.style.marginLeft = '10px';
-  insertAfter(parent, img, f_useradd);
-  
-  var req = toJSONString({
-      mode: 'uid_lookup',
-      username: f_useradd.value
-    });
-  ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + ajaxEscape(req), function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        var img = document.getElementById('themeed_useradd_status');
-        var f_useradd = document.getElementById('themeed_field_adduser');
-        
-        f_useradd.disabled = null;
-        img.parentNode.removeChild(img);
-        
-        // process response
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          alert(response);
-          return false;
-        }
-        response = parseJSON(response);
-        if ( response.mode == 'error' )
-        {
-          alert(response.error);
-          return false;
-        }
-            
-        var uid = parseInt(response.uid);
-        var username = response.username;
-        
-        // Loop through the list of users and remove any existing ones with the same uid
-        var div_users = document.getElementById('themeed_user_list');
-        var children = div_users.getElementsByTagName('span');
-        for ( var i = 0; i < children.length; i++ )
-        {
-          var child = children[i];
-          if ( child.userid == uid )
-          {
-            // the sister is the br element next to the span with the checkbox/text
-            var sister = child.nextSibling;
-            div_users.removeChild(child);
-            div_users.removeChild(sister);
-            break;
-          }
-        }
-        
-        var useritem = document.createElement('span');
-        useritem.appendChild(document.createTextNode(username + ' '));
-        useritem.userid = parseInt(uid);
-        var deleter = document.createElement('a');
-        deleter.href = '#';
-        deleter.onclick = function()
-        {
-          ajaxThemeManagerHandleUserRemoval(this);
-          return false;
-        }
-        deleter.appendChild(document.createTextNode('[X]'));
-        useritem.appendChild(deleter);
-        div_users.appendChild(useritem);
-        div_users.appendChild(document.createElement('br'));
-      }
-    });
+	var f_useradd = document.getElementById('themeed_field_adduser');
+	f_useradd.setAttribute('disabled', 'disabled');
+	var parent = f_useradd.parentNode;
+	var img = document.createElement('img');
+	img.src = ajax_load_icon;
+	img.id = 'themeed_useradd_status';
+	img.style.marginLeft = '10px';
+	insertAfter(parent, img, f_useradd);
+	
+	var req = toJSONString({
+			mode: 'uid_lookup',
+			username: f_useradd.value
+		});
+	ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + ajaxEscape(req), function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				var img = document.getElementById('themeed_useradd_status');
+				var f_useradd = document.getElementById('themeed_field_adduser');
+				
+				f_useradd.disabled = null;
+				img.parentNode.removeChild(img);
+				
+				// process response
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					alert(response);
+					return false;
+				}
+				response = parseJSON(response);
+				if ( response.mode == 'error' )
+				{
+					alert(response.error);
+					return false;
+				}
+						
+				var uid = parseInt(response.uid);
+				var username = response.username;
+				
+				// Loop through the list of users and remove any existing ones with the same uid
+				var div_users = document.getElementById('themeed_user_list');
+				var children = div_users.getElementsByTagName('span');
+				for ( var i = 0; i < children.length; i++ )
+				{
+					var child = children[i];
+					if ( child.userid == uid )
+					{
+						// the sister is the br element next to the span with the checkbox/text
+						var sister = child.nextSibling;
+						div_users.removeChild(child);
+						div_users.removeChild(sister);
+						break;
+					}
+				}
+				
+				var useritem = document.createElement('span');
+				useritem.appendChild(document.createTextNode(username + ' '));
+				useritem.userid = parseInt(uid);
+				var deleter = document.createElement('a');
+				deleter.href = '#';
+				deleter.onclick = function()
+				{
+					ajaxThemeManagerHandleUserRemoval(this);
+					return false;
+				}
+				deleter.appendChild(document.createTextNode('[X]'));
+				useritem.appendChild(deleter);
+				div_users.appendChild(useritem);
+				div_users.appendChild(document.createElement('br'));
+			}
+		});
 }
 
 function ajaxThemeManagerHandleUserRemoval(el)
 {
-  var parent = el.parentNode;
-  var uid = parent.userid;
-  
-  var grandparent = parent.parentNode;
-  var sister = parent.nextSibling;
-  grandparent.removeChild(parent);
-  grandparent.removeChild(sister);
+	var parent = el.parentNode;
+	var uid = parent.userid;
+	
+	var grandparent = parent.parentNode;
+	var sister = parent.nextSibling;
+	grandparent.removeChild(parent);
+	grandparent.removeChild(sister);
 }
 
 function ajaxThemeManagerHandleSaveRequest()
 {
-  // Build a JSON condensed request
-  var md = false;
-  if ( document.getElementById('themeed_field_default') )
-  {
-    if ( document.getElementById('themeed_field_default').checked )
-    {
-      md = true;
-    }
-  }
-  var policy = 'allow_all';
-  if ( document.getElementById('themeed_field_policy_whitelist').checked )
-    policy = 'whitelist';
-  else if ( document.getElementById('themeed_field_policy_blacklist').checked )
-    policy = 'blacklist';
-  var json_packet = {
-    theme_id: document.getElementById('themeed_theme_id').value,
-    theme_name: document.getElementById('themeed_field_name').value,
-    default_style: document.getElementById('themeed_field_style').value,
-    make_default: md,
-    group_policy: policy,
-    enabled: ( document.getElementById('themeed_field_disable').checked ? false : true )
-  };
-  var acl_list = [];
-  var checks = document.getElementById('themeed_group_list').getElementsByTagName('input');
-  for ( var i = 0; i < checks.length; i++ )
-  {
-    if ( checks[i].checked )
-      acl_list.push('g:' + checks[i].group_id);
-  }
-  var spans = document.getElementById('themeed_user_list').getElementsByTagName('span');
-  for ( var i = 0; i < spans.length; i++ )
-  {
-    if ( spans[i].userid )
-      acl_list.push('u:' + spans[i].userid);
-  }
-  json_packet.group_list = acl_list;
-  
-  var json_send = {
-    mode: 'save_theme',
-    theme_data: json_packet
-  };
-  
-  json_send = ajaxEscape(toJSONString(json_send));
-  
-  // Request the save
-  var parent = document.getElementById('ajaxPageContainer');
-  ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + json_send, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        // process response
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          // For this we actually *expect* an HTML response.
-          parent.innerHTML = response;
-          return false;
-        }
-        response = parseJSON(response);
-        if ( response.mode == 'error' )
-        {
-          alert(response.error);
-          return false;
-        }
-      }
-    });
+	// Build a JSON condensed request
+	var md = false;
+	if ( document.getElementById('themeed_field_default') )
+	{
+		if ( document.getElementById('themeed_field_default').checked )
+		{
+			md = true;
+		}
+	}
+	var policy = 'allow_all';
+	if ( document.getElementById('themeed_field_policy_whitelist').checked )
+		policy = 'whitelist';
+	else if ( document.getElementById('themeed_field_policy_blacklist').checked )
+		policy = 'blacklist';
+	var json_packet = {
+		theme_id: document.getElementById('themeed_theme_id').value,
+		theme_name: document.getElementById('themeed_field_name').value,
+		default_style: document.getElementById('themeed_field_style').value,
+		make_default: md,
+		group_policy: policy,
+		enabled: ( document.getElementById('themeed_field_disable').checked ? false : true )
+	};
+	var acl_list = [];
+	var checks = document.getElementById('themeed_group_list').getElementsByTagName('input');
+	for ( var i = 0; i < checks.length; i++ )
+	{
+		if ( checks[i].checked )
+			acl_list.push('g:' + checks[i].group_id);
+	}
+	var spans = document.getElementById('themeed_user_list').getElementsByTagName('span');
+	for ( var i = 0; i < spans.length; i++ )
+	{
+		if ( spans[i].userid )
+			acl_list.push('u:' + spans[i].userid);
+	}
+	json_packet.group_list = acl_list;
+	
+	var json_send = {
+		mode: 'save_theme',
+		theme_data: json_packet
+	};
+	
+	json_send = ajaxEscape(toJSONString(json_send));
+	
+	// Request the save
+	var parent = document.getElementById('ajaxPageContainer');
+	ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + json_send, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				// process response
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					// For this we actually *expect* an HTML response.
+					parent.innerHTML = response;
+					return false;
+				}
+				response = parseJSON(response);
+				if ( response.mode == 'error' )
+				{
+					alert(response.error);
+					return false;
+				}
+			}
+		});
 }
 
 function ajaxThemeManagerHandleUninstallClick()
 {
-  var theme_id = document.getElementById('themeed_theme_id').value;
-  var json_send = {
-    mode: 'uninstall',
-    theme_id: theme_id
-  };
-  
-  json_send = ajaxEscape(toJSONString(json_send));
-  
-  // Request the action
-  var parent = document.getElementById('ajaxPageContainer');
-  ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + json_send, function(ajax)
-    {
-      if ( ajax.readyState == 4 && ajax.status == 200 )
-      {
-        // process response
-        var response = String(ajax.responseText + '');
-        if ( !check_json_response(response) )
-        {
-          // For this we actually *expect* an HTML response.
-          parent.innerHTML = response;
-          return false;
-        }
-        response = parseJSON(response);
-        if ( response.mode == 'error' )
-        {
-          alert(response.error);
-          return false;
-        }
-      }
-    });
+	var theme_id = document.getElementById('themeed_theme_id').value;
+	var json_send = {
+		mode: 'uninstall',
+		theme_id: theme_id
+	};
+	
+	json_send = ajaxEscape(toJSONString(json_send));
+	
+	// Request the action
+	var parent = document.getElementById('ajaxPageContainer');
+	ajaxPost(makeUrlNS('Admin', 'ThemeManager/action.json'), 'r=' + json_send, function(ajax)
+		{
+			if ( ajax.readyState == 4 && ajax.status == 200 )
+			{
+				// process response
+				var response = String(ajax.responseText + '');
+				if ( !check_json_response(response) )
+				{
+					// For this we actually *expect* an HTML response.
+					parent.innerHTML = response;
+					return false;
+				}
+				response = parseJSON(response);
+				if ( response.mode == 'error' )
+				{
+					alert(response.error);
+					return false;
+				}
+			}
+		});
 }
--- a/includes/clientside/static/tinymce-init.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/tinymce-init.js	Sun Mar 28 23:10:46 2010 -0400
@@ -5,27 +5,27 @@
 // Check tinyMCE to make sure its init is finished
 var initTinyMCE = function(e)
 {
-  if ( typeof(tinyMCE_GZ) == 'object' )
-  {
-    if ( !KILL_SWITCH && !DISABLE_MCE )
-    {
-      tinyMCE_GZ.init(enano_tinymce_gz_options, function()
-        {
-          tinyMCE.init(enano_tinymce_options);
-        });
-      tinymce_initted = true;
-    }
-  }
+	if ( typeof(tinyMCE_GZ) == 'object' )
+	{
+		if ( !KILL_SWITCH && !DISABLE_MCE )
+		{
+			tinyMCE_GZ.init(enano_tinymce_gz_options, function()
+				{
+					tinyMCE.init(enano_tinymce_options);
+				});
+			tinymce_initted = true;
+		}
+	}
 };
 
 // editor options
 if ( document.getElementById('mdgCss') )
 {
-  var css_url = document.getElementById('mdgCss').href;
+	var css_url = document.getElementById('mdgCss').href;
 }
 else
 {
-  var css_url = scriptPath + '/includes/clientside/css/enano_shared.css';
+	var css_url = scriptPath + '/includes/clientside/css/enano_shared.css';
 }
 
 var do_popups = ( is_Safari ) ? '' : ',inlinepopups';
@@ -33,21 +33,21 @@
 var tinymce_initted = false;
 
 var enano_tinymce_options = {
-  mode : "none",
-  plugins : 'table,save,safari,pagebreak,style,layer,advhr,insertdatetime,searchreplace,spellchecker,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,wordcount' + do_popups,
-  theme : 'advanced',
-  skin : _skin,
-  theme_advanced_resize_horizontal : false,
-  theme_advanced_resizing : true,
-  theme_advanced_toolbar_location : "top",
-  theme_advanced_toolbar_align : "left",
-  theme_advanced_buttons1 : "save,|,bold,italic,underline,strikethrough,|,spellchecker,|,justifyleft,justifycenter,justifyright,justifyfull,|,forecolor,backcolor,|,formatselect,|,fontselect,fontsizeselect",
-  theme_advanced_buttons3_add_before : "tablecontrols,separator",
-  theme_advanced_buttons3_add_after : "|,fullscreen",
-  theme_advanced_statusbar_location : 'bottom',
-  noneditable_noneditable_class : 'mce_readonly',
-  content_css : css_url,
-  spellchecker_rpc_url : scriptPath + '/includes/clientside/tinymce/plugins/spellchecker/rpc.php'
+	mode : "none",
+	plugins : 'table,save,safari,pagebreak,style,layer,advhr,insertdatetime,searchreplace,spellchecker,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,wordcount' + do_popups,
+	theme : 'advanced',
+	skin : _skin,
+	theme_advanced_resize_horizontal : false,
+	theme_advanced_resizing : true,
+	theme_advanced_toolbar_location : "top",
+	theme_advanced_toolbar_align : "left",
+	theme_advanced_buttons1 : "save,|,bold,italic,underline,strikethrough,|,spellchecker,|,justifyleft,justifycenter,justifyright,justifyfull,|,forecolor,backcolor,|,formatselect,|,fontselect,fontsizeselect",
+	theme_advanced_buttons3_add_before : "tablecontrols,separator",
+	theme_advanced_buttons3_add_after : "|,fullscreen",
+	theme_advanced_statusbar_location : 'bottom',
+	noneditable_noneditable_class : 'mce_readonly',
+	content_css : css_url,
+	spellchecker_rpc_url : scriptPath + '/includes/clientside/tinymce/plugins/spellchecker/rpc.php'
 };
 
 var enano_tinymce_gz_options = {
@@ -62,10 +62,10 @@
 
 if ( !KILL_SWITCH && !DISABLE_MCE )
 {
-  var uri = scriptPath + '/includes/clientside/tinymce/tiny_mce_gzip.js?327';
-  var sc = document.createElement('script');
-  sc.src = uri;
-  sc.type = 'text/javascript';
-  var head = document.getElementsByTagName('head')[0];
-  head.appendChild(sc);
+	var uri = scriptPath + '/includes/clientside/tinymce/tiny_mce_gzip.js?327';
+	var sc = document.createElement('script');
+	sc.src = uri;
+	sc.type = 'text/javascript';
+	var head = document.getElementsByTagName('head')[0];
+	head.appendChild(sc);
 }
--- a/includes/clientside/static/toolbar.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/toolbar.js	Sun Mar 28 23:10:46 2010 -0400
@@ -2,62 +2,62 @@
 
 window.unselectAllButtonsMajor = function()
 {
-  if ( !document.getElementById('pagebar_main') )
-    return false;
-  obj = document.getElementById('pagebar_main').firstChild;
-  while(obj)
-  {
-    if(obj.id == 'mdgToolbar_article' || obj.id == 'mdgToolbar_discussion')
-    {
-      $dynano(obj).rmClass('selected');
-    }
-    obj = obj.nextSibling;
-  }
+	if ( !document.getElementById('pagebar_main') )
+		return false;
+	obj = document.getElementById('pagebar_main').firstChild;
+	while(obj)
+	{
+		if(obj.id == 'mdgToolbar_article' || obj.id == 'mdgToolbar_discussion')
+		{
+			$dynano(obj).rmClass('selected');
+		}
+		obj = obj.nextSibling;
+	}
 }
 
 window.unselectAllButtonsMinor = function()
 {
-  if ( !document.getElementById('pagebar_main') )
-    return false;
-  obj = document.getElementById('pagebar_main').firstChild.nextSibling;
-  while(obj)
-  {
-    if ( !$dynano(obj).hasClass('selected') )
-    {
-      obj = obj.nextSibling;
-      continue;
-    }
-    if(obj.id != 'mdgToolbar_article' && obj.id != 'mdgToolbar_discussion')
-    {
-      if ( obj.className )
-        $dynano(obj).rmClass('selected');
-    }
-    obj = obj.nextSibling;
-  }
+	if ( !document.getElementById('pagebar_main') )
+		return false;
+	obj = document.getElementById('pagebar_main').firstChild.nextSibling;
+	while(obj)
+	{
+		if ( !$dynano(obj).hasClass('selected') )
+		{
+			obj = obj.nextSibling;
+			continue;
+		}
+		if(obj.id != 'mdgToolbar_article' && obj.id != 'mdgToolbar_discussion')
+		{
+			if ( obj.className )
+				$dynano(obj).rmClass('selected');
+		}
+		obj = obj.nextSibling;
+	}
 }
 
 window.selectButtonMajor = function(which)
 {
-  if ( !document.getElementById('pagebar_main') )
-    return false;
-  var dom = document.getElementById('mdgToolbar_'+which);
-  if ( !dom )
-    return false;
-  if(typeof(dom) == 'object')
-  {
-    unselectAllButtonsMajor();
-    $dynano('mdgToolbar_'+which).addClass('selected');
-  }
+	if ( !document.getElementById('pagebar_main') )
+		return false;
+	var dom = document.getElementById('mdgToolbar_'+which);
+	if ( !dom )
+		return false;
+	if(typeof(dom) == 'object')
+	{
+		unselectAllButtonsMajor();
+		$dynano('mdgToolbar_'+which).addClass('selected');
+	}
 }
 
 window.selectButtonMinor = function(which)
 {
-  if ( !document.getElementById('pagebar_main') )
-    return false;
-  if(typeof(document.getElementById('mdgToolbar_'+which)) == 'object')
-  {
-    unselectAllButtonsMinor();
-    $dynano('mdgToolbar_'+which).addClass('selected');
-  }
+	if ( !document.getElementById('pagebar_main') )
+		return false;
+	if(typeof(document.getElementById('mdgToolbar_'+which)) == 'object')
+	{
+		unselectAllButtonsMinor();
+		$dynano('mdgToolbar_'+which).addClass('selected');
+	}
 }
 
--- a/includes/clientside/static/userpage.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/userpage.js	Sun Mar 28 23:10:46 2010 -0400
@@ -4,58 +4,58 @@
 
 var userpage_onload = function()
 {
-  var wrapper = document.getElementById('userpage_wrap');
-  var links = document.getElementById('userpage_links');
-  
-  if ( !wrapper )
-    return false;
-  
-  wrapper.className = 'userpage_wrap';
-  links.className = 'userpage_links';
-  
-  var blocks = wrapper.getElementsByTagName('div');
-  var first_block = false;
-  for ( var i = 0; i < blocks.length; i++ )
-  {
-    var block = blocks[i];
-    if ( /^tab:/.test(block.id) )
-    {
-      $dynano(block).addClass('userpage_block');
-      var block_id = block.id.substr(4);
-      userpage_blocks.push(block_id);
-      if ( !first_block )
-      {
-        // this is the first block on the page, memorize it
-        first_block = block_id;
-      }
-    }
-  }
-  // init links
-  var as = links.getElementsByTagName('a');
-  for ( var i = 0; i < as.length; i++ )
-  {
-    var a = as[i];
-    if ( a.href.indexOf('#') > -1 )
-    {
-      var hash = a.href.substr(a.href.indexOf('#'));
-      var blockid = hash.substr(5);
-      a.blockid = blockid;
-      a.onclick = function()
-      {
-        userpage_select_block(this.blockid);
-        return false;
-      }
-      a.id = 'userpage_blocklink_' + blockid;
-    }
-  }
-  if ( $_REQUEST['tab'] )
-  {
-    userpage_select_block($_REQUEST['tab'], true);
-  }
-  else
-  {
-    userpage_select_block(first_block, true);
-  }
+	var wrapper = document.getElementById('userpage_wrap');
+	var links = document.getElementById('userpage_links');
+	
+	if ( !wrapper )
+		return false;
+	
+	wrapper.className = 'userpage_wrap';
+	links.className = 'userpage_links';
+	
+	var blocks = wrapper.getElementsByTagName('div');
+	var first_block = false;
+	for ( var i = 0; i < blocks.length; i++ )
+	{
+		var block = blocks[i];
+		if ( /^tab:/.test(block.id) )
+		{
+			$dynano(block).addClass('userpage_block');
+			var block_id = block.id.substr(4);
+			userpage_blocks.push(block_id);
+			if ( !first_block )
+			{
+				// this is the first block on the page, memorize it
+				first_block = block_id;
+			}
+		}
+	}
+	// init links
+	var as = links.getElementsByTagName('a');
+	for ( var i = 0; i < as.length; i++ )
+	{
+		var a = as[i];
+		if ( a.href.indexOf('#') > -1 )
+		{
+			var hash = a.href.substr(a.href.indexOf('#'));
+			var blockid = hash.substr(5);
+			a.blockid = blockid;
+			a.onclick = function()
+			{
+				userpage_select_block(this.blockid);
+				return false;
+			}
+			a.id = 'userpage_blocklink_' + blockid;
+		}
+	}
+	if ( $_REQUEST['tab'] )
+	{
+		userpage_select_block($_REQUEST['tab'], true);
+	}
+	else
+	{
+		userpage_select_block(first_block, true);
+	}
 }
 
 addOnloadHook(userpage_onload);
@@ -68,53 +68,53 @@
 
 function userpage_select_block(block, nofade)
 {
-  // memorize existing scroll position, reset the hash, then scroll back to where we were
-  // a little hackish and might cause a flash, but it's better than hiding the tabs on each click
-  var currentScroll = getScrollOffset();
-  
-  var current_block = false;
-  nofade = true;
-  for ( var i = 0; i < userpage_blocks.length; i++ )
-  {
-    var div = document.getElementById('tab:' + userpage_blocks[i]);
-    if ( div )
-    {
-      if ( div.style.display != 'none' )
-      {
-        current_block = userpage_blocks[i];
-        if ( nofade || aclDisableTransitionFX )
-        {
-          div.style.display = 'none';
-        }
-      }
-    }
-    var a = document.getElementById('userpage_blocklink_' + userpage_blocks[i]);
-    if ( a )
-    {
-      if ( $dynano(a.parentNode).hasClass('userpage_tab_active') )
-      {
-        $dynano(a.parentNode).rmClass('userpage_tab_active');
-      }
-    }
-  }
-  if ( nofade || !current_block || aclDisableTransitionFX )
-  {
-    var div = document.getElementById('tab:' + block);
-    div.style.display = 'block';
-  }
-  else
-  {
-    // DISABLED: see "nofade = true;" above.
-    // do this in a slightly fancier fashion
-    load_component(['jquery', 'jquery-ui']);
-    $('#tab:' + current_block).hide("blind", {}, 500, function()
-    {
-      $('#tab:' + block).show("blind", {}, 500);
-    });
-  }
-  var a = document.getElementById('userpage_blocklink_' + block);
-  $dynano(a.parentNode).addClass('userpage_tab_active');
-  
-  window.location.hash = 'tab:' + block;
-  setScrollOffset(currentScroll);
+	// memorize existing scroll position, reset the hash, then scroll back to where we were
+	// a little hackish and might cause a flash, but it's better than hiding the tabs on each click
+	var currentScroll = getScrollOffset();
+	
+	var current_block = false;
+	nofade = true;
+	for ( var i = 0; i < userpage_blocks.length; i++ )
+	{
+		var div = document.getElementById('tab:' + userpage_blocks[i]);
+		if ( div )
+		{
+			if ( div.style.display != 'none' )
+			{
+				current_block = userpage_blocks[i];
+				if ( nofade || aclDisableTransitionFX )
+				{
+					div.style.display = 'none';
+				}
+			}
+		}
+		var a = document.getElementById('userpage_blocklink_' + userpage_blocks[i]);
+		if ( a )
+		{
+			if ( $dynano(a.parentNode).hasClass('userpage_tab_active') )
+			{
+				$dynano(a.parentNode).rmClass('userpage_tab_active');
+			}
+		}
+	}
+	if ( nofade || !current_block || aclDisableTransitionFX )
+	{
+		var div = document.getElementById('tab:' + block);
+		div.style.display = 'block';
+	}
+	else
+	{
+		// DISABLED: see "nofade = true;" above.
+		// do this in a slightly fancier fashion
+		load_component(['jquery', 'jquery-ui']);
+		$('#tab:' + current_block).hide("blind", {}, 500, function()
+		{
+			$('#tab:' + block).show("blind", {}, 500);
+		});
+	}
+	var a = document.getElementById('userpage_blocklink_' + block);
+	$dynano(a.parentNode).addClass('userpage_tab_active');
+	
+	window.location.hash = 'tab:' + block;
+	setScrollOffset(currentScroll);
 }
--- a/includes/clientside/static/windows.js	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/clientside/static/windows.js	Sun Mar 28 23:10:46 2010 -0400
@@ -5,131 +5,131 @@
  * Yes, it's part of Enano, so it's GPL
  */
 
-  var position;
-  position = getScrollOffset();
-  
-  var jws = {
-    position : position,
-    obj : null,
-    startup : function() {
-      jws.debug('jws.startup()');
-      var divs = document.getElementsByTagName('div');
-      if(IE) { position = document.body.scrollTop; }
-      else   { position = window.pageYOffset; }
-      for(i=0;i<divs.length;i++) {
-        if(divs[i].id && divs[i].id.substr(0, 4) == 'root') {
-          divs[i].onClick = 'jws.focus(\''+divs[i].id+'\')';
-          var tb = i + 1
-          tb = divs[tb];
-          tb.innerHTML = '<table border="0" width="100%"><tr><td>' + tb.innerHTML + '</td><td align="right"><div align="center" class="closebtn" onclick="jws.closeWin(\''+divs[i].id+'\');">X</div></td></tr></table>';
-          divs[i].style.width = '640px';
-          divs[i].style.height = '480px';
-          Drag.init(tb, divs[i]);
-        }
-      }
-    },
-    initWindow : function(o) {
-      jws.debug('jws.initWindow('+o+' ['+o.id+'])');
-      var divs = document.getElementsByTagName('div');
-      for(i=0;i<divs.length;i++) {
-        if(divs[i].id && divs[i].id == o.id) {
-          var tb = i + 1
-          tb = divs[tb];
-          tb.innerHTML = '<table border="0" width="100%"><tr><td>' + tb.innerHTML + '</td><td align="right"><div class="closebtn" onclick="jws.closeWin(\''+divs[i].id+'\');"></div></td></tr></table>';
-          divs[i].style.width = '640px';
-          divs[i].style.height = '480px';
-          Drag.init(tb, divs[i]);
-        }
-      }
-    },
-    closeWin : function(id) {
-      jws.debug('jws.closeWin(\''+id+'\')');
-      document.getElementById(id).style.display = 'none';
-      enlighten();
-    },
-    openWin : function(id, x, y) {
-      darken();
-      var e = document.getElementById(id);
-      if(!x) x = 640;
-      if(!y) y = 480;
-      jws.debug('jws.openWin(\''+id+'\', '+x+', '+y+')');
-      e.style.display = 'block';
-      e.style.width   = x+'px';
-      e.style.height  = y+'px';
-      
-      var divs = document.getElementsByTagName('div');
-      for(i=0;i<divs.length;i++) {
-        if(divs[i].id && divs[i].id == e.id) {
-          var cn = i + 3;
-          cn = divs[cn];
-          
-          var h = getElementHeight(e.id) - 53;
-          var w = getElementWidth(cn.id) - 20;
-          cn.style.width   =  w + 'px';
-          cn.style.height  =  h + 'px';
-          cn.style.clip.top = 0 + 'px';
-          cn.style.clip.left = 0 + 'px';
-          cn.style.clip.right =  w + 'px';
-          cn.style.clip.bottom = h + 'px';
-          cn.style.overflow = 'auto';
-        }
-      }
-      jws.setpos(id);
-      jws.focus(id);
-    },
-    setpos : function(el) {
-      jws.debug('jws.setpos(\''+el+'\')');
-      el = document.getElementById(el);
-      var w = getWidth();
-      var h = getHeight();
-      var ew = getElementWidth(el.id);
-      var eh = getElementHeight(el.id);
-      px = (w/2) - (ew/2);
-      py = (h/2) - (eh/2);
-      if (IE) { position = document.body.scrollTop; }
-      else    { position = window.pageYOffset; }
-      py=py+0;
-      if ( IE )
-        el.style.position = "absolute";
-      else
-        el.style.position = "fixed";
-      el.style.left=px+'px';
-      el.style.top =py+'px';
-    },
-    scrollHandler : function() {
-      var divs = document.getElementsByTagName('div');
-      for(i=0;i<divs.length;i++) {
-        if(divs[i].id && divs[i].id.substr(0, 4) == 'root' && divs[i].style.display == 'block') {
-          c = divs[i];
-          jws.debug('jws.scrollHandler(): moving element: '+c.id);
-          var t = c.style.top;
-          var py = t.substr(0, t.length - 2);
-          py = parseInt(py);
-          if(jws.position) { py = py - jws.position; }
-          position = getScrollOffset();
-          py=py+position;                                                           
-          c.style.position = "absolute";
-          if(!isNaN(py)) c.style.top =py+'px';
-          jws.debug('jws.scrollHandler(): value of py: '+py);
-        }
-      }
-      jws.position = position;
-    },
-    focus : function(e) {
-      e = document.getElementById(e);
-      if(e.style.zindex) z = e.style.zindex;
-      else z = 1;
-      z=z+5;
-      e.style.zIndex = z;
-    },
-    debug : function(t) {
-      if(document.getElementById('jsw-debug-console')) {
-        dbg = document.getElementById('jsw-debug-console');
-        debugdata = dbg.innerHTML;
-        dbg.innerHTML = debugdata+"<br />"+t;
-      }
-    }
-  } // class jws
+	var position;
+	position = getScrollOffset();
+	
+	var jws = {
+		position : position,
+		obj : null,
+		startup : function() {
+			jws.debug('jws.startup()');
+			var divs = document.getElementsByTagName('div');
+			if(IE) { position = document.body.scrollTop; }
+			else   { position = window.pageYOffset; }
+			for(i=0;i<divs.length;i++) {
+				if(divs[i].id && divs[i].id.substr(0, 4) == 'root') {
+					divs[i].onClick = 'jws.focus(\''+divs[i].id+'\')';
+					var tb = i + 1
+					tb = divs[tb];
+					tb.innerHTML = '<table border="0" width="100%"><tr><td>' + tb.innerHTML + '</td><td align="right"><div align="center" class="closebtn" onclick="jws.closeWin(\''+divs[i].id+'\');">X</div></td></tr></table>';
+					divs[i].style.width = '640px';
+					divs[i].style.height = '480px';
+					Drag.init(tb, divs[i]);
+				}
+			}
+		},
+		initWindow : function(o) {
+			jws.debug('jws.initWindow('+o+' ['+o.id+'])');
+			var divs = document.getElementsByTagName('div');
+			for(i=0;i<divs.length;i++) {
+				if(divs[i].id && divs[i].id == o.id) {
+					var tb = i + 1
+					tb = divs[tb];
+					tb.innerHTML = '<table border="0" width="100%"><tr><td>' + tb.innerHTML + '</td><td align="right"><div class="closebtn" onclick="jws.closeWin(\''+divs[i].id+'\');"></div></td></tr></table>';
+					divs[i].style.width = '640px';
+					divs[i].style.height = '480px';
+					Drag.init(tb, divs[i]);
+				}
+			}
+		},
+		closeWin : function(id) {
+			jws.debug('jws.closeWin(\''+id+'\')');
+			document.getElementById(id).style.display = 'none';
+			enlighten();
+		},
+		openWin : function(id, x, y) {
+			darken();
+			var e = document.getElementById(id);
+			if(!x) x = 640;
+			if(!y) y = 480;
+			jws.debug('jws.openWin(\''+id+'\', '+x+', '+y+')');
+			e.style.display = 'block';
+			e.style.width   = x+'px';
+			e.style.height  = y+'px';
+			
+			var divs = document.getElementsByTagName('div');
+			for(i=0;i<divs.length;i++) {
+				if(divs[i].id && divs[i].id == e.id) {
+					var cn = i + 3;
+					cn = divs[cn];
+					
+					var h = getElementHeight(e.id) - 53;
+					var w = getElementWidth(cn.id) - 20;
+					cn.style.width   =  w + 'px';
+					cn.style.height  =  h + 'px';
+					cn.style.clip.top = 0 + 'px';
+					cn.style.clip.left = 0 + 'px';
+					cn.style.clip.right =  w + 'px';
+					cn.style.clip.bottom = h + 'px';
+					cn.style.overflow = 'auto';
+				}
+			}
+			jws.setpos(id);
+			jws.focus(id);
+		},
+		setpos : function(el) {
+			jws.debug('jws.setpos(\''+el+'\')');
+			el = document.getElementById(el);
+			var w = getWidth();
+			var h = getHeight();
+			var ew = getElementWidth(el.id);
+			var eh = getElementHeight(el.id);
+			px = (w/2) - (ew/2);
+			py = (h/2) - (eh/2);
+			if (IE) { position = document.body.scrollTop; }
+			else    { position = window.pageYOffset; }
+			py=py+0;
+			if ( IE )
+				el.style.position = "absolute";
+			else
+				el.style.position = "fixed";
+			el.style.left=px+'px';
+			el.style.top =py+'px';
+		},
+		scrollHandler : function() {
+			var divs = document.getElementsByTagName('div');
+			for(i=0;i<divs.length;i++) {
+				if(divs[i].id && divs[i].id.substr(0, 4) == 'root' && divs[i].style.display == 'block') {
+					c = divs[i];
+					jws.debug('jws.scrollHandler(): moving element: '+c.id);
+					var t = c.style.top;
+					var py = t.substr(0, t.length - 2);
+					py = parseInt(py);
+					if(jws.position) { py = py - jws.position; }
+					position = getScrollOffset();
+					py=py+position;                                                           
+					c.style.position = "absolute";
+					if(!isNaN(py)) c.style.top =py+'px';
+					jws.debug('jws.scrollHandler(): value of py: '+py);
+				}
+			}
+			jws.position = position;
+		},
+		focus : function(e) {
+			e = document.getElementById(e);
+			if(e.style.zindex) z = e.style.zindex;
+			else z = 1;
+			z=z+5;
+			e.style.zIndex = z;
+		},
+		debug : function(t) {
+			if(document.getElementById('jsw-debug-console')) {
+				dbg = document.getElementById('jsw-debug-console');
+				debugdata = dbg.innerHTML;
+				dbg.innerHTML = debugdata+"<br />"+t;
+			}
+		}
+	} // class jws
 
 //window.onscroll=jws['scrollHandler'];
 
@@ -145,114 +145,114 @@
 
 var Drag = {
 
-  obj : null,
+	obj : null,
 
-  init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
-  {
-    o.onmousedown	= Drag.start;
+	init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
+	{
+		o.onmousedown	= Drag.start;
 
-    o.hmode			= bSwapHorzRef ? false : true ;
-    o.vmode			= bSwapVertRef ? false : true ;
+		o.hmode			= bSwapHorzRef ? false : true ;
+		o.vmode			= bSwapVertRef ? false : true ;
 
-    o.root = oRoot && oRoot != null ? oRoot : o ;
+		o.root = oRoot && oRoot != null ? oRoot : o ;
 
-    if (o.hmode  && isNaN(parseInt(o.root.style.left  ))) o.root.style.left   = "0px";
-    if (o.vmode  && isNaN(parseInt(o.root.style.top   ))) o.root.style.top    = "0px";
-    if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
-    if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";
+		if (o.hmode  && isNaN(parseInt(o.root.style.left  ))) o.root.style.left   = "0px";
+		if (o.vmode  && isNaN(parseInt(o.root.style.top   ))) o.root.style.top    = "0px";
+		if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
+		if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";
 
-    o.minX	= typeof minX != 'undefined' ? minX : null;
-    o.minY	= typeof minY != 'undefined' ? minY : null;
-    o.maxX	= typeof maxX != 'undefined' ? maxX : null;
-    o.maxY	= typeof maxY != 'undefined' ? maxY : null;
+		o.minX	= typeof minX != 'undefined' ? minX : null;
+		o.minY	= typeof minY != 'undefined' ? minY : null;
+		o.maxX	= typeof maxX != 'undefined' ? maxX : null;
+		o.maxY	= typeof maxY != 'undefined' ? maxY : null;
 
-    o.xMapper = fXMapper ? fXMapper : null;
-    o.yMapper = fYMapper ? fYMapper : null;
+		o.xMapper = fXMapper ? fXMapper : null;
+		o.yMapper = fYMapper ? fYMapper : null;
 
-    o.root.onDragStart	= new Function();
-    o.root.onDragEnd	= new Function();
-    o.root.onDrag		= new Function();
-  },
+		o.root.onDragStart	= new Function();
+		o.root.onDragEnd	= new Function();
+		o.root.onDrag		= new Function();
+	},
 
-  start : function(e)
-  {
-    var o = Drag.obj = this;
-    e = Drag.fixE(e);
-    var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
-    var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
-    o.root.onDragStart(x, y);
+	start : function(e)
+	{
+		var o = Drag.obj = this;
+		e = Drag.fixE(e);
+		var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
+		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
+		o.root.onDragStart(x, y);
 
-    o.lastMouseX	= e.clientX;
-    o.lastMouseY	= e.clientY;
+		o.lastMouseX	= e.clientX;
+		o.lastMouseY	= e.clientY;
 
-    if (o.hmode) {
-      if (o.minX != null)	o.minMouseX	= e.clientX - x + o.minX;
-      if (o.maxX != null)	o.maxMouseX	= o.minMouseX + o.maxX - o.minX;
-    } else {
-      if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
-      if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
-    }
+		if (o.hmode) {
+			if (o.minX != null)	o.minMouseX	= e.clientX - x + o.minX;
+			if (o.maxX != null)	o.maxMouseX	= o.minMouseX + o.maxX - o.minX;
+		} else {
+			if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
+			if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
+		}
 
-    if (o.vmode) {
-      if (o.minY != null)	o.minMouseY	= e.clientY - y + o.minY;
-      if (o.maxY != null)	o.maxMouseY	= o.minMouseY + o.maxY - o.minY;
-    } else {
-      if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
-      if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
-    }
+		if (o.vmode) {
+			if (o.minY != null)	o.minMouseY	= e.clientY - y + o.minY;
+			if (o.maxY != null)	o.maxMouseY	= o.minMouseY + o.maxY - o.minY;
+		} else {
+			if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
+			if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
+		}
 
-    document.onmousemove	= Drag.drag;
-    document.onmouseup		= Drag.end;
+		document.onmousemove	= Drag.drag;
+		document.onmouseup		= Drag.end;
 
-    return false;
-  },
+		return false;
+	},
 
-  drag : function(e)
-  {
-    e = Drag.fixE(e);
-    var o = Drag.obj;
+	drag : function(e)
+	{
+		e = Drag.fixE(e);
+		var o = Drag.obj;
 
-    var ey	= e.clientY;
-    var ex	= e.clientX;
-    var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
-    var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
-    var nx, ny;
+		var ey	= e.clientY;
+		var ex	= e.clientX;
+		var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
+		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
+		var nx, ny;
 
-    if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
-    if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
-    if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
-    if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);
+		if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
+		if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
+		if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
+		if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);
 
-    nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
-    ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
+		nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
+		ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));
 
-    if (o.xMapper)		nx = o.xMapper(y)
-    else if (o.yMapper)	ny = o.yMapper(x)
+		if (o.xMapper)		nx = o.xMapper(y)
+		else if (o.yMapper)	ny = o.yMapper(x)
 
-    Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
-    Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
-    Drag.obj.lastMouseX	= ex;
-    Drag.obj.lastMouseY	= ey;
+		Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
+		Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
+		Drag.obj.lastMouseX	= ex;
+		Drag.obj.lastMouseY	= ey;
 
-    Drag.obj.root.onDrag(nx, ny);
-    return false;
-  },
+		Drag.obj.root.onDrag(nx, ny);
+		return false;
+	},
 
-  end : function()
-  {
-    document.onmousemove = getMouseXY;
-    document.onmouseup   = null;
-    Drag.obj.root.onDragEnd(	parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]), 
-                  parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
-    Drag.obj = null;
-  },
+	end : function()
+	{
+		document.onmousemove = getMouseXY;
+		document.onmouseup   = null;
+		Drag.obj.root.onDragEnd(	parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]), 
+									parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
+		Drag.obj = null;
+	},
 
-  fixE : function(e)
-  {
-    if (typeof e == 'undefined') e = window.event;
-    if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
-    if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
-    return e;
-  }
+	fixE : function(e)
+	{
+		if (typeof e == 'undefined') e = window.event;
+		if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
+		if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
+		return e;
+	}
 };
 
--- a/includes/comment.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/comment.php	Sun Mar 28 23:10:46 2010 -0400
@@ -20,466 +20,466 @@
 
 class Comments
 {
-  #
-  # VARIABLES
-  #
-  
-  /**
-   * Current list of comments.
-   * @var array
-   */
-  
-  var $comments = Array();
-  
-  /**
-   * Object to track permissions.
-   * @var object
-   */
-  
-  var $perms;
-  
-  #
-  # METHODS
-  #
-  
-  /**
-   * Constructor.
-   * @param string Page ID of the page to load comments for
-   * @param string Namespace of the page to load comments for
-   */
-  
-  function __construct($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Initialize permissions
-    if ( $page_id == $paths->page_id && $namespace == $paths->namespace )
-      $this->perms =& $GLOBALS['session'];
-    else
-      $this->perms = $session->fetch_page_acl($page_id, $namespace);
-    
-    $this->page_id = $db->escape($page_id);
-    $this->namespace = $db->escape($namespace);
-  }
-  
-  /**
-   * Processes a command in JSON format.
-   * @param mixed Either the JSON-encoded input string, probably something sent from the Javascript/AJAX frontend, or an equivalent array
-   */
-   
-  function process_json($json)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $is_json = !is_array($json);
-    
-    if ( $is_json )
-    {
-      $data = enano_json_decode($json);
-      $data = decode_unicode_array($data);
-    }
-    else
-    {
-      $data =& $json;
-    }
-    if ( !isset($data['mode']) )
-    {
-      $ret = Array('mode'=>'error','error'=>'No mode defined!');
-      echo enano_json_encode($ret);
-      return $ret;
-    }
-    if ( getConfig('enable_comments', '1') == '0' )
-    {
-      $ret = Array('mode'=>'error','error'=>'Comments are not enabled on this site.');
-      echo enano_json_encode($ret);
-      return $ret;
-    }
-    $ret = Array();
-    $ret['mode'] = $data['mode'];
-    if ( isset($data['passback']) )
-      $ret['passback'] = $data['passback'];
-    switch ( $data['mode'] )
-    {
-      case 'fetch':
-        if ( !$template->theme_loaded )
-          $template->load_theme();
-        if ( !isset($data['have_template']) )
-        {
-          $ret['template'] = file_get_contents(ENANO_ROOT . '/themes/' . $template->theme . '/comment.tpl');
-        }
-        $approve_clause = $this->perms->get_permissions('mod_comments') ? '' : " AND approved = " . COMMENT_APPROVED;
-        // Get totals
-        $q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$this->page_id' AND namespace = '$this->namespace'{$approve_clause};");
-        if ( !$q )
-          $db->die_json();
-        $counts = array('total' => 0, 'approved' => 0, 'unapproved' => 0, 'spam' => 0);
-        while ( $row = $db->fetchrow() )
-        {
-          $counts['total']++;
-          switch($row['approved']):
-            case COMMENT_APPROVED:   $counts['approved']++;   break;
-            case COMMENT_UNAPPROVED: $counts['unapproved']++; break;
-            case COMMENT_SPAM:       $counts['spam']++;       break;
-          endswitch;
-        }
-        $counts['unapproved'] = $counts['total'] - $counts['approved'];
-        $data['counts'] = $counts;
-        // FIXME, this should be a user preference eventually
-        $ret['per_page'] = $per_page = getConfig('comments_per_page', 10);
-        $page = ( !empty($data['pagenum']) ) ? intval($data['pagenum']) : 0;
-        if ( $page > 0 )
-        {
-          $ret['mode'] = 'refetch';
-        }
-        $limit_clause = "LIMIT $per_page OFFSET " . ($page * $per_page);
-        $q = $db->sql_query('SELECT c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,( c.ip_address IS NOT NULL ) AS have_ip,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type, b.buddy_id IS NOT NULL AS is_buddy, ( b.is_friend IS NOT NULL AND b.is_friend=1 ) AS is_friend FROM '.table_prefix.'comments AS c
-                               LEFT JOIN '.table_prefix.'users AS u
-                                 ON (u.user_id=c.user_id)
-                               LEFT JOIN '.table_prefix.'buddies AS b
-                                 ON ( ( b.user_id=' . $session->user_id.' AND b.buddy_user_id=c.user_id ) OR b.user_id IS NULL)
-                               LEFT JOIN '.table_prefix.'ranks AS r
-                                 ON ( ( u.user_rank = r.rank_id ) )
-                               WHERE page_id=\'' . $this->page_id . '\'
-                                 AND namespace=\'' . $this->namespace . '\'
-                                 ' . $approve_clause . '
-                               GROUP BY c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,c.ip_address,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type,b.buddy_id,b.is_friend
-                               ORDER BY c.time ASC
-                               ' . $limit_clause . ';');
-        $ret['comments'] = Array();
-        if (!$q)
-          $db->die_json();
-        if ( $row = $db->fetchrow($q) )
-        {
-          do {
-            
-            if ( !$this->perms->get_permissions('mod_comments') && $row['approved'] != COMMENT_APPROVED )
-              continue;
-            
-            // Localize the rank
-            $row = array_merge($row, $session->get_user_rank(intval($row['user_id'])));
-            
-            // Send the source
-            $row['comment_source'] = $row['comment_data'];
-            
-            // Format text
-            $row['comment_data'] = RenderMan::render($row['comment_data']);
-            
-            // Hide it if it's a post from a foe
-            if ( $row['is_buddy'] == 1 && $row['is_friend'] == 0 )
-            {
-              $seed = md5(sha1(mt_rand() . microtime()));
-              $wrapper = '
-                <div id="posthide_'.$seed.'" style="display: none;">
-                  ' . $row['comment_data'] . '
-                </div>
-                <p><span style="opacity: 0.4; filter: alpha(opacity=40);">' . $lang->get('comment_msg_foe_comment_hidden') . '</span> <span style="text-align: right;"><a href="#showpost" onclick="document.getElementById(\'posthide_'.$seed.'\').style.display=\'block\'; this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode); return false;">' . $lang->get('comment_btn_display_foe_comment') . '</a></span></p>
-              ';
-              $row['comment_data'] = $wrapper;
-            }
-            
-            // Format date
-            $row['time'] = enano_date(ED_DATE | ED_TIME, $row['time']);
-            
-            // Format signature
-            $row['signature'] = ( !empty($row['signature']) ) ? RenderMan::render($row['signature']) : '';
-            
-            // Do we have the IP?
-            $row['have_ip'] = ( $row['have_ip'] == 1 );
-            
-            // Avatar URL
-            $row['avatar_path'] = make_avatar_url($row['user_id'], $row['avatar_type'], $row['email']);
-            
-            // Add the comment to the list
-            $ret['comments'][] = $row;
-            
-          } while ( $row = $db->fetchrow($q) );
-        }
-        $db->free_result();
-        $ret['count_appr'] = $counts['approved'];
-        $ret['count_total'] = $counts['total'];
-        $ret['count_visible'] = $this->perms->get_permissions('mod_comments') ? $counts['total'] : $counts['approved'];
-        $ret['count_unappr'] = $counts['unapproved'];
-        $ret['auth_mod_comments'] = $this->perms->get_permissions('mod_comments');
-        $ret['auth_post_comments'] = $this->perms->get_permissions('post_comments');
-        $ret['auth_edit_comments'] = $this->perms->get_permissions('edit_comments');
-        $ret['auth_edit_wysiwyg'] = $this->perms->get_permissions('edit_wysiwyg');
-        $ret['user_id'] = $session->user_id;
-        $ret['username'] = $session->username;
-        $ret['logged_in'] = $session->user_logged_in;
-        
-        $ret['user_level'] = Array();
-        $ret['user_level']['guest'] = USER_LEVEL_GUEST;
-        $ret['user_level']['member'] = USER_LEVEL_MEMBER;
-        $ret['user_level']['mod'] = USER_LEVEL_MOD;
-        $ret['user_level']['admin'] = USER_LEVEL_ADMIN;
-        
-        $ret['approval_needed'] = ( getConfig('approve_comments', '0') == '1' );
-        $ret['guest_posting'] = getConfig('comments_need_login');
-        
-        if ( $ret['guest_posting'] == '1' && !$session->user_logged_in )
-        {
-          $session->kill_captcha();
-          $ret['captcha'] = $session->make_captcha();
-        }
-        break;
-      case 'edit':
-        $cid = (string)$data['id'];
-        if ( !ctype_digit($cid) || intval($cid) < 1 )
-        {
-          echo '{"mode":"error","error":"HACKING ATTEMPT"}';
-          return false;
-        }
-        $cid = intval($cid);
-        $q = $db->sql_query('SELECT c.user_id,c.approved FROM '.table_prefix.'comments c LEFT JOIN '.table_prefix.'users u ON (u.user_id=c.user_id) WHERE comment_id='.$cid.';');
-        if(!$q)
-          $db->die_json();
-        $row = $db->fetchrow();
-        $uid = intval($row['user_id']);
-        $can_edit = ( ( $uid == $session->user_id && $uid != 1 && $this->perms->get_permissions('edit_comments') ) || ( $this->perms->get_permissions('mod_comments') ) );
-        if(!$can_edit)
-        {
-          echo '{"mode":"error","error":"HACKING ATTEMPT"}';
-          return false;
-        }
-        $data['data'] = str_replace("\r", '', $data['data']); // Windows compatibility
-        $text = RenderMan::preprocess_text($data['data'], true, false);
-        $text2 = $db->escape($text);
-        $subj = $db->escape(htmlspecialchars($data['subj']));
-        $q = $db->sql_query('UPDATE '.table_prefix.'comments SET subject=\'' . $subj . '\',comment_data=\'' . $text2 . '\' WHERE comment_id=' . $cid . ';');
-        if(!$q)
-          $db->die_json();
-        $ret = Array(
-            'mode' => 'redraw',
-            'id'   => $data['local_id'],
-            'subj' => htmlspecialchars($data['subj']),
-            'text' => RenderMan::render($text),
-            'src'  => $text,
-            'approved' => $row['approved']
-          );
-        break;
-      case 'delete':
-        $cid = (string)$data['id'];
-        if ( !ctype_digit($cid) || intval($cid) < 1 )
-        {
-          echo '{"mode":"error","error":"HACKING ATTEMPT"}';
-          return false;
-        }
-        $cid = intval($cid);
-        $q = $db->sql_query('SELECT c.user_id FROM '.table_prefix.'comments c LEFT JOIN '.table_prefix.'users u ON (u.user_id=c.user_id) WHERE comment_id='.$cid.';');
-        if(!$q)
-          $db->die_json();
-        $row = $db->fetchrow();
-        $uid = intval($row['user_id']);
-        $can_edit = ( ( $uid == $session->user_id && $uid != 1 && $this->perms->get_permissions('edit_comments') ) || ( $this->perms->get_permissions('mod_comments') ) );
-        if(!$can_edit)
-        {
-          echo '{"mode":"error","error":"HACKING ATTEMPT"}';
-          return false;
-        }
-        $q = $db->sql_query('DELETE FROM '.table_prefix.'comments WHERE comment_id='.$cid.';');
-        if(!$q)
-          $db->die_json();
-        $ret = Array(
-            'mode' => 'annihilate',
-            'id'   => $data['local_id']
-          );
-        break;
-      case 'submit':
-        
-        // Now for a huge round of security checks...
-        
-        $errors = Array();
-        
-        // Authorization
-        // Like the rest of the ACL system, this call is a one-stop check for ALL ACL entries.
-        if ( !$this->perms->get_permissions('post_comments') )
-          $errors[] = 'The site security policy prevents your user account from posting comments;';
-        
-        // Guest authorization
-        if ( getConfig('comments_need_login') == '2' && !$session->user_logged_in )
-          $errors[] = $lang->get('comment_err_need_login');
-        
-        // CAPTCHA code
-        if ( getConfig('comments_need_login') == '1' && !$session->user_logged_in )
-        {
-          $real_code = $session->get_captcha($data['captcha_id']);
-          if ( strtolower($real_code) !== strtolower($data['captcha_code']) )
-            $errors[] = $lang->get('comment_err_captcha_wrong');
-          $session->kill_captcha();
-        }
-        
-        // Spam check
-        $spam_policy = getConfig('comment_spam_policy', 'moderate');
-        $sc_name = ( $session->user_logged_in ) ? $session->username : $data['name'];
-        $sc_mail = ( $session->user_logged_in ) ? $session->email : false;
-        $sc_url  = ( $session->user_logged_in ) ? @$session->user_extra['user_homepage'] : false;
-        $spamcheck = $spam_policy === 'accept' ? true : spamalyze($data['text'], $sc_name, $sc_mail, $sc_url);
-        if ( !$spamcheck && $spam_policy === 'reject' )
-        {
-          $errors[] = $lang->get('comment_err_spamcheck_failed_rejected');
-        }
-        
-        if ( count($errors) > 0 )
-        {
-          $ret = Array(
-            'mode' => 'error',
-            'error' => implode("\n", $errors)
-            );
-        }
-        else
-        {
-          // We're authorized!
-          
-          // Preprocess
-          $name = ( $session->user_logged_in ) ? htmlspecialchars($session->username) : htmlspecialchars($data['name']);
-          $subj = htmlspecialchars($data['subj']);
-          $text = RenderMan::preprocess_text($data['text'], true, false);
-          $src = $text;
-          $sql_subj = $db->escape($subj);
-          $sql_text = $db->escape($text);
-          $text = RenderMan::render($text);
-          $appr = ( getConfig('approve_comments', '0') == '1' ) ? COMMENT_UNAPPROVED : COMMENT_APPROVED;
-          if ( $spam_policy === 'moderate' && !$spamcheck )
-            $appr = COMMENT_SPAM;
-          $time = time();
-          $date = enano_date(ED_DATE | ED_TIME, $time);
-          $ip = $_SERVER['REMOTE_ADDR'];
-          if ( !is_valid_ip($ip) )
-            die('Hacking attempt');
-          
-          // Send it to the database
-          $q = $db->sql_query('INSERT INTO '.table_prefix.'comments(page_id,namespace,name,subject,comment_data,approved, time, user_id, ip_address) VALUES' . "\n  " .
-                             "('$this->page_id', '$this->namespace', '$name', '$sql_subj', '$sql_text', $appr, $time, {$session->user_id}, '$ip');");
-          if(!$q)
-            $db->die_json();
-          
-          // Re-fetch
-          $q = $db->sql_query('SELECT c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type FROM '.table_prefix.'comments AS c
-                               LEFT JOIN '.table_prefix.'users AS u
-                                 ON (u.user_id=c.user_id)
-                               WHERE page_id=\'' . $this->page_id . '\'
-                                 AND namespace=\'' . $this->namespace . '\'
-                                 AND time='.$time.' ORDER BY comment_id DESC LIMIT 1;');
-          if(!$q)
-            $db->die_json();
-          
-          $row = $db->fetchrow();
-          $db->free_result();
-          $row['time'] = $date;
-          $row['comment_data'] = $text;
-          $row['comment_source'] = $src;
-          $ret = Array(
-              'mode' => 'materialize'
-            );
-          $ret = enano_safe_array_merge($ret, $row);
-          
-          $ret['auth_mod_comments'] = $this->perms->get_permissions('mod_comments');
-          $ret['auth_post_comments'] = $this->perms->get_permissions('post_comments');
-          $ret['auth_edit_comments'] = $this->perms->get_permissions('edit_comments');
-          $ret['user_id'] = $session->user_id;
-          $ret['rank_data'] = $session->get_user_rank($session->user_id);
-          $ret['username'] = $session->username;
-          $ret['logged_in'] = $session->user_logged_in;
-          $ret['signature'] = RenderMan::render($row['signature']);
-          
-          $ret['user_level_list'] = Array();
-          $ret['user_level_list']['guest'] = USER_LEVEL_GUEST;
-          $ret['user_level_list']['member'] = USER_LEVEL_MEMBER;
-          $ret['user_level_list']['mod'] = USER_LEVEL_MOD;
-          $ret['user_level_list']['admin'] = USER_LEVEL_ADMIN;
-          $ret['avatar_path'] = make_avatar_url($row['user_id'], $row['avatar_type'], $row['email']);
-        }
-        
-        break;
-      case 'approve':
-        if ( !$this->perms->get_permissions('mod_comments') )
-        {
-          $ret = Array(
-          'mode' => 'error', 
-          'error' => 'You are not authorized to moderate comments.'
-          );
-          echo enano_json_encode($ret);
-          return $ret;
-        }
-        
-        $cid = (string)$data['id'];
-        if ( !ctype_digit($cid) || intval($cid) < 1 )
-        {
-          echo '{"mode":"error","error":"HACKING ATTEMPT"}';
-          return false;
-        }
-        $cid = intval($cid);
-        $q = $db->sql_query('SELECT subject,approved FROM '.table_prefix.'comments WHERE comment_id='.$cid.';');
-        if(!$q || $db->numrows() < 1)
-          $db->die_json();
-        $row = $db->fetchrow();
-        $db->free_result();
-        $appr = ( $row['approved'] == '1' ) ? '0' : '1';
-        $q = $db->sql_query('UPDATE '.table_prefix."comments SET approved=$appr WHERE comment_id=$cid;");
-        if (!$q)
-          $db->die_json();
-        
-        $ret = Array(
-            'mode' => 'redraw',
-            'approved' => $appr,
-            'subj' => $row['subject'],
-            'id'   => $data['local_id'],
-            'approve_updated' => 'yes'
-          );
-        
-        break;
-      case 'view_ip':
-        if ( !$session->get_permissions('mod_comments') )
-        {
-          return array(
-              'mode' => 'error',
-              'error' => 'Unauthorized'
-            );
-        }
-        // fetch comment info
-        if ( !is_int($data['id']) )
-        {
-          return array(
-              'mode' => 'error',
-              'error' => 'Unauthorized'
-            );
-        }
-        $id =& $data['id'];
-        $q = $db->sql_query('SELECT ip_address, name FROM ' . table_prefix . 'comments WHERE comment_id = ' . $id . ';');
-        if ( !$q || $db->numrows() < 1 )
-        {
-          $db->die_json();
-        }
-        list($ip_addr, $name) = $db->fetchrow_num($q);
-        $db->free_result();
-        $name = $db->escape($name);
-        $username = $db->escape($session->username);
-        // log this action
-        $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs(time_id, log_type, action, page_text, author, author_uid, edit_summary) VALUES\n  "
-                            . "( " . time() . ", 'security', 'view_comment_ip', '$name', '$username', $session->user_id, '{$_SERVER['REMOTE_ADDR']}' );");
-        if ( !$q )
-          $db->die_json();
-        
-        // send packet
-        $ret = array(
-            'mode' => 'redraw',
-            'ip_addr' => $ip_addr,
-            'local_id' => $data['local_id']
-          );
-        break;
-      default:
-        $ret = Array(
-          'mode' => 'error', 
-          'error' => $data['mode'] . ' is not a valid request mode'
-          );
-        break;
-    }
-    if ( $is_json )
-      echo enano_json_encode($ret);
-    
-    return $ret;
-  }
-  
+	#
+	# VARIABLES
+	#
+	
+	/**
+ 	* Current list of comments.
+ 	* @var array
+ 	*/
+	
+	var $comments = Array();
+	
+	/**
+ 	* Object to track permissions.
+ 	* @var object
+ 	*/
+	
+	var $perms;
+	
+	#
+	# METHODS
+	#
+	
+	/**
+ 	* Constructor.
+ 	* @param string Page ID of the page to load comments for
+ 	* @param string Namespace of the page to load comments for
+ 	*/
+	
+	function __construct($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Initialize permissions
+		if ( $page_id == $paths->page_id && $namespace == $paths->namespace )
+			$this->perms =& $GLOBALS['session'];
+		else
+			$this->perms = $session->fetch_page_acl($page_id, $namespace);
+		
+		$this->page_id = $db->escape($page_id);
+		$this->namespace = $db->escape($namespace);
+	}
+	
+	/**
+ 	* Processes a command in JSON format.
+ 	* @param mixed Either the JSON-encoded input string, probably something sent from the Javascript/AJAX frontend, or an equivalent array
+ 	*/
+ 	
+	function process_json($json)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$is_json = !is_array($json);
+		
+		if ( $is_json )
+		{
+			$data = enano_json_decode($json);
+			$data = decode_unicode_array($data);
+		}
+		else
+		{
+			$data =& $json;
+		}
+		if ( !isset($data['mode']) )
+		{
+			$ret = Array('mode'=>'error','error'=>'No mode defined!');
+			echo enano_json_encode($ret);
+			return $ret;
+		}
+		if ( getConfig('enable_comments', '1') == '0' )
+		{
+			$ret = Array('mode'=>'error','error'=>'Comments are not enabled on this site.');
+			echo enano_json_encode($ret);
+			return $ret;
+		}
+		$ret = Array();
+		$ret['mode'] = $data['mode'];
+		if ( isset($data['passback']) )
+			$ret['passback'] = $data['passback'];
+		switch ( $data['mode'] )
+		{
+			case 'fetch':
+				if ( !$template->theme_loaded )
+					$template->load_theme();
+				if ( !isset($data['have_template']) )
+				{
+					$ret['template'] = file_get_contents(ENANO_ROOT . '/themes/' . $template->theme . '/comment.tpl');
+				}
+				$approve_clause = $this->perms->get_permissions('mod_comments') ? '' : " AND approved = " . COMMENT_APPROVED;
+				// Get totals
+				$q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$this->page_id' AND namespace = '$this->namespace'{$approve_clause};");
+				if ( !$q )
+					$db->die_json();
+				$counts = array('total' => 0, 'approved' => 0, 'unapproved' => 0, 'spam' => 0);
+				while ( $row = $db->fetchrow() )
+				{
+					$counts['total']++;
+					switch($row['approved']):
+						case COMMENT_APPROVED:   $counts['approved']++;   break;
+						case COMMENT_UNAPPROVED: $counts['unapproved']++; break;
+						case COMMENT_SPAM:       $counts['spam']++;       break;
+					endswitch;
+				}
+				$counts['unapproved'] = $counts['total'] - $counts['approved'];
+				$data['counts'] = $counts;
+				// FIXME, this should be a user preference eventually
+				$ret['per_page'] = $per_page = getConfig('comments_per_page', 10);
+				$page = ( !empty($data['pagenum']) ) ? intval($data['pagenum']) : 0;
+				if ( $page > 0 )
+				{
+					$ret['mode'] = 'refetch';
+				}
+				$limit_clause = "LIMIT $per_page OFFSET " . ($page * $per_page);
+				$q = $db->sql_query('SELECT c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,( c.ip_address IS NOT NULL ) AS have_ip,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type, b.buddy_id IS NOT NULL AS is_buddy, ( b.is_friend IS NOT NULL AND b.is_friend=1 ) AS is_friend FROM '.table_prefix.'comments AS c
+ 															LEFT JOIN '.table_prefix.'users AS u
+ 																ON (u.user_id=c.user_id)
+ 															LEFT JOIN '.table_prefix.'buddies AS b
+ 																ON ( ( b.user_id=' . $session->user_id.' AND b.buddy_user_id=c.user_id ) OR b.user_id IS NULL)
+ 															LEFT JOIN '.table_prefix.'ranks AS r
+ 																ON ( ( u.user_rank = r.rank_id ) )
+ 															WHERE page_id=\'' . $this->page_id . '\'
+ 																AND namespace=\'' . $this->namespace . '\'
+ 																' . $approve_clause . '
+ 															GROUP BY c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,c.ip_address,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type,b.buddy_id,b.is_friend
+ 															ORDER BY c.time ASC
+ 															' . $limit_clause . ';');
+				$ret['comments'] = Array();
+				if (!$q)
+					$db->die_json();
+				if ( $row = $db->fetchrow($q) )
+				{
+					do {
+						
+						if ( !$this->perms->get_permissions('mod_comments') && $row['approved'] != COMMENT_APPROVED )
+							continue;
+						
+						// Localize the rank
+						$row = array_merge($row, $session->get_user_rank(intval($row['user_id'])));
+						
+						// Send the source
+						$row['comment_source'] = $row['comment_data'];
+						
+						// Format text
+						$row['comment_data'] = RenderMan::render($row['comment_data']);
+						
+						// Hide it if it's a post from a foe
+						if ( $row['is_buddy'] == 1 && $row['is_friend'] == 0 )
+						{
+							$seed = md5(sha1(mt_rand() . microtime()));
+							$wrapper = '
+								<div id="posthide_'.$seed.'" style="display: none;">
+									' . $row['comment_data'] . '
+								</div>
+								<p><span style="opacity: 0.4; filter: alpha(opacity=40);">' . $lang->get('comment_msg_foe_comment_hidden') . '</span> <span style="text-align: right;"><a href="#showpost" onclick="document.getElementById(\'posthide_'.$seed.'\').style.display=\'block\'; this.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode); return false;">' . $lang->get('comment_btn_display_foe_comment') . '</a></span></p>
+							';
+							$row['comment_data'] = $wrapper;
+						}
+						
+						// Format date
+						$row['time'] = enano_date(ED_DATE | ED_TIME, $row['time']);
+						
+						// Format signature
+						$row['signature'] = ( !empty($row['signature']) ) ? RenderMan::render($row['signature']) : '';
+						
+						// Do we have the IP?
+						$row['have_ip'] = ( $row['have_ip'] == 1 );
+						
+						// Avatar URL
+						$row['avatar_path'] = make_avatar_url($row['user_id'], $row['avatar_type'], $row['email']);
+						
+						// Add the comment to the list
+						$ret['comments'][] = $row;
+						
+					} while ( $row = $db->fetchrow($q) );
+				}
+				$db->free_result();
+				$ret['count_appr'] = $counts['approved'];
+				$ret['count_total'] = $counts['total'];
+				$ret['count_visible'] = $this->perms->get_permissions('mod_comments') ? $counts['total'] : $counts['approved'];
+				$ret['count_unappr'] = $counts['unapproved'];
+				$ret['auth_mod_comments'] = $this->perms->get_permissions('mod_comments');
+				$ret['auth_post_comments'] = $this->perms->get_permissions('post_comments');
+				$ret['auth_edit_comments'] = $this->perms->get_permissions('edit_comments');
+				$ret['auth_edit_wysiwyg'] = $this->perms->get_permissions('edit_wysiwyg');
+				$ret['user_id'] = $session->user_id;
+				$ret['username'] = $session->username;
+				$ret['logged_in'] = $session->user_logged_in;
+				
+				$ret['user_level'] = Array();
+				$ret['user_level']['guest'] = USER_LEVEL_GUEST;
+				$ret['user_level']['member'] = USER_LEVEL_MEMBER;
+				$ret['user_level']['mod'] = USER_LEVEL_MOD;
+				$ret['user_level']['admin'] = USER_LEVEL_ADMIN;
+				
+				$ret['approval_needed'] = ( getConfig('approve_comments', '0') == '1' );
+				$ret['guest_posting'] = getConfig('comments_need_login');
+				
+				if ( $ret['guest_posting'] == '1' && !$session->user_logged_in )
+				{
+					$session->kill_captcha();
+					$ret['captcha'] = $session->make_captcha();
+				}
+				break;
+			case 'edit':
+				$cid = (string)$data['id'];
+				if ( !ctype_digit($cid) || intval($cid) < 1 )
+				{
+					echo '{"mode":"error","error":"HACKING ATTEMPT"}';
+					return false;
+				}
+				$cid = intval($cid);
+				$q = $db->sql_query('SELECT c.user_id,c.approved FROM '.table_prefix.'comments c LEFT JOIN '.table_prefix.'users u ON (u.user_id=c.user_id) WHERE comment_id='.$cid.';');
+				if(!$q)
+					$db->die_json();
+				$row = $db->fetchrow();
+				$uid = intval($row['user_id']);
+				$can_edit = ( ( $uid == $session->user_id && $uid != 1 && $this->perms->get_permissions('edit_comments') ) || ( $this->perms->get_permissions('mod_comments') ) );
+				if(!$can_edit)
+				{
+					echo '{"mode":"error","error":"HACKING ATTEMPT"}';
+					return false;
+				}
+				$data['data'] = str_replace("\r", '', $data['data']); // Windows compatibility
+				$text = RenderMan::preprocess_text($data['data'], true, false);
+				$text2 = $db->escape($text);
+				$subj = $db->escape(htmlspecialchars($data['subj']));
+				$q = $db->sql_query('UPDATE '.table_prefix.'comments SET subject=\'' . $subj . '\',comment_data=\'' . $text2 . '\' WHERE comment_id=' . $cid . ';');
+				if(!$q)
+					$db->die_json();
+				$ret = Array(
+						'mode' => 'redraw',
+						'id'   => $data['local_id'],
+						'subj' => htmlspecialchars($data['subj']),
+						'text' => RenderMan::render($text),
+						'src'  => $text,
+						'approved' => $row['approved']
+					);
+				break;
+			case 'delete':
+				$cid = (string)$data['id'];
+				if ( !ctype_digit($cid) || intval($cid) < 1 )
+				{
+					echo '{"mode":"error","error":"HACKING ATTEMPT"}';
+					return false;
+				}
+				$cid = intval($cid);
+				$q = $db->sql_query('SELECT c.user_id FROM '.table_prefix.'comments c LEFT JOIN '.table_prefix.'users u ON (u.user_id=c.user_id) WHERE comment_id='.$cid.';');
+				if(!$q)
+					$db->die_json();
+				$row = $db->fetchrow();
+				$uid = intval($row['user_id']);
+				$can_edit = ( ( $uid == $session->user_id && $uid != 1 && $this->perms->get_permissions('edit_comments') ) || ( $this->perms->get_permissions('mod_comments') ) );
+				if(!$can_edit)
+				{
+					echo '{"mode":"error","error":"HACKING ATTEMPT"}';
+					return false;
+				}
+				$q = $db->sql_query('DELETE FROM '.table_prefix.'comments WHERE comment_id='.$cid.';');
+				if(!$q)
+					$db->die_json();
+				$ret = Array(
+						'mode' => 'annihilate',
+						'id'   => $data['local_id']
+					);
+				break;
+			case 'submit':
+				
+				// Now for a huge round of security checks...
+				
+				$errors = Array();
+				
+				// Authorization
+				// Like the rest of the ACL system, this call is a one-stop check for ALL ACL entries.
+				if ( !$this->perms->get_permissions('post_comments') )
+					$errors[] = 'The site security policy prevents your user account from posting comments;';
+				
+				// Guest authorization
+				if ( getConfig('comments_need_login') == '2' && !$session->user_logged_in )
+					$errors[] = $lang->get('comment_err_need_login');
+				
+				// CAPTCHA code
+				if ( getConfig('comments_need_login') == '1' && !$session->user_logged_in )
+				{
+					$real_code = $session->get_captcha($data['captcha_id']);
+					if ( strtolower($real_code) !== strtolower($data['captcha_code']) )
+						$errors[] = $lang->get('comment_err_captcha_wrong');
+					$session->kill_captcha();
+				}
+				
+				// Spam check
+				$spam_policy = getConfig('comment_spam_policy', 'moderate');
+				$sc_name = ( $session->user_logged_in ) ? $session->username : $data['name'];
+				$sc_mail = ( $session->user_logged_in ) ? $session->email : false;
+				$sc_url  = ( $session->user_logged_in ) ? @$session->user_extra['user_homepage'] : false;
+				$spamcheck = $spam_policy === 'accept' ? true : spamalyze($data['text'], $sc_name, $sc_mail, $sc_url);
+				if ( !$spamcheck && $spam_policy === 'reject' )
+				{
+					$errors[] = $lang->get('comment_err_spamcheck_failed_rejected');
+				}
+				
+				if ( count($errors) > 0 )
+				{
+					$ret = Array(
+						'mode' => 'error',
+						'error' => implode("\n", $errors)
+						);
+				}
+				else
+				{
+					// We're authorized!
+					
+					// Preprocess
+					$name = ( $session->user_logged_in ) ? htmlspecialchars($session->username) : htmlspecialchars($data['name']);
+					$subj = htmlspecialchars($data['subj']);
+					$text = RenderMan::preprocess_text($data['text'], true, false);
+					$src = $text;
+					$sql_subj = $db->escape($subj);
+					$sql_text = $db->escape($text);
+					$text = RenderMan::render($text);
+					$appr = ( getConfig('approve_comments', '0') == '1' ) ? COMMENT_UNAPPROVED : COMMENT_APPROVED;
+					if ( $spam_policy === 'moderate' && !$spamcheck )
+						$appr = COMMENT_SPAM;
+					$time = time();
+					$date = enano_date(ED_DATE | ED_TIME, $time);
+					$ip = $_SERVER['REMOTE_ADDR'];
+					if ( !is_valid_ip($ip) )
+						die('Hacking attempt');
+					
+					// Send it to the database
+					$q = $db->sql_query('INSERT INTO '.table_prefix.'comments(page_id,namespace,name,subject,comment_data,approved, time, user_id, ip_address) VALUES' . "\n  " .
+ 														"('$this->page_id', '$this->namespace', '$name', '$sql_subj', '$sql_text', $appr, $time, {$session->user_id}, '$ip');");
+					if(!$q)
+						$db->die_json();
+					
+					// Re-fetch
+					$q = $db->sql_query('SELECT c.comment_id,c.name,c.subject,c.comment_data,c.time,c.approved,u.user_level,u.user_id,u.email,u.signature,u.user_has_avatar,u.avatar_type FROM '.table_prefix.'comments AS c
+ 															LEFT JOIN '.table_prefix.'users AS u
+ 																ON (u.user_id=c.user_id)
+ 															WHERE page_id=\'' . $this->page_id . '\'
+ 																AND namespace=\'' . $this->namespace . '\'
+ 																AND time='.$time.' ORDER BY comment_id DESC LIMIT 1;');
+					if(!$q)
+						$db->die_json();
+					
+					$row = $db->fetchrow();
+					$db->free_result();
+					$row['time'] = $date;
+					$row['comment_data'] = $text;
+					$row['comment_source'] = $src;
+					$ret = Array(
+							'mode' => 'materialize'
+						);
+					$ret = enano_safe_array_merge($ret, $row);
+					
+					$ret['auth_mod_comments'] = $this->perms->get_permissions('mod_comments');
+					$ret['auth_post_comments'] = $this->perms->get_permissions('post_comments');
+					$ret['auth_edit_comments'] = $this->perms->get_permissions('edit_comments');
+					$ret['user_id'] = $session->user_id;
+					$ret['rank_data'] = $session->get_user_rank($session->user_id);
+					$ret['username'] = $session->username;
+					$ret['logged_in'] = $session->user_logged_in;
+					$ret['signature'] = RenderMan::render($row['signature']);
+					
+					$ret['user_level_list'] = Array();
+					$ret['user_level_list']['guest'] = USER_LEVEL_GUEST;
+					$ret['user_level_list']['member'] = USER_LEVEL_MEMBER;
+					$ret['user_level_list']['mod'] = USER_LEVEL_MOD;
+					$ret['user_level_list']['admin'] = USER_LEVEL_ADMIN;
+					$ret['avatar_path'] = make_avatar_url($row['user_id'], $row['avatar_type'], $row['email']);
+				}
+				
+				break;
+			case 'approve':
+				if ( !$this->perms->get_permissions('mod_comments') )
+				{
+					$ret = Array(
+					'mode' => 'error', 
+					'error' => 'You are not authorized to moderate comments.'
+					);
+					echo enano_json_encode($ret);
+					return $ret;
+				}
+				
+				$cid = (string)$data['id'];
+				if ( !ctype_digit($cid) || intval($cid) < 1 )
+				{
+					echo '{"mode":"error","error":"HACKING ATTEMPT"}';
+					return false;
+				}
+				$cid = intval($cid);
+				$q = $db->sql_query('SELECT subject,approved FROM '.table_prefix.'comments WHERE comment_id='.$cid.';');
+				if(!$q || $db->numrows() < 1)
+					$db->die_json();
+				$row = $db->fetchrow();
+				$db->free_result();
+				$appr = ( $row['approved'] == '1' ) ? '0' : '1';
+				$q = $db->sql_query('UPDATE '.table_prefix."comments SET approved=$appr WHERE comment_id=$cid;");
+				if (!$q)
+					$db->die_json();
+				
+				$ret = Array(
+						'mode' => 'redraw',
+						'approved' => $appr,
+						'subj' => $row['subject'],
+						'id'   => $data['local_id'],
+						'approve_updated' => 'yes'
+					);
+				
+				break;
+			case 'view_ip':
+				if ( !$session->get_permissions('mod_comments') )
+				{
+					return array(
+							'mode' => 'error',
+							'error' => 'Unauthorized'
+						);
+				}
+				// fetch comment info
+				if ( !is_int($data['id']) )
+				{
+					return array(
+							'mode' => 'error',
+							'error' => 'Unauthorized'
+						);
+				}
+				$id =& $data['id'];
+				$q = $db->sql_query('SELECT ip_address, name FROM ' . table_prefix . 'comments WHERE comment_id = ' . $id . ';');
+				if ( !$q || $db->numrows() < 1 )
+				{
+					$db->die_json();
+				}
+				list($ip_addr, $name) = $db->fetchrow_num($q);
+				$db->free_result();
+				$name = $db->escape($name);
+				$username = $db->escape($session->username);
+				// log this action
+				$q = $db->sql_query('INSERT INTO ' . table_prefix . "logs(time_id, log_type, action, page_text, author, author_uid, edit_summary) VALUES\n  "
+														. "( " . time() . ", 'security', 'view_comment_ip', '$name', '$username', $session->user_id, '{$_SERVER['REMOTE_ADDR']}' );");
+				if ( !$q )
+					$db->die_json();
+				
+				// send packet
+				$ret = array(
+						'mode' => 'redraw',
+						'ip_addr' => $ip_addr,
+						'local_id' => $data['local_id']
+					);
+				break;
+			default:
+				$ret = Array(
+					'mode' => 'error', 
+					'error' => $data['mode'] . ' is not a valid request mode'
+					);
+				break;
+		}
+		if ( $is_json )
+			echo enano_json_encode($ret);
+		
+		return $ret;
+	}
+	
 } // class Comments
 
--- a/includes/common.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/common.php	Sun Mar 28 23:10:46 2010 -0400
@@ -22,13 +22,13 @@
 // Make sure we don't have an attempt to inject globals (register_globals on)
 if ( isset($_REQUEST['GLOBALS']) )
 {
-  ?>
-  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html><head><title>Hacking Attempt</title><meta http-equiv="Content-type" content="text/html; charset=utf-8" /></head><style type="text/css">body{background-color:#000;color:#CCC;font-family:trebuchet ms,sans-serif;font-size:9pt;}a{color:#FFF;}</style><body><p>Hacking attempt using <a href="http://www.hardened-php.net/index.76.html">PHP $GLOBALS overwrite vulnerability</a> detected, reported to admin</p><p>You're worse than this guy! Unless you are this guy...</p><p id="billp"><img alt=" " src="about:blank" id="billi" /></p><script type="text/javascript">// <![CDATA[
-  window.onload=function(){counter();setInterval('counter();', 1000);};var text=false;var cnt=10;function counter(){if(!text){text=document.createElement('span');text.id='billc';text.innerHTML=cnt;text.style.fontSize='96pt';text.style.color='#FF0000';p=document.getElementById('billp');p.appendChild(text);}else{if(cnt==1){document.getElementById('billi').src='http://upload.wikimedia.org/wikipedia/commons/7/7f/Bill_Gates_2004_cr.jpg';document.getElementById('billc').innerHTML='';return;}cnt--;document.getElementById('billc').innerHTML=cnt+' ';}}
-  // ]]>
-  </script><p><span style="color:black;">You been f***ed by Enano | valid XHTML 1.1</span></p></body></html>
-  <?php
-  exit;
+	?>
+	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html><head><title>Hacking Attempt</title><meta http-equiv="Content-type" content="text/html; charset=utf-8" /></head><style type="text/css">body{background-color:#000;color:#CCC;font-family:trebuchet ms,sans-serif;font-size:9pt;}a{color:#FFF;}</style><body><p>Hacking attempt using <a href="http://www.hardened-php.net/index.76.html">PHP $GLOBALS overwrite vulnerability</a> detected, reported to admin</p><p>You're worse than this guy! Unless you are this guy...</p><p id="billp"><img alt=" " src="about:blank" id="billi" /></p><script type="text/javascript">// <![CDATA[
+	window.onload=function(){counter();setInterval('counter();', 1000);};var text=false;var cnt=10;function counter(){if(!text){text=document.createElement('span');text.id='billc';text.innerHTML=cnt;text.style.fontSize='96pt';text.style.color='#FF0000';p=document.getElementById('billp');p.appendChild(text);}else{if(cnt==1){document.getElementById('billi').src='http://upload.wikimedia.org/wikipedia/commons/7/7f/Bill_Gates_2004_cr.jpg';document.getElementById('billc').innerHTML='';return;}cnt--;document.getElementById('billc').innerHTML=cnt+' ';}}
+	// ]]>
+	</script><p><span style="color:black;">You been f***ed by Enano | valid XHTML 1.1</span></p></body></html>
+	<?php
+	exit;
 }
 
 // only do this if it hasn't been done yet
@@ -54,11 +54,11 @@
 // First check to see if something already declared this function.... it happens often.
 if ( !function_exists('microtime_float') )
 {
-  function microtime_float()
-  {
-    list($usec, $sec) = explode(" ", microtime());
-    return ((float)$usec + (float)$sec);
-  }
+	function microtime_float()
+	{
+		list($usec, $sec) = explode(" ", microtime());
+		return ((float)$usec + (float)$sec);
+	}
 }
 
 // Determine starting time
@@ -68,13 +68,13 @@
 // Verbose error reporting
 if ( defined('E_STRICT') )
 {
-  // PHP5, PHP6
-  error_reporting(E_ALL & ~E_STRICT);
+	// PHP5, PHP6
+	error_reporting(E_ALL & ~E_STRICT);
 }
 else
 {
-  // PHP4
-  error_reporting(E_ALL);
+	// PHP4
+	error_reporting(E_ALL);
 }
 
 //
@@ -88,24 +88,24 @@
 // .enanodev is found in the Enano root (not /repo/).
 if ( strpos(__FILE__, '/repo/') && file_exists(dirname(__FILE__) . '/../../.enanodev') )
 {
-  // We have a development directory. Remove /repo/ from the picture.
-  $filename = str_replace('/repo/', '/', __FILE__);
+	// We have a development directory. Remove /repo/ from the picture.
+	$filename = str_replace('/repo/', '/', __FILE__);
 }
 else
 {
-  // Standard Enano installation
-  $filename = __FILE__;
+	// Standard Enano installation
+	$filename = __FILE__;
 }
 
 // ENANO_ROOT is sometimes defined by plugins like AjIM that need the constant before the Enano API is initialized
 if ( !defined('ENANO_ROOT') )
-  define('ENANO_ROOT', dirname(dirname($filename)));
+	define('ENANO_ROOT', dirname(dirname($filename)));
 
 // We deprecated debugConsole in 1.0.2 because it was never used and there were a lot of unneeded debugging points in the code.
 
 // _nightly.php is used to tag non-Mercurial-generated nightly builds
 if ( file_exists( ENANO_ROOT . '/_nightly.php') )
-  require(ENANO_ROOT.'/_nightly.php');
+	require(ENANO_ROOT.'/_nightly.php');
 
 // List of scheduled tasks (don't change this manually, use register_cron_task())
 $cron_tasks = array();
@@ -113,11 +113,11 @@
 } // check for ENANO_COMMON_ROOT_LOADED
 else
 {
-  // loading a second time
-  if ( !defined('ENANO_COMMON_ROOT_LOADED_MULTI') )
-  {
-    define('ENANO_COMMON_ROOT_LOADED_MULTI', 1);
-  }
+	// loading a second time
+	if ( !defined('ENANO_COMMON_ROOT_LOADED_MULTI') )
+	{
+		define('ENANO_COMMON_ROOT_LOADED_MULTI', 1);
+	}
 }
 
 // If all we really need is the root directory, just leave now
@@ -125,7 +125,7 @@
 // is included a second time, the rest of Enano will load.
 if ( defined('ENANO_COMMON_ROOTONLY') && !defined('ENANO_COMMON_ROOT_LOADED_MULTI') )
 {
-  return true;
+	return true;
 }
 
 // Start including files. LOTS of files. Yeah!
@@ -160,7 +160,7 @@
 
 global $db, $session, $paths, $template, $plugins; // Common objects
 global $enano_config; // A global used to cache config information without making loads of queries ;-)
-                      // In addition, $enano_config is used to fetch config information if die_semicritical() is called.
+											// In addition, $enano_config is used to fetch config information if die_semicritical() is called.
 
 // Jim Tucek's e-mail encryption code                      
 global $email;
@@ -182,17 +182,17 @@
 // Divert to CLI loader if running from CLI
 if ( defined('ENANO_CLI') || ( isset($argc) && isset($argv) ) )
 {
-  if ( defined('ENANO_CLI') || ( is_int($argc) && is_array($argv) && !isset($_SERVER['REQUEST_URI']) ) )
-  {
-    require(ENANO_ROOT . '/includes/common_cli.php');
-    return;
-  }
+	if ( defined('ENANO_CLI') || ( is_int($argc) && is_array($argv) && !isset($_SERVER['REQUEST_URI']) ) )
+	{
+		require(ENANO_ROOT . '/includes/common_cli.php');
+		return;
+	}
 }
 
 // Because Enano sends out complete URLs in several occasions, we need to know what hostname the user is requesting the page from.
 // In future versions we may include a fallback "safety" host to use, but that's too much to worry about now
 if ( !isset($_SERVER['HTTP_HOST']) )
-  grinding_halt('Cannot get hostname', '<p>Your web browser did not provide the HTTP Host: field. This site requires a modern browser that supports the HTTP 1.1 standard.</p>');
+	grinding_halt('Cannot get hostname', '<p>Your web browser did not provide the HTTP Host: field. This site requires a modern browser that supports the HTTP 1.1 standard.</p>');
 
 //
 // END BACKGROUND AND ENVIRONMENT CHECKS
@@ -212,7 +212,7 @@
 @include(ENANO_ROOT . $config_file);
 unset($dbuser, $dbpasswd);
 if ( !isset($dbdriver) )
-  $dbdriver = 'mysql';
+	$dbdriver = 'mysql';
 
 $db = new $dbdriver();
 $db->connect();
@@ -229,18 +229,18 @@
 // Build the list of system tables (this is mostly done in constants.php, but that's before table_prefix is known)
 if ( defined('table_prefix') && !defined('ENANO_TABLELIST_PREFIXED') )
 {
-  define('ENANO_TABLELIST_PREFIXED', 1);
-  foreach ( $system_table_list as $i => $_ )
-  {
-    $system_table_list[$i] = table_prefix . $system_table_list[$i];
-  }
+	define('ENANO_TABLELIST_PREFIXED', 1);
+	foreach ( $system_table_list as $i => $_ )
+	{
+		$system_table_list[$i] = table_prefix . $system_table_list[$i];
+	}
 }
 
 // Select and fetch the site configuration
 $e = $db->sql_query('SELECT config_name, config_value FROM '.table_prefix.'config;');
 if ( !$e )
 {
-  $db->_die('Some critical configuration information could not be selected.');
+	$db->_die('Some critical configuration information could not be selected.');
 }
 // Used in die_semicritical to figure out whether to call getConfig() or not
 define('ENANO_CONFIG_FETCHED', '');
@@ -249,7 +249,7 @@
 $enano_config = Array();
 while($r = $db->fetchrow())
 {
-  $enano_config[$r['config_name']] = $r['config_value'];
+	$enano_config[$r['config_name']] = $r['config_value'];
 }
 
 $db->free_result();
@@ -258,22 +258,22 @@
 
 if ( defined('ENANO_EXIT_AFTER_CONFIG') )
 {
-  return true;
+	return true;
 }
 
 // Now that we have the config, check the Enano version.
 if ( enano_version(false, true) != $version && !defined('IN_ENANO_UPGRADE') )
 {
-  grinding_halt('Version mismatch', '<p>It seems that the Enano release we\'re trying to run ('.$version.') is different from the version specified in your database ('.enano_version().'). Perhaps you need to <a href="'.scriptPath.'/install/index.php">upgrade</a>?</p>');
+	grinding_halt('Version mismatch', '<p>It seems that the Enano release we\'re trying to run ('.$version.') is different from the version specified in your database ('.enano_version().'). Perhaps you need to <a href="'.scriptPath.'/install/index.php">upgrade</a>?</p>');
 }
 
 // Set our CDN path
 if ( !defined('cdnPath') )
 {
-  $cdnpath = getConfig('cdn_path', scriptPath);
-  if ( empty($cdnpath) )
-    $cdnpath = scriptPath;
-  define('cdnPath', $cdnpath);
+	$cdnpath = getConfig('cdn_path', scriptPath);
+	if ( empty($cdnpath) )
+		$cdnpath = scriptPath;
+	define('cdnPath', $cdnpath);
 }
 
 //
@@ -283,47 +283,47 @@
 // If the AES key size has been changed, bail out and fast
 if ( !getConfig('aes_key_size') )
 {
-  setConfig('aes_key_size', AES_BITS);
+	setConfig('aes_key_size', AES_BITS);
 }
 else if ( $ks = getConfig('aes_key_size') )
 {
-  if ( intval($ks) != AES_BITS )
-  {
-    grinding_halt('AES key size changed', '<p>Enano has detected that the AES key size in constants.php has been changed. This change cannot be performed after installation, otherwise the private key would have to be re-generated and all passwords would have to be re-encrypted.</p><p>Please change the key size back to ' . $ks . ' bits and reload this page.</p>');
-  }
+	if ( intval($ks) != AES_BITS )
+	{
+		grinding_halt('AES key size changed', '<p>Enano has detected that the AES key size in constants.php has been changed. This change cannot be performed after installation, otherwise the private key would have to be re-generated and all passwords would have to be re-encrypted.</p><p>Please change the key size back to ' . $ks . ' bits and reload this page.</p>');
+	}
 }
 
 // Same for AES block size
 if ( !getConfig('aes_block_size') )
 {
-  setConfig('aes_block_size', AES_BLOCKSIZE);
+	setConfig('aes_block_size', AES_BLOCKSIZE);
 }
 else if ( $ks = getConfig('aes_block_size') )
 {
-  if ( intval($ks) != AES_BLOCKSIZE )
-  {
-    grinding_halt('AES block size changed', '<p>Enano has detected that the AES block size in constants.php has been changed. This change cannot be performed after installation, otherwise all passwords would have to be re-encrypted.</p><p>Please change the block size back to ' . $ks . ' bits and reload this page.</p>');
-  }
+	if ( intval($ks) != AES_BLOCKSIZE )
+	{
+		grinding_halt('AES block size changed', '<p>Enano has detected that the AES block size in constants.php has been changed. This change cannot be performed after installation, otherwise all passwords would have to be re-encrypted.</p><p>Please change the block size back to ' . $ks . ' bits and reload this page.</p>');
+	}
 }
 
 // Is there no default language?
 if ( getConfig('default_language') === false && !defined('IN_ENANO_MIGRATION') )
 {
-  $q = $db->sql_query('SELECT lang_id FROM '.table_prefix.'language LIMIT 1;');
-  if ( !$q )
-    $db->_die('common.php - setting default language');
-  if ( $db->numrows() < 1 && !defined('ENANO_ALLOW_LOAD_NOLANG') )
-  {
-    grinding_halt('No languages', '<p>There are no languages installed on this site.</p>
-        <p>If you are the website administrator, you may install a language by writing and executing a simple PHP script to install it:</p>
-        <pre>
+	$q = $db->sql_query('SELECT lang_id FROM '.table_prefix.'language LIMIT 1;');
+	if ( !$q )
+		$db->_die('common.php - setting default language');
+	if ( $db->numrows() < 1 && !defined('ENANO_ALLOW_LOAD_NOLANG') )
+	{
+		grinding_halt('No languages', '<p>There are no languages installed on this site.</p>
+				<p>If you are the website administrator, you may install a language by writing and executing a simple PHP script to install it:</p>
+				<pre>
 &lt;?php
 define("ENANO_ALLOW_LOAD_NOLANG", 1);
 require("includes/common.php");
 install_language("eng", "English", "English", ENANO_ROOT . "/language/english/enano.json");</pre>');
-  }
-  $row = $db->fetchrow();
-  setConfig('default_language', $row['lang_id']);
+	}
+	$row = $db->fetchrow();
+	setConfig('default_language', $row['lang_id']);
 }
 
 profiler_log('Ran checks');
@@ -348,8 +348,8 @@
 // Load plugins from common because we can't give plugins full abilities in object context
 foreach ( $plugins->load_list as $f )
 {
-  if ( file_exists(ENANO_ROOT . '/plugins/' . $f) )
-    include_once ENANO_ROOT . '/plugins/' . $f;
+	if ( file_exists(ENANO_ROOT . '/plugins/' . $f) )
+		include_once ENANO_ROOT . '/plugins/' . $f;
 }
 
 profiler_log('Loaded plugins');
@@ -371,125 +371,125 @@
 // script though, because that will make the DBAL look in the wrong place for the config file.
 if ( !defined('IN_ENANO_INSTALL') )
 {
-  // And here you have it, the de facto way to place a hook. Plugins can place hooks and hook
-  // into other plugins. You just never know.
-  $code = $plugins->setHook('base_classes_initted');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  profiler_log('Finished base_classes_initted hook');
-  
-  // One quick security check...
-  if ( !is_valid_ip($_SERVER['REMOTE_ADDR']) )
-  {
-    die('SECURITY: spoofed IP address: ' . htmlspecialchars($_SERVER['REMOTE_ADDR']));
-  }
-  
-  // For special and administration pages, sometimes there is a "preloader" function that must be run
-  // before the session manager and/or path manager get the init signal. Call it here.
-  $urlname = get_title(true);
-  list($page_id, $namespace) = RenderMan::strToPageID($urlname);
-  list($page_id_top) = explode('/', $page_id);
-  $fname = "page_{$namespace}_{$page_id_top}_preloader";
-  if( ( $namespace == 'Admin' || $namespace == 'Special' ) && function_exists($fname))
-  {
-    call_user_func($fname);
-  }
-  
-  profiler_log('Checked for (and ran, if applicable) preloader');
-  
-  // Add all of our built in special pages
-  foreach ( array('SpecialUserFuncs', 'SpecialPageFuncs', 'SpecialAdmin', 'SpecialCSS', 'SpecialUpDownload', 'SpecialSearch', 'PrivateMessages', 'SpecialGroups', 'SpecialLog') as $plugin )
-  {
-    $funcname = "{$plugin}_paths_init";
-    if ( function_exists($funcname) )
-    {
-      $funcname();
-    }
-  }
-  profiler_log('Added special pages');
-  
-  // All checks passed! Start the main components up.  
-  $session->start();
-  
-  // This is where plugins will want to add pages from 1.1.x on out. You can still add
-  // pages at base_classes_initted but the titles won't be localized. This is because
-  // the session manager has to be started before localization will work in the user's
-  // preferred language.
-  $code = $plugins->setHook('session_started');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  profiler_log('Ran session_started hook');
-  
-  $paths->init($urlname);
-  
-  // setup output format
-  if ( defined('ENANO_OUTPUT_FORMAT') )
-    $class = 'Output_' . ENANO_OUTPUT_FORMAT;
-  else
-    $class = ( isset($_GET['noheaders']) ) ? 'Output_Naked' : 'Output_HTML';
-    
-  $output = new $class();
-  
-  // Are we running from the API? If so, did the page set a title?
-  if ( !defined('ENANO_INTERFACE_INDEX') && !defined('ENANO_INTERFACE_AJAX') && isset($title) )
-  {
-    $output->set_title($title);
-  }
-  
-  // We're ready for whatever life throws us now, at least from an API point of view.
-  define('ENANO_MAINSTREAM', '');
-  
-  // If the site is disabled, bail out, unless we're trying to log in or administer the site
-  if(getConfig('site_disabled') == '1' && $session->user_level < USER_LEVEL_ADMIN)
-  {
-    // is this one of the more critical special pages?
-    if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->page_id == 'CSS' || $paths->page_id == 'Administration' || $paths->page_id == 'Login' ) ) )
-    {
-      // yeah, we need to keep this page available. do nothing; allow execution to continue
-    }
-    else
-    {
-      if(!$n = getConfig('site_disabled_notice')) 
-      {
-        $n = 'The administrator has disabled the site. Please check back later.';
-      }
-      
-      $text = RenderMan::render($n) . '
-      <div class="info-box">
-        If you have an administrative account, you may <a href="'.makeUrlNS('Special', 'Login').'">log in</a> to the site.
-      </div>';
-      $paths->wiki_mode = 0;
-      die_semicritical('Site disabled', $text);
-    }
-  }
-  else if ( getConfig('site_disabled') == '1' && $session->user_level >= USER_LEVEL_ADMIN )
-  {
-    // If the site is disabled but the user has admin rights, allow browsing
-    // and stuff, but display the orange box notifying the admin.
-    $template->site_disabled = true;
-  }
-  
-  // At this point all of Enano is fully initialized and running and you're ready to do whatever you want.
-  $code = $plugins->setHook('common_post');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  profiler_log('Ran disabled-site checks and common_post');
-  
-  load_rank_data();
-  
-  profiler_log('Loaded user rank data');
-  
-  if ( isset($_GET['noheaders']) )
-    $template->no_headers = true;
+	// And here you have it, the de facto way to place a hook. Plugins can place hooks and hook
+	// into other plugins. You just never know.
+	$code = $plugins->setHook('base_classes_initted');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	profiler_log('Finished base_classes_initted hook');
+	
+	// One quick security check...
+	if ( !is_valid_ip($_SERVER['REMOTE_ADDR']) )
+	{
+		die('SECURITY: spoofed IP address: ' . htmlspecialchars($_SERVER['REMOTE_ADDR']));
+	}
+	
+	// For special and administration pages, sometimes there is a "preloader" function that must be run
+	// before the session manager and/or path manager get the init signal. Call it here.
+	$urlname = get_title(true);
+	list($page_id, $namespace) = RenderMan::strToPageID($urlname);
+	list($page_id_top) = explode('/', $page_id);
+	$fname = "page_{$namespace}_{$page_id_top}_preloader";
+	if( ( $namespace == 'Admin' || $namespace == 'Special' ) && function_exists($fname))
+	{
+		call_user_func($fname);
+	}
+	
+	profiler_log('Checked for (and ran, if applicable) preloader');
+	
+	// Add all of our built in special pages
+	foreach ( array('SpecialUserFuncs', 'SpecialPageFuncs', 'SpecialAdmin', 'SpecialCSS', 'SpecialUpDownload', 'SpecialSearch', 'PrivateMessages', 'SpecialGroups', 'SpecialLog') as $plugin )
+	{
+		$funcname = "{$plugin}_paths_init";
+		if ( function_exists($funcname) )
+		{
+			$funcname();
+		}
+	}
+	profiler_log('Added special pages');
+	
+	// All checks passed! Start the main components up.  
+	$session->start();
+	
+	// This is where plugins will want to add pages from 1.1.x on out. You can still add
+	// pages at base_classes_initted but the titles won't be localized. This is because
+	// the session manager has to be started before localization will work in the user's
+	// preferred language.
+	$code = $plugins->setHook('session_started');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	profiler_log('Ran session_started hook');
+	
+	$paths->init($urlname);
+	
+	// setup output format
+	if ( defined('ENANO_OUTPUT_FORMAT') )
+		$class = 'Output_' . ENANO_OUTPUT_FORMAT;
+	else
+		$class = ( isset($_GET['noheaders']) ) ? 'Output_Naked' : 'Output_HTML';
+		
+	$output = new $class();
+	
+	// Are we running from the API? If so, did the page set a title?
+	if ( !defined('ENANO_INTERFACE_INDEX') && !defined('ENANO_INTERFACE_AJAX') && isset($title) )
+	{
+		$output->set_title($title);
+	}
+	
+	// We're ready for whatever life throws us now, at least from an API point of view.
+	define('ENANO_MAINSTREAM', '');
+	
+	// If the site is disabled, bail out, unless we're trying to log in or administer the site
+	if(getConfig('site_disabled') == '1' && $session->user_level < USER_LEVEL_ADMIN)
+	{
+		// is this one of the more critical special pages?
+		if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->page_id == 'CSS' || $paths->page_id == 'Administration' || $paths->page_id == 'Login' ) ) )
+		{
+			// yeah, we need to keep this page available. do nothing; allow execution to continue
+		}
+		else
+		{
+			if(!$n = getConfig('site_disabled_notice')) 
+			{
+				$n = 'The administrator has disabled the site. Please check back later.';
+			}
+			
+			$text = RenderMan::render($n) . '
+			<div class="info-box">
+				If you have an administrative account, you may <a href="'.makeUrlNS('Special', 'Login').'">log in</a> to the site.
+			</div>';
+			$paths->wiki_mode = 0;
+			die_semicritical('Site disabled', $text);
+		}
+	}
+	else if ( getConfig('site_disabled') == '1' && $session->user_level >= USER_LEVEL_ADMIN )
+	{
+		// If the site is disabled but the user has admin rights, allow browsing
+		// and stuff, but display the orange box notifying the admin.
+		$template->site_disabled = true;
+	}
+	
+	// At this point all of Enano is fully initialized and running and you're ready to do whatever you want.
+	$code = $plugins->setHook('common_post');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	profiler_log('Ran disabled-site checks and common_post');
+	
+	load_rank_data();
+	
+	profiler_log('Loaded user rank data');
+	
+	if ( isset($_GET['noheaders']) )
+		$template->no_headers = true;
 }
 
 profiler_log('common finished');
--- a/includes/common_cli.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/common_cli.php	Sun Mar 28 23:10:46 2010 -0400
@@ -34,7 +34,7 @@
 @include(ENANO_ROOT . $config_file);
 unset($dbuser, $dbpasswd);
 if ( !isset($dbdriver) )
-  $dbdriver = 'mysql';
+	$dbdriver = 'mysql';
 
 $db = new $dbdriver();
 $db->connect();
@@ -52,7 +52,7 @@
 $e = $db->sql_query('SELECT config_name, config_value FROM '.table_prefix.'config;');
 if ( !$e )
 {
-  $db->_die('Some critical configuration information could not be selected.');
+	$db->_die('Some critical configuration information could not be selected.');
 }
 // Used in die_semicritical to figure out whether to call getConfig() or not
 define('ENANO_CONFIG_FETCHED', '');
@@ -61,7 +61,7 @@
 $enano_config = Array();
 while($r = $db->fetchrow())
 {
-  $enano_config[$r['config_name']] = $r['config_value'];
+	$enano_config[$r['config_name']] = $r['config_value'];
 }
 
 $db->free_result();
@@ -71,12 +71,12 @@
 // Now that we have the config, check the Enano version.
 if ( enano_version(false, true) != $version && !defined('IN_ENANO_UPGRADE') )
 {
-  grinding_halt('Version mismatch', 'Trying to run Enano version '.$version.' on database version '.enano_version().', you might need to upgrade.');
+	grinding_halt('Version mismatch', 'Trying to run Enano version '.$version.' on database version '.enano_version().', you might need to upgrade.');
 }
 
 // Set our CDN path
 if ( !defined('cdnPath') )
-  define('cdnPath', getConfig('cdn_path', scriptPath));
+	define('cdnPath', getConfig('cdn_path', scriptPath));
 
 //
 // Low level maintenance
@@ -85,41 +85,41 @@
 // If the AES key size has been changed, bail out and fast
 if ( !getConfig('aes_key_size') )
 {
-  setConfig('aes_key_size', AES_BITS);
+	setConfig('aes_key_size', AES_BITS);
 }
 else if ( $ks = getConfig('aes_key_size') )
 {
-  if ( intval($ks) != AES_BITS )
-  {
-    grinding_halt('AES key size changed', 'Enano has detected that the AES key size in constants.php has been changed. This change cannot be performed after installation, otherwise the private key would have to be re-generated and all passwords would have to be re-encrypted.' . "\n\n" . 'Please change the key size back to ' . $ks . ' bits and rerun this script.');
-  }
+	if ( intval($ks) != AES_BITS )
+	{
+		grinding_halt('AES key size changed', 'Enano has detected that the AES key size in constants.php has been changed. This change cannot be performed after installation, otherwise the private key would have to be re-generated and all passwords would have to be re-encrypted.' . "\n\n" . 'Please change the key size back to ' . $ks . ' bits and rerun this script.');
+	}
 }
 
 // Same for AES block size
 if ( !getConfig('aes_block_size') )
 {
-  setConfig('aes_block_size', AES_BLOCKSIZE);
+	setConfig('aes_block_size', AES_BLOCKSIZE);
 }
 else if ( $ks = getConfig('aes_block_size') )
 {
-  if ( intval($ks) != AES_BLOCKSIZE )
-  {
-    grinding_halt('AES block size changed', "Enano has detected that the AES block size in constants.php has been changed. This change cannot be performed after installation, otherwise all passwords would have to be re-encrypted.\n\nPlease change the block size back to $ks bits and rerun this script.");
-  }
+	if ( intval($ks) != AES_BLOCKSIZE )
+	{
+		grinding_halt('AES block size changed', "Enano has detected that the AES block size in constants.php has been changed. This change cannot be performed after installation, otherwise all passwords would have to be re-encrypted.\n\nPlease change the block size back to $ks bits and rerun this script.");
+	}
 }
 
 // Is there no default language?
 if ( getConfig('default_language') === false && !defined('IN_ENANO_MIGRATION') )
 {
-  $q = $db->sql_query('SELECT lang_id FROM '.table_prefix.'language LIMIT 1;');
-  if ( !$q )
-    $db->_die('common.php - setting default language');
-  if ( $db->numrows() < 1 && !defined('ENANO_ALLOW_LOAD_NOLANG') )
-  {
-    grinding_halt('No languages', 'No languages are installed on the site, load from web interface for instructions on how to fix this.');
-  }
-  $row = $db->fetchrow();
-  setConfig('default_language', $row['lang_id']);
+	$q = $db->sql_query('SELECT lang_id FROM '.table_prefix.'language LIMIT 1;');
+	if ( !$q )
+		$db->_die('common.php - setting default language');
+	if ( $db->numrows() < 1 && !defined('ENANO_ALLOW_LOAD_NOLANG') )
+	{
+		grinding_halt('No languages', 'No languages are installed on the site, load from web interface for instructions on how to fix this.');
+	}
+	$row = $db->fetchrow();
+	setConfig('default_language', $row['lang_id']);
 }
 
 profiler_log('Ran checks');
@@ -144,11 +144,11 @@
 // Load plugins from common because we can't give plugins full abilities in object context
 if ( !defined('ENANO_NO_PLUGINS') )
 {
-  foreach ( $plugins->load_list as $f )
-  {
-    if ( file_exists(ENANO_ROOT . '/plugins/' . $f) )
-      include_once ENANO_ROOT . '/plugins/' . $f;
-  }
+	foreach ( $plugins->load_list as $f )
+	{
+		if ( file_exists(ENANO_ROOT . '/plugins/' . $f) )
+			include_once ENANO_ROOT . '/plugins/' . $f;
+	}
 }
 
 profiler_log('Loaded plugins');
@@ -170,87 +170,87 @@
 // script though, because that will make the DBAL look in the wrong place for the config file.
 if ( !defined('IN_ENANO_INSTALL') )
 {
-  // And here you have it, the de facto way to place a hook. Plugins can place hooks and hook
-  // into other plugins. You just never know.
-  $code = $plugins->setHook('base_classes_initted');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  profiler_log('Finished base_classes_initted hook');
-  
-  // One quick security check...
-  if ( isset($_SERVER['REMOTE_ADDR']) )
-  {
-    grinding_halt('REMOTE_ADDR detected', 'Detected a REMOTE_ADDR, this should not happen in CLI mode.');
-  }
-  $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+	// And here you have it, the de facto way to place a hook. Plugins can place hooks and hook
+	// into other plugins. You just never know.
+	$code = $plugins->setHook('base_classes_initted');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	profiler_log('Finished base_classes_initted hook');
+	
+	// One quick security check...
+	if ( isset($_SERVER['REMOTE_ADDR']) )
+	{
+		grinding_halt('REMOTE_ADDR detected', 'Detected a REMOTE_ADDR, this should not happen in CLI mode.');
+	}
+	$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
 
-  // All checks passed! Start the main components up.  
-  $session->start();
-  
-  // This is where plugins will want to add pages from 1.1.x on out. You can still add
-  // pages at base_classes_initted but the titles won't be localized. This is because
-  // the session manager has to be started before localization will work in the user's
-  // preferred language.
-  $code = $plugins->setHook('session_started');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  profiler_log('Ran session_started hook');
-  
-  $paths->init($paths->nslist['System'] . 'API');
-  
-  // We're ready for whatever life throws us now.
-  define('ENANO_MAINSTREAM', '');
-  
-  // If the site is disabled, bail out, unless we're trying to log in or administer the site
-  if(getConfig('site_disabled') == '1' && $session->user_level < USER_LEVEL_ADMIN)
-  {
-    if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->page_id == 'CSS' || $paths->page_id == 'Administration' || $paths->page_id == 'Login' ) ) )
-    {
-      // do nothing; allow execution to continue
-    }
-    else
-    {
-      if(!$n = getConfig('site_disabled_notice')) 
-      {
-        $n = 'The administrator has disabled the site. Please check back later.';
-      }
-      
-      $text = RenderMan::render($n) . '
-      <div class="info-box">
-        If you have an administrative account, you may <a href="'.makeUrlNS('Special', 'Login').'">log in</a> to the site.
-      </div>';
-      $paths->wiki_mode = 0;
-      die_semicritical('Site disabled', $text);
-    }
-  }
-  else if ( getConfig('site_disabled') == '1' && $session->user_level >= USER_LEVEL_ADMIN )
-  {
-    // If the site is disabled but the user has admin rights, allow browsing
-    // and stuff, but display the orange box notifying the admin.
-    $template->site_disabled = true;
-  }
-  
-  // At this point all of Enano is fully initialized and running and you're ready to do whatever you want.
-  $code = $plugins->setHook('common_post');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  profiler_log('Ran disabled-site checks and common_post');
-  
-  load_rank_data();
-  
-  profiler_log('Loaded user rank data');
-  
-  if ( isset($_GET['noheaders']) )
-    $template->no_headers = true;
+	// All checks passed! Start the main components up.  
+	$session->start();
+	
+	// This is where plugins will want to add pages from 1.1.x on out. You can still add
+	// pages at base_classes_initted but the titles won't be localized. This is because
+	// the session manager has to be started before localization will work in the user's
+	// preferred language.
+	$code = $plugins->setHook('session_started');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	profiler_log('Ran session_started hook');
+	
+	$paths->init($paths->nslist['System'] . 'API');
+	
+	// We're ready for whatever life throws us now.
+	define('ENANO_MAINSTREAM', '');
+	
+	// If the site is disabled, bail out, unless we're trying to log in or administer the site
+	if(getConfig('site_disabled') == '1' && $session->user_level < USER_LEVEL_ADMIN)
+	{
+		if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->page_id == 'CSS' || $paths->page_id == 'Administration' || $paths->page_id == 'Login' ) ) )
+		{
+			// do nothing; allow execution to continue
+		}
+		else
+		{
+			if(!$n = getConfig('site_disabled_notice')) 
+			{
+				$n = 'The administrator has disabled the site. Please check back later.';
+			}
+			
+			$text = RenderMan::render($n) . '
+			<div class="info-box">
+				If you have an administrative account, you may <a href="'.makeUrlNS('Special', 'Login').'">log in</a> to the site.
+			</div>';
+			$paths->wiki_mode = 0;
+			die_semicritical('Site disabled', $text);
+		}
+	}
+	else if ( getConfig('site_disabled') == '1' && $session->user_level >= USER_LEVEL_ADMIN )
+	{
+		// If the site is disabled but the user has admin rights, allow browsing
+		// and stuff, but display the orange box notifying the admin.
+		$template->site_disabled = true;
+	}
+	
+	// At this point all of Enano is fully initialized and running and you're ready to do whatever you want.
+	$code = $plugins->setHook('common_post');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	profiler_log('Ran disabled-site checks and common_post');
+	
+	load_rank_data();
+	
+	profiler_log('Loaded user rank data');
+	
+	if ( isset($_GET['noheaders']) )
+		$template->no_headers = true;
 }
 
 profiler_log('common finished');
--- a/includes/constants.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/constants.php	Sun Mar 28 23:10:46 2010 -0400
@@ -276,45 +276,45 @@
 
 // Our list of tables included in Enano
 $system_table_list = Array(
-    'categories',
-    'comments',
-    'config',
-    'logs',
-    'page_text',
-    'session_keys',
-    'pages',
-    'users',
-    'users_extra',
-    'themes',
-    'buddies',
-    'banlist',
-    'files',
-    'privmsgs',
-    'sidebar',
-    'hits',
-    'groups',
-    'group_members',
-    'acl',
-    'page_groups',
-    'page_group_members',
-    'tags',
-    'language',
-    'language_strings',
-    'lockout',
-    'search_index',
-    'captcha',
-    'diffiehellman',
-    'plugins',
-    'ranks'
-  );
+		'categories',
+		'comments',
+		'config',
+		'logs',
+		'page_text',
+		'session_keys',
+		'pages',
+		'users',
+		'users_extra',
+		'themes',
+		'buddies',
+		'banlist',
+		'files',
+		'privmsgs',
+		'sidebar',
+		'hits',
+		'groups',
+		'group_members',
+		'acl',
+		'page_groups',
+		'page_group_members',
+		'tags',
+		'language',
+		'language_strings',
+		'lockout',
+		'search_index',
+		'captcha',
+		'diffiehellman',
+		'plugins',
+		'ranks'
+	);
 
 if ( defined('table_prefix') && !defined('ENANO_TABLELIST_PREFIXED') )
 {
-  define('ENANO_TABLELIST_PREFIXED', 1);
-  foreach ( $system_table_list as $i => $_ )
-  {
-    $system_table_list[$i] = table_prefix . $system_table_list[$i];
-  }
+	define('ENANO_TABLELIST_PREFIXED', 1);
+	foreach ( $system_table_list as $i => $_ )
+	{
+		$system_table_list[$i] = table_prefix . $system_table_list[$i];
+	}
 }
 
 /*
@@ -328,335 +328,335 @@
 
 // IMPORTANT: this array can NEVER have items removed from it or key indexes changed
 $mime_types = Array (
-  'ai'      => 'application/postscript',
-  'aif'     => 'audio/x-aiff',
-  'aifc'    => 'audio/x-aiff',
-  'aiff'    => 'audio/x-aiff',
-  'au'      => 'audio/basic',
-  'avi'     => 'video/x-msvideo',
-  'bcpio'   => 'application/x-bcpio',
-  'bin'     => 'application/octet-stream',
-  'bmp'     => 'image/bmp',
-  'bz2'     => 'application/x-bzip',
-  'cdf'     => 'application/x-netcdf',
-  'cgm'     => 'image/cgm',
-  'class'   => 'application/octet-stream',
-  'cpio'    => 'application/x-cpio',
-  'cpt'     => 'application/mac-compactpro',
-  'csh'     => 'application/x-csh',
-  'css'     => 'text/css',
-  'dcr'     => 'application/x-director',
-  'dir'     => 'application/x-director',
-  'djv'     => 'image/vnd.djvu',
-  'djvu'    => 'image/vnd.djvu',
-  'dll'     => 'application/octet-stream',
-  'dms'     => 'application/octet-stream',
-  'doc'     => 'application/msword',
-  'dtd'     => 'application/xml-dtd',
-  'dvi'     => 'application/x-dvi',
-  'dxr'     => 'application/x-director',
-  'eps'     => 'application/postscript',
-  'etx'     => 'text/x-setext',
-  'exe'     => 'application/octet-stream',
-  'ez'      => 'application/andrew-inset',
-  'gif'     => 'image/gif',
-  'gram'    => 'application/srgs',
-  'grxml'   => 'application/srgs+xml',
-  'gtar'    => 'application/x-gtar',
-  'gz'      => 'application/x-gzip',
-  'hdf'     => 'application/x-hdf',
-  'hqx'     => 'application/mac-binhex40',
-  'htm'     => 'text/html',
-  'html'    => 'text/html',
-  'ice'     => 'x-conference/x-cooltalk',
-  'ico'     => 'image/x-icon',
-  'ics'     => 'text/calendar',
-  'ief'     => 'image/ief',
-  'ifb'     => 'text/calendar',
-  'iges'    => 'model/iges',
-  'igs'     => 'model/iges',
-  'jar'     => 'application/zip',
-  'jpe'     => 'image/jpeg',
-  'jpeg'    => 'image/jpeg',
-  'jpg'     => 'image/jpeg',
-  'js'      => 'application/x-javascript',
-  'kar'     => 'audio/midi',
-  'latex'   => 'application/x-latex',
-  'lha'     => 'application/octet-stream',
-  'lzh'     => 'application/octet-stream',
-  'm3u'     => 'audio/x-mpegurl',
-  'man'     => 'application/x-troff-man',
-  'mathml'  => 'application/mathml+xml',
-  'me'      => 'application/x-troff-me',
-  'mesh'    => 'model/mesh',
-  'mid'     => 'audio/midi',
-  'midi'    => 'audio/midi',
-  'mif'     => 'application/vnd.mif',
-  'mov'     => 'video/quicktime',
-  'movie'   => 'video/x-sgi-movie',
-  'mp2'     => 'audio/mpeg',
-  'mp3'     => 'audio/mpeg',
-  'mpe'     => 'video/mpeg',
-  'mpeg'    => 'video/mpeg',
-  'mpg'     => 'video/mpeg',
-  'mpga'    => 'audio/mpeg',
-  'ms'      => 'application/x-troff-ms',
-  'msh'     => 'model/mesh',
-  'mxu'     => 'video/vnd.mpegurl',
-  'nc'      => 'application/x-netcdf',
-  'oda'     => 'application/oda',
-  'ogg'     => 'application/ogg',
-  'ogm'     => 'application/ogg',
-  'pbm'     => 'image/x-portable-bitmap',
-  'pdb'     => 'chemical/x-pdb',
-  'pdf'     => 'application/pdf',
-  'pgm'     => 'image/x-portable-graymap',
-  'pgn'     => 'application/x-chess-pgn',
-  'png'     => 'image/png',
-  'pnm'     => 'image/x-portable-anymap',
-  'ppm'     => 'image/x-portable-pixmap',
-  'ppt'     => 'application/vnd.ms-powerpoint',
-  'ps'      => 'application/postscript',
-  'psd'     => 'image/x-photoshop',
-  'qt'      => 'video/quicktime',
-  'ra'      => 'audio/x-realaudio',
-  'ram'     => 'audio/x-pn-realaudio',
-  'ras'     => 'image/x-cmu-raster',
-  'rdf'     => 'text/xml',
-  'rgb'     => 'image/x-rgb',
-  'rm'      => 'audio/x-pn-realaudio',
-  'roff'    => 'application/x-troff',
-  'rpm'     => 'audio/x-pn-realaudio-plugin',
-  'rss'     => 'text/xml',
-  'rtf'     => 'text/rtf',
-  'rtx'     => 'text/richtext',
-  'sgm'     => 'text/sgml',
-  'sgml'    => 'text/sgml',
-  'sh'      => 'application/x-sh',
-  'shar'    => 'application/x-shar',
-  'silo'    => 'model/mesh',
-  'sit'     => 'application/x-stuffit',
-  'skd'     => 'application/x-koan',
-  'skm'     => 'application/x-koan',
-  'skp'     => 'application/x-koan',
-  'skt'     => 'application/x-koan',
-  'smi'     => 'application/smil',
-  'smil'    => 'application/smil',
-  'snd'     => 'audio/basic',
-  'so'      => 'application/octet-stream',
-  'spl'     => 'application/x-futuresplash',
-  'src'     => 'application/x-wais-source',
-  'stc'     => 'application/zip',
-  'std'     => 'application/zip',
-  'sti'     => 'application/zip',
-  'stm'     => 'application/zip',
-  'stw'     => 'application/zip',
-  'sv4cpio' => 'application/x-sv4cpio',
-  'sv4crc'  => 'application/x-sv4crc',
-  'svg'     => 'image/svg+xml',
-  'swf'     => 'application/x-shockwave-flash',
-  'sxc'     => 'application/zip',
-  'sxd'     => 'application/zip',
-  'sxi'     => 'application/zip',
-  'sxm'     => 'application/zip',
-  'sxw'     => 'application/zip',
-  't'       => 'application/x-troff',
-  'tar'     => 'application/x-tar',
-  'tcl'     => 'application/x-tcl',
-  'tex'     => 'application/x-tex',
-  'texi'    => 'application/x-texinfo',
-  'texinfo' => 'application/x-texinfo',
-  'tif'     => 'image/tiff',
-  'tiff'    => 'image/tiff',
-  'tr'      => 'application/x-troff',
-  'tsv'     => 'text/tab-separated-values',
-  'txt'     => 'text/plain',
-  'ustar'   => 'application/x-ustar',
-  'vcd'     => 'application/x-cdlink',
-  'vrml'    => 'model/vrml',
-  'vxml'    => 'application/voicexml+xml',
-  'wav'     => 'audio/x-wav',
-  'wbmp'    => 'image/vnd.wap.wbmp',
-  'wbxml'   => 'application/vnd.wap.wbxml',
-  'wml'     => 'text/vnd.wap.wml',
-  'wmlc'    => 'application/vnd.wap.wmlc',
-  'wmls'    => 'text/vnd.wap.wmlscript',
-  'wmlsc'   => 'application/vnd.wap.wmlscriptc',
-  'wrl'     => 'model/vrml',
-  'xbm'     => 'image/x-xbitmap',
-  'xcf'     => 'image/xcf',
-  'xht'     => 'application/xhtml+xml',
-  'xhtml'   => 'application/xhtml+xml',
-  'xls'     => 'application/vnd.ms-excel',
-  'xml'     => 'text/xml',
-  'xpi'     => 'application/zip',
-  'xpm'     => 'image/x-xpixmap',
-  'xsl'     => 'text/xml',
-  'xslt'    => 'text/xml',
-  'xwd'     => 'image/x-xwindowdump',
-  'xyz'     => 'chemical/x-xyz',
-  'zip'     => 'application/zip',
-  'odt' => 'application/vnd.oasis.opendocument.text',
-  'ott' => 'application/vnd.oasis.opendocument.text-template',
-  'odg' => 'application/vnd.oasis.opendocument.graphics',
-  'otg' => 'application/vnd.oasis.opendocument.graphics-template',
-  'odp' => 'application/vnd.oasis.opendocument.presentation',
-  'otp' => 'application/vnd.oasis.opendocument.presentation-template',
-  'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
-  'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
-  'odc' => 'application/vnd.oasis.opendocument.chart',
-  'otc' => 'application/vnd.oasis.opendocument.chart-template',
-  'odi' => 'application/vnd.oasis.opendocument.image',
-  'oti' => 'application/vnd.oasis.opendocument.image-template',
-  'odf' => 'application/vnd.oasis.opendocument.formula',
-  'otf' => 'application/vnd.oasis.opendocument.formula-template',
-  'odm' => 'application/vnd.oasis.opendocument.text-master',
-  'oth' => 'application/vnd.oasis.opendocument.text-web'
+	'ai'      => 'application/postscript',
+	'aif'     => 'audio/x-aiff',
+	'aifc'    => 'audio/x-aiff',
+	'aiff'    => 'audio/x-aiff',
+	'au'      => 'audio/basic',
+	'avi'     => 'video/x-msvideo',
+	'bcpio'   => 'application/x-bcpio',
+	'bin'     => 'application/octet-stream',
+	'bmp'     => 'image/bmp',
+	'bz2'     => 'application/x-bzip',
+	'cdf'     => 'application/x-netcdf',
+	'cgm'     => 'image/cgm',
+	'class'   => 'application/octet-stream',
+	'cpio'    => 'application/x-cpio',
+	'cpt'     => 'application/mac-compactpro',
+	'csh'     => 'application/x-csh',
+	'css'     => 'text/css',
+	'dcr'     => 'application/x-director',
+	'dir'     => 'application/x-director',
+	'djv'     => 'image/vnd.djvu',
+	'djvu'    => 'image/vnd.djvu',
+	'dll'     => 'application/octet-stream',
+	'dms'     => 'application/octet-stream',
+	'doc'     => 'application/msword',
+	'dtd'     => 'application/xml-dtd',
+	'dvi'     => 'application/x-dvi',
+	'dxr'     => 'application/x-director',
+	'eps'     => 'application/postscript',
+	'etx'     => 'text/x-setext',
+	'exe'     => 'application/octet-stream',
+	'ez'      => 'application/andrew-inset',
+	'gif'     => 'image/gif',
+	'gram'    => 'application/srgs',
+	'grxml'   => 'application/srgs+xml',
+	'gtar'    => 'application/x-gtar',
+	'gz'      => 'application/x-gzip',
+	'hdf'     => 'application/x-hdf',
+	'hqx'     => 'application/mac-binhex40',
+	'htm'     => 'text/html',
+	'html'    => 'text/html',
+	'ice'     => 'x-conference/x-cooltalk',
+	'ico'     => 'image/x-icon',
+	'ics'     => 'text/calendar',
+	'ief'     => 'image/ief',
+	'ifb'     => 'text/calendar',
+	'iges'    => 'model/iges',
+	'igs'     => 'model/iges',
+	'jar'     => 'application/zip',
+	'jpe'     => 'image/jpeg',
+	'jpeg'    => 'image/jpeg',
+	'jpg'     => 'image/jpeg',
+	'js'      => 'application/x-javascript',
+	'kar'     => 'audio/midi',
+	'latex'   => 'application/x-latex',
+	'lha'     => 'application/octet-stream',
+	'lzh'     => 'application/octet-stream',
+	'm3u'     => 'audio/x-mpegurl',
+	'man'     => 'application/x-troff-man',
+	'mathml'  => 'application/mathml+xml',
+	'me'      => 'application/x-troff-me',
+	'mesh'    => 'model/mesh',
+	'mid'     => 'audio/midi',
+	'midi'    => 'audio/midi',
+	'mif'     => 'application/vnd.mif',
+	'mov'     => 'video/quicktime',
+	'movie'   => 'video/x-sgi-movie',
+	'mp2'     => 'audio/mpeg',
+	'mp3'     => 'audio/mpeg',
+	'mpe'     => 'video/mpeg',
+	'mpeg'    => 'video/mpeg',
+	'mpg'     => 'video/mpeg',
+	'mpga'    => 'audio/mpeg',
+	'ms'      => 'application/x-troff-ms',
+	'msh'     => 'model/mesh',
+	'mxu'     => 'video/vnd.mpegurl',
+	'nc'      => 'application/x-netcdf',
+	'oda'     => 'application/oda',
+	'ogg'     => 'application/ogg',
+	'ogm'     => 'application/ogg',
+	'pbm'     => 'image/x-portable-bitmap',
+	'pdb'     => 'chemical/x-pdb',
+	'pdf'     => 'application/pdf',
+	'pgm'     => 'image/x-portable-graymap',
+	'pgn'     => 'application/x-chess-pgn',
+	'png'     => 'image/png',
+	'pnm'     => 'image/x-portable-anymap',
+	'ppm'     => 'image/x-portable-pixmap',
+	'ppt'     => 'application/vnd.ms-powerpoint',
+	'ps'      => 'application/postscript',
+	'psd'     => 'image/x-photoshop',
+	'qt'      => 'video/quicktime',
+	'ra'      => 'audio/x-realaudio',
+	'ram'     => 'audio/x-pn-realaudio',
+	'ras'     => 'image/x-cmu-raster',
+	'rdf'     => 'text/xml',
+	'rgb'     => 'image/x-rgb',
+	'rm'      => 'audio/x-pn-realaudio',
+	'roff'    => 'application/x-troff',
+	'rpm'     => 'audio/x-pn-realaudio-plugin',
+	'rss'     => 'text/xml',
+	'rtf'     => 'text/rtf',
+	'rtx'     => 'text/richtext',
+	'sgm'     => 'text/sgml',
+	'sgml'    => 'text/sgml',
+	'sh'      => 'application/x-sh',
+	'shar'    => 'application/x-shar',
+	'silo'    => 'model/mesh',
+	'sit'     => 'application/x-stuffit',
+	'skd'     => 'application/x-koan',
+	'skm'     => 'application/x-koan',
+	'skp'     => 'application/x-koan',
+	'skt'     => 'application/x-koan',
+	'smi'     => 'application/smil',
+	'smil'    => 'application/smil',
+	'snd'     => 'audio/basic',
+	'so'      => 'application/octet-stream',
+	'spl'     => 'application/x-futuresplash',
+	'src'     => 'application/x-wais-source',
+	'stc'     => 'application/zip',
+	'std'     => 'application/zip',
+	'sti'     => 'application/zip',
+	'stm'     => 'application/zip',
+	'stw'     => 'application/zip',
+	'sv4cpio' => 'application/x-sv4cpio',
+	'sv4crc'  => 'application/x-sv4crc',
+	'svg'     => 'image/svg+xml',
+	'swf'     => 'application/x-shockwave-flash',
+	'sxc'     => 'application/zip',
+	'sxd'     => 'application/zip',
+	'sxi'     => 'application/zip',
+	'sxm'     => 'application/zip',
+	'sxw'     => 'application/zip',
+	't'       => 'application/x-troff',
+	'tar'     => 'application/x-tar',
+	'tcl'     => 'application/x-tcl',
+	'tex'     => 'application/x-tex',
+	'texi'    => 'application/x-texinfo',
+	'texinfo' => 'application/x-texinfo',
+	'tif'     => 'image/tiff',
+	'tiff'    => 'image/tiff',
+	'tr'      => 'application/x-troff',
+	'tsv'     => 'text/tab-separated-values',
+	'txt'     => 'text/plain',
+	'ustar'   => 'application/x-ustar',
+	'vcd'     => 'application/x-cdlink',
+	'vrml'    => 'model/vrml',
+	'vxml'    => 'application/voicexml+xml',
+	'wav'     => 'audio/x-wav',
+	'wbmp'    => 'image/vnd.wap.wbmp',
+	'wbxml'   => 'application/vnd.wap.wbxml',
+	'wml'     => 'text/vnd.wap.wml',
+	'wmlc'    => 'application/vnd.wap.wmlc',
+	'wmls'    => 'text/vnd.wap.wmlscript',
+	'wmlsc'   => 'application/vnd.wap.wmlscriptc',
+	'wrl'     => 'model/vrml',
+	'xbm'     => 'image/x-xbitmap',
+	'xcf'     => 'image/xcf',
+	'xht'     => 'application/xhtml+xml',
+	'xhtml'   => 'application/xhtml+xml',
+	'xls'     => 'application/vnd.ms-excel',
+	'xml'     => 'text/xml',
+	'xpi'     => 'application/zip',
+	'xpm'     => 'image/x-xpixmap',
+	'xsl'     => 'text/xml',
+	'xslt'    => 'text/xml',
+	'xwd'     => 'image/x-xwindowdump',
+	'xyz'     => 'chemical/x-xyz',
+	'zip'     => 'application/zip',
+	'odt' => 'application/vnd.oasis.opendocument.text',
+	'ott' => 'application/vnd.oasis.opendocument.text-template',
+	'odg' => 'application/vnd.oasis.opendocument.graphics',
+	'otg' => 'application/vnd.oasis.opendocument.graphics-template',
+	'odp' => 'application/vnd.oasis.opendocument.presentation',
+	'otp' => 'application/vnd.oasis.opendocument.presentation-template',
+	'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+	'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
+	'odc' => 'application/vnd.oasis.opendocument.chart',
+	'otc' => 'application/vnd.oasis.opendocument.chart-template',
+	'odi' => 'application/vnd.oasis.opendocument.image',
+	'oti' => 'application/vnd.oasis.opendocument.image-template',
+	'odf' => 'application/vnd.oasis.opendocument.formula',
+	'otf' => 'application/vnd.oasis.opendocument.formula-template',
+	'odm' => 'application/vnd.oasis.opendocument.text-master',
+	'oth' => 'application/vnd.oasis.opendocument.text-web'
 );
 
 $mimetype_extlist = Array(
-  'application/andrew-inset'=>'ez',
-  'application/mac-binhex40'=>'hqx',
-  'application/mac-compactpro'=>'cpt',
-  'application/mathml+xml'=>'mathml',
-  'application/msword'=>'doc',
-  'application/octet-stream'=>'bin dms lha lzh exe class so dll',
-  'application/oda'=>'oda',
-  'application/ogg'=>'ogg ogm',
-  'application/pdf'=>'pdf',
-  'application/postscript'=>'ai eps ps',
-  'application/rdf+xml'=>'rdf',
-  'application/smil'=>'smi smil',
-  'application/srgs'=>'gram',
-  'application/srgs+xml'=>'grxml',
-  'application/vnd.mif'=>'mif',
-  'application/vnd.ms-excel'=>'xls',
-  'application/vnd.ms-powerpoint'=>'ppt',
-  'application/vnd.wap.wbxml'=>'wbxml',
-  'application/vnd.wap.wmlc'=>'wmlc',
-  'application/vnd.wap.wmlscriptc'=>'wmlsc',
-  'application/voicexml+xml'=>'vxml',
-  'application/x-bcpio'=>'bcpio',
-  'application/x-bzip'=>'gz bz2',
-  'application/x-cdlink'=>'vcd',
-  'application/x-chess-pgn'=>'pgn',
-  'application/x-cpio'=>'cpio',
-  'application/x-csh'=>'csh',
-  'application/x-director'=>'dcr dir dxr',
-  'application/x-dvi'=>'dvi',
-  'application/x-futuresplash'=>'spl',
-  'application/x-gtar'=>'gtar tar',
-  'application/x-gzip'=>'gz',
-  'application/x-hdf'=>'hdf',
-  'application/x-jar'=>'jar',
-  'application/x-javascript'=>'js',
-  'application/x-koan'=>'skp skd skt skm',
-  'application/x-latex'=>'latex',
-  'application/x-netcdf'=>'nc cdf',
-  'application/x-sh'=>'sh',
-  'application/x-shar'=>'shar',
-  'application/x-shockwave-flash'=>'swf',
-  'application/x-stuffit'=>'sit',
-  'application/x-sv4cpio'=>'sv4cpio',
-  'application/x-sv4crc'=>'sv4crc',
-  'application/x-tar'=>'tar',
-  'application/x-tcl'=>'tcl',
-  'application/x-tex'=>'tex',
-  'application/x-texinfo'=>'texinfo texi',
-  'application/x-troff'=>'t tr roff',
-  'application/x-troff-man'=>'man',
-  'application/x-troff-me'=>'me',
-  'application/x-troff-ms'=>'ms',
-  'application/x-ustar'=>'ustar',
-  'application/x-wais-source'=>'src',
-  'application/x-xpinstall'=>'xpi',
-  'application/xhtml+xml'=>'xhtml xht',
-  'application/xslt+xml'=>'xslt',
-  'application/xml'=>'xml xsl',
-  'application/xml-dtd'=>'dtd',
-  'application/zip'=>'zip jar xpi  sxc stc  sxd std   sxi sti   sxm stm   sxw stw  ',
-  'audio/basic'=>'au snd',
-  'audio/midi'=>'mid midi kar',
-  'audio/mpeg'=>'mpga mp2 mp3',
-  'audio/ogg'=>'ogg ',
-  'audio/x-aiff'=>'aif aiff aifc',
-  'audio/x-mpegurl'=>'m3u',
-  'audio/x-ogg'=>'ogg ',
-  'audio/x-pn-realaudio'=>'ram rm',
-  'audio/x-pn-realaudio-plugin'=>'rpm',
-  'audio/x-realaudio'=>'ra',
-  'audio/x-wav'=>'wav',
-  'chemical/x-pdb'=>'pdb',
-  'chemical/x-xyz'=>'xyz',
-  'image/bmp'=>'bmp',
-  'image/cgm'=>'cgm',
-  'image/gif'=>'gif',
-  'image/ief'=>'ief',
-  'image/jpeg'=>'jpeg jpg jpe',
-  'image/png'=>'png',
-  'image/svg+xml'=>'svg',
-  'image/tiff'=>'tiff tif',
-  'image/vnd.djvu'=>'djvu djv',
-  'image/vnd.wap.wbmp'=>'wbmp',
-  'image/x-cmu-raster'=>'ras',
-  'image/x-icon'=>'ico',
-  'image/x-portable-anymap'=>'pnm',
-  'image/x-portable-bitmap'=>'pbm',
-  'image/x-portable-graymap'=>'pgm',
-  'image/x-portable-pixmap'=>'ppm',
-  'image/x-rgb'=>'rgb',
-  'image/x-photoshop'=>'psd',
-  'image/x-xbitmap'=>'xbm',
-  'image/x-xpixmap'=>'xpm',
-  'image/x-xwindowdump'=>'xwd',
-  'model/iges'=>'igs iges',
-  'model/mesh'=>'msh mesh silo',
-  'model/vrml'=>'wrl vrml',
-  'text/calendar'=>'ics ifb',
-  'text/css'=>'css',
-  'text/html'=>'html htm',
-  'text/plain'=>'txt',
-  'text/richtext'=>'rtx',
-  'text/rtf'=>'rtf',
-  'text/sgml'=>'sgml sgm',
-  'text/tab-separated-values'=>'tsv',
-  'text/vnd.wap.wml'=>'wml',
-  'text/vnd.wap.wmlscript'=>'wmls',
-  'text/xml'=>'xml xsl xslt rss rdf',
-  'text/x-setext'=>'etx',
-  'video/mpeg'=>'mpeg mpg mpe',
-  'video/ogg'=>'ogm ogg',
-  'video/quicktime'=>'qt mov',
-  'video/vnd.mpegurl'=>'mxu',
-  'video/x-msvideo'=>'avi',
-  'video/x-ogg'=>'ogm ogg',
-  'video/x-sgi-movie'=>'movie',
-  'x-conference/x-cooltalk'=>'ice',
-  // Added for Enano
-  'image/xcf' => 'xcf xcfbz2 xcf.bz2',
-  'application/vnd.oasis.opendocument.text' => 'odt',
-  'application/vnd.oasis.opendocument.text-template' => 'ott',
-  'application/vnd.oasis.opendocument.graphics' => 'odg',
-  'application/vnd.oasis.opendocument.graphics-template' => 'otg',
-  'application/vnd.oasis.opendocument.presentation' => 'odp',
-  'application/vnd.oasis.opendocument.presentation-template' => 'otp',
-  'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
-  'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
-  'application/vnd.oasis.opendocument.chart' => 'odc',
-  'application/vnd.oasis.opendocument.chart-template' => 'otc',
-  'application/vnd.oasis.opendocument.image' => 'odi',
-  'application/vnd.oasis.opendocument.image-template' => 'oti',
-  'application/vnd.oasis.opendocument.formula' => 'odf',
-  'application/vnd.oasis.opendocument.formula-template' => 'otf',
-  'application/vnd.oasis.opendocument.text-master' => 'odm',
-  'application/vnd.oasis.opendocument.text-web' => 'oth'
+	'application/andrew-inset'=>'ez',
+	'application/mac-binhex40'=>'hqx',
+	'application/mac-compactpro'=>'cpt',
+	'application/mathml+xml'=>'mathml',
+	'application/msword'=>'doc',
+	'application/octet-stream'=>'bin dms lha lzh exe class so dll',
+	'application/oda'=>'oda',
+	'application/ogg'=>'ogg ogm',
+	'application/pdf'=>'pdf',
+	'application/postscript'=>'ai eps ps',
+	'application/rdf+xml'=>'rdf',
+	'application/smil'=>'smi smil',
+	'application/srgs'=>'gram',
+	'application/srgs+xml'=>'grxml',
+	'application/vnd.mif'=>'mif',
+	'application/vnd.ms-excel'=>'xls',
+	'application/vnd.ms-powerpoint'=>'ppt',
+	'application/vnd.wap.wbxml'=>'wbxml',
+	'application/vnd.wap.wmlc'=>'wmlc',
+	'application/vnd.wap.wmlscriptc'=>'wmlsc',
+	'application/voicexml+xml'=>'vxml',
+	'application/x-bcpio'=>'bcpio',
+	'application/x-bzip'=>'gz bz2',
+	'application/x-cdlink'=>'vcd',
+	'application/x-chess-pgn'=>'pgn',
+	'application/x-cpio'=>'cpio',
+	'application/x-csh'=>'csh',
+	'application/x-director'=>'dcr dir dxr',
+	'application/x-dvi'=>'dvi',
+	'application/x-futuresplash'=>'spl',
+	'application/x-gtar'=>'gtar tar',
+	'application/x-gzip'=>'gz',
+	'application/x-hdf'=>'hdf',
+	'application/x-jar'=>'jar',
+	'application/x-javascript'=>'js',
+	'application/x-koan'=>'skp skd skt skm',
+	'application/x-latex'=>'latex',
+	'application/x-netcdf'=>'nc cdf',
+	'application/x-sh'=>'sh',
+	'application/x-shar'=>'shar',
+	'application/x-shockwave-flash'=>'swf',
+	'application/x-stuffit'=>'sit',
+	'application/x-sv4cpio'=>'sv4cpio',
+	'application/x-sv4crc'=>'sv4crc',
+	'application/x-tar'=>'tar',
+	'application/x-tcl'=>'tcl',
+	'application/x-tex'=>'tex',
+	'application/x-texinfo'=>'texinfo texi',
+	'application/x-troff'=>'t tr roff',
+	'application/x-troff-man'=>'man',
+	'application/x-troff-me'=>'me',
+	'application/x-troff-ms'=>'ms',
+	'application/x-ustar'=>'ustar',
+	'application/x-wais-source'=>'src',
+	'application/x-xpinstall'=>'xpi',
+	'application/xhtml+xml'=>'xhtml xht',
+	'application/xslt+xml'=>'xslt',
+	'application/xml'=>'xml xsl',
+	'application/xml-dtd'=>'dtd',
+	'application/zip'=>'zip jar xpi  sxc stc  sxd std   sxi sti   sxm stm   sxw stw  ',
+	'audio/basic'=>'au snd',
+	'audio/midi'=>'mid midi kar',
+	'audio/mpeg'=>'mpga mp2 mp3',
+	'audio/ogg'=>'ogg ',
+	'audio/x-aiff'=>'aif aiff aifc',
+	'audio/x-mpegurl'=>'m3u',
+	'audio/x-ogg'=>'ogg ',
+	'audio/x-pn-realaudio'=>'ram rm',
+	'audio/x-pn-realaudio-plugin'=>'rpm',
+	'audio/x-realaudio'=>'ra',
+	'audio/x-wav'=>'wav',
+	'chemical/x-pdb'=>'pdb',
+	'chemical/x-xyz'=>'xyz',
+	'image/bmp'=>'bmp',
+	'image/cgm'=>'cgm',
+	'image/gif'=>'gif',
+	'image/ief'=>'ief',
+	'image/jpeg'=>'jpeg jpg jpe',
+	'image/png'=>'png',
+	'image/svg+xml'=>'svg',
+	'image/tiff'=>'tiff tif',
+	'image/vnd.djvu'=>'djvu djv',
+	'image/vnd.wap.wbmp'=>'wbmp',
+	'image/x-cmu-raster'=>'ras',
+	'image/x-icon'=>'ico',
+	'image/x-portable-anymap'=>'pnm',
+	'image/x-portable-bitmap'=>'pbm',
+	'image/x-portable-graymap'=>'pgm',
+	'image/x-portable-pixmap'=>'ppm',
+	'image/x-rgb'=>'rgb',
+	'image/x-photoshop'=>'psd',
+	'image/x-xbitmap'=>'xbm',
+	'image/x-xpixmap'=>'xpm',
+	'image/x-xwindowdump'=>'xwd',
+	'model/iges'=>'igs iges',
+	'model/mesh'=>'msh mesh silo',
+	'model/vrml'=>'wrl vrml',
+	'text/calendar'=>'ics ifb',
+	'text/css'=>'css',
+	'text/html'=>'html htm',
+	'text/plain'=>'txt',
+	'text/richtext'=>'rtx',
+	'text/rtf'=>'rtf',
+	'text/sgml'=>'sgml sgm',
+	'text/tab-separated-values'=>'tsv',
+	'text/vnd.wap.wml'=>'wml',
+	'text/vnd.wap.wmlscript'=>'wmls',
+	'text/xml'=>'xml xsl xslt rss rdf',
+	'text/x-setext'=>'etx',
+	'video/mpeg'=>'mpeg mpg mpe',
+	'video/ogg'=>'ogm ogg',
+	'video/quicktime'=>'qt mov',
+	'video/vnd.mpegurl'=>'mxu',
+	'video/x-msvideo'=>'avi',
+	'video/x-ogg'=>'ogm ogg',
+	'video/x-sgi-movie'=>'movie',
+	'x-conference/x-cooltalk'=>'ice',
+	// Added for Enano
+	'image/xcf' => 'xcf xcfbz2 xcf.bz2',
+	'application/vnd.oasis.opendocument.text' => 'odt',
+	'application/vnd.oasis.opendocument.text-template' => 'ott',
+	'application/vnd.oasis.opendocument.graphics' => 'odg',
+	'application/vnd.oasis.opendocument.graphics-template' => 'otg',
+	'application/vnd.oasis.opendocument.presentation' => 'odp',
+	'application/vnd.oasis.opendocument.presentation-template' => 'otp',
+	'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
+	'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
+	'application/vnd.oasis.opendocument.chart' => 'odc',
+	'application/vnd.oasis.opendocument.chart-template' => 'otc',
+	'application/vnd.oasis.opendocument.image' => 'odi',
+	'application/vnd.oasis.opendocument.image-template' => 'oti',
+	'application/vnd.oasis.opendocument.formula' => 'odf',
+	'application/vnd.oasis.opendocument.formula-template' => 'otf',
+	'application/vnd.oasis.opendocument.text-master' => 'odm',
+	'application/vnd.oasis.opendocument.text-web' => 'oth'
 );
 
 $k = array_keys($mime_types);
 $mimetype_exps = Array();
 foreach($k as $s => $x)
 {
-  $mimetype_exps[$x] = pow(2, $s);
+	$mimetype_exps[$x] = pow(2, $s);
 }
 
 unset($k, $s, $x);
@@ -669,38 +669,38 @@
 ******************************************************************************/
 
 $GLOBALS[ "JPEG_Segment_Names" ] = array(
-  0xC0 =>  "SOF0",  0xC1 =>  "SOF1",  0xC2 =>  "SOF2",  0xC3 =>  "SOF4",
-  0xC5 =>  "SOF5",  0xC6 =>  "SOF6",  0xC7 =>  "SOF7",  0xC8 =>  "JPG",
-  0xC9 =>  "SOF9",  0xCA =>  "SOF10", 0xCB =>  "SOF11", 0xCD =>  "SOF13",
-  0xCE =>  "SOF14", 0xCF =>  "SOF15",
-  0xC4 =>  "DHT",   0xCC =>  "DAC",
-  
-  0xD0 =>  "RST0",  0xD1 =>  "RST1",  0xD2 =>  "RST2",  0xD3 =>  "RST3",
-  0xD4 =>  "RST4",  0xD5 =>  "RST5",  0xD6 =>  "RST6",  0xD7 =>  "RST7",
-  
-  0xD8 =>  "SOI",   0xD9 =>  "EOI",   0xDA =>  "SOS",   0xDB =>  "DQT",
-  0xDC =>  "DNL",   0xDD =>  "DRI",   0xDE =>  "DHP",   0xDF =>  "EXP",
-  
-  0xE0 =>  "APP0",  0xE1 =>  "APP1",  0xE2 =>  "APP2",  0xE3 =>  "APP3",
-  0xE4 =>  "APP4",  0xE5 =>  "APP5",  0xE6 =>  "APP6",  0xE7 =>  "APP7",
-  0xE8 =>  "APP8",  0xE9 =>  "APP9",  0xEA =>  "APP10", 0xEB =>  "APP11",
-  0xEC =>  "APP12", 0xED =>  "APP13", 0xEE =>  "APP14", 0xEF =>  "APP15",
-  
-  
-  0xF0 =>  "JPG0",  0xF1 =>  "JPG1",  0xF2 =>  "JPG2",  0xF3 =>  "JPG3",
-  0xF4 =>  "JPG4",  0xF5 =>  "JPG5",  0xF6 =>  "JPG6",  0xF7 =>  "JPG7",
-  0xF8 =>  "JPG8",  0xF9 =>  "JPG9",  0xFA =>  "JPG10", 0xFB =>  "JPG11",
-  0xFC =>  "JPG12", 0xFD =>  "JPG13",
-  
-  0xFE =>  "COM",   0x01 =>  "TEM",   0x02 =>  "RES",
+	0xC0 =>  "SOF0",  0xC1 =>  "SOF1",  0xC2 =>  "SOF2",  0xC3 =>  "SOF4",
+	0xC5 =>  "SOF5",  0xC6 =>  "SOF6",  0xC7 =>  "SOF7",  0xC8 =>  "JPG",
+	0xC9 =>  "SOF9",  0xCA =>  "SOF10", 0xCB =>  "SOF11", 0xCD =>  "SOF13",
+	0xCE =>  "SOF14", 0xCF =>  "SOF15",
+	0xC4 =>  "DHT",   0xCC =>  "DAC",
+	
+	0xD0 =>  "RST0",  0xD1 =>  "RST1",  0xD2 =>  "RST2",  0xD3 =>  "RST3",
+	0xD4 =>  "RST4",  0xD5 =>  "RST5",  0xD6 =>  "RST6",  0xD7 =>  "RST7",
+	
+	0xD8 =>  "SOI",   0xD9 =>  "EOI",   0xDA =>  "SOS",   0xDB =>  "DQT",
+	0xDC =>  "DNL",   0xDD =>  "DRI",   0xDE =>  "DHP",   0xDF =>  "EXP",
+	
+	0xE0 =>  "APP0",  0xE1 =>  "APP1",  0xE2 =>  "APP2",  0xE3 =>  "APP3",
+	0xE4 =>  "APP4",  0xE5 =>  "APP5",  0xE6 =>  "APP6",  0xE7 =>  "APP7",
+	0xE8 =>  "APP8",  0xE9 =>  "APP9",  0xEA =>  "APP10", 0xEB =>  "APP11",
+	0xEC =>  "APP12", 0xED =>  "APP13", 0xEE =>  "APP14", 0xEF =>  "APP15",
+	
+	
+	0xF0 =>  "JPG0",  0xF1 =>  "JPG1",  0xF2 =>  "JPG2",  0xF3 =>  "JPG3",
+	0xF4 =>  "JPG4",  0xF5 =>  "JPG5",  0xF6 =>  "JPG6",  0xF7 =>  "JPG7",
+	0xF8 =>  "JPG8",  0xF9 =>  "JPG9",  0xFA =>  "JPG10", 0xFB =>  "JPG11",
+	0xFC =>  "JPG12", 0xFD =>  "JPG13",
+	
+	0xFE =>  "COM",   0x01 =>  "TEM",   0x02 =>  "RES",
 );
 
 // DST profiles
 global $dst_profiles;
 $dst_profiles = array(
-    'off' => '0;0;0;0;60',
-    'usa' => '3;' . SECOND_SUNDAY . ';11;' . FIRST_SUNDAY . ';60',
-    'europe' => '3;' . LAST_SUNDAY . ';10;' . LAST_SUNDAY . ';60',
-    'australia' => '10;' . LAST_SUNDAY . ';3;' . LAST_SUNDAY . ';60',
-    'tasmania' => '10;' . FIRST_SUNDAY . ';3;' . LAST_SUNDAY . ';60'
-  );
+		'off' => '0;0;0;0;60',
+		'usa' => '3;' . SECOND_SUNDAY . ';11;' . FIRST_SUNDAY . ';60',
+		'europe' => '3;' . LAST_SUNDAY . ';10;' . LAST_SUNDAY . ';60',
+		'australia' => '10;' . LAST_SUNDAY . ';3;' . LAST_SUNDAY . ';60',
+		'tasmania' => '10;' . FIRST_SUNDAY . ';3;' . LAST_SUNDAY . ';60'
+	);
--- a/includes/dbal.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/dbal.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,1152 +13,1152 @@
  
 function db_error_handler($errno, $errstr, $errfile = false, $errline = false, $errcontext = Array() )
 {
-  if ( !defined('ENANO_DEBUG') )
-    return;
-  $e = error_reporting(0);
-  error_reporting($e);
-  if ( $e < $errno )
-    return;
-  $errtype = 'Notice';
-  switch ( $errno )
-  {
-    case E_ERROR: case E_USER_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: $errtype = 'Error'; break;
-    case E_WARNING: case E_USER_WARNING: case E_CORE_WARNING: case E_COMPILE_WARNING: $errtype = 'Warning'; break;
-  }
-  $debug = debug_backtrace();
-  if ( !isset($debug[0]['file']) )
-    return false;
-  $debug = $debug[0]['file'] . ', line ' . $debug[0]['line'];
-  echo "<b>$errtype:</b> $errstr<br />Error source:<pre>$debug</pre>";
+	if ( !defined('ENANO_DEBUG') )
+		return;
+	$e = error_reporting(0);
+	error_reporting($e);
+	if ( $e < $errno )
+		return;
+	$errtype = 'Notice';
+	switch ( $errno )
+	{
+		case E_ERROR: case E_USER_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: $errtype = 'Error'; break;
+		case E_WARNING: case E_USER_WARNING: case E_CORE_WARNING: case E_COMPILE_WARNING: $errtype = 'Warning'; break;
+	}
+	$debug = debug_backtrace();
+	if ( !isset($debug[0]['file']) )
+		return false;
+	$debug = $debug[0]['file'] . ', line ' . $debug[0]['line'];
+	echo "<b>$errtype:</b> $errstr<br />Error source:<pre>$debug</pre>";
 }
 
 global $db_sql_parse_time;
 $db_sql_parse_time = 0;
 
 class mysql {
-  var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
-  var $row = array();
+	var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
+	var $row = array();
 	var $rowset = array();
-  var $errhandler;
-  var $dbms_name = 'MySQL';
-  
-  /**
-   * Get a flat textual list of queries that have been made.
-   */
-  
-  function sql_backtrace()
-  {
-    return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
-  }
-  
-  /**
-   * Connect to the database, but only if a connection isn't already up.
-   */
-  
-  function ensure_connection()
-  {
-    if(!$this->_conn)
-    {
-      $this->connect();
-    }
-  }
-  
-  /**
-   * Exit Enano, dumping out a friendly error message indicating a database error on the way out.
-   * @param string Description or location of error; defaults to none
-   */
+	var $errhandler;
+	var $dbms_name = 'MySQL';
+	
+	/**
+ 	* Get a flat textual list of queries that have been made.
+ 	*/
+	
+	function sql_backtrace()
+	{
+		return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
+	}
+	
+	/**
+ 	* Connect to the database, but only if a connection isn't already up.
+ 	*/
+	
+	function ensure_connection()
+	{
+		if(!$this->_conn)
+		{
+			$this->connect();
+		}
+	}
+	
+	/**
+ 	* Exit Enano, dumping out a friendly error message indicating a database error on the way out.
+ 	* @param string Description or location of error; defaults to none
+ 	*/
  
-  function _die($t = '')
-  {
-    if ( defined('ENANO_HEADERS_SENT') )
-      ob_clean();
-    
-    $internal_text = $this->get_error($t);
-    
-    if ( defined('ENANO_CONFIG_FETCHED') )
-      // config is in, we can show a slightly nicer looking error page
-      die_semicritical('Database error', $internal_text);
-    else
-      // no config, display using no-DB template engine
-      grinding_halt('Database error', $internal_text);
-    
-    exit;
-  }
-  
-  /**
-   * Get the internal text used for a database error message.
-   * @param string Description or location of error; defaults to none
-   */
-  
-  function get_error($t = '')
-  {
-    @header('HTTP/1.1 500 Internal Server Error');
-    
-    $bt = $this->latest_query;
-    $e = htmlspecialchars($this->sql_error());
-    if ( empty($e) )
-      $e = '&lt;none&gt;';
-    
-    global $email;
-    
-    // As long as the admin's e-mail is accessible, display it.
-    $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) )
-                    ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;'
-                    : '';
-    
-    $internal_text = "<h3>The site was unable to finish serving your request.</h3>
-                      <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site{$email_info}.</p>
-                      <p>Description or location of error: $t<br />
-                      Error returned by $this->dbms_name extension: $e</p>
-                      <p>Most recent SQL query:</p>
-                      <pre>$bt</pre>";
-    return $internal_text;
-  }
-  
-  /**
-   * Exit Enano and output a JSON format datbase error.
-   * @param string Description or location of error; defaults to none
-   */
-  
-  function die_json($loc = false)
-  {
-    $e = str_replace("\n", "\\n", addslashes(htmlspecialchars($this->sql_error())));
-    $q = str_replace("\n", "\\n", addslashes($this->latest_query));
-    $loc = ( $loc ) ? addslashes("\n\nDescription or location of error: $loc") : "";
-    $loc .= "\n\nPlease report the full text of this error to the administrator of the site. If you believe that this is a bug with the software, please contact support@enanocms.org.";
-    $loc = str_replace("\n", "\\n", $loc);
-    $t = "{\"mode\":\"error\",\"error\":\"An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by {$this->dbms_name}: $e$loc\"}";
-    die($t);
-  }
-  
-  /**
-   * Connect to the database.
-   * @param bool If true, enables all other parameters. Defaults to false, which emans that you can call this function with no arguments and it will fetch information from the config file.
-   * @param string Database server hostname
-   * @param string Database server username
-   * @param string Database server password
-   * @param string Name of the database
-   * @param int Optional port number to connect over
-   */
-  
-  function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
-  {
-    if ( !defined('ENANO_SQL_CONSTANTS') )
-    {
-      define('ENANO_SQL_CONSTANTS', '');
-      define('ENANO_DBLAYER', 'MYSQL');
-      define('ENANO_SQLFUNC_LOWERCASE', 'lcase');
-      define('ENANO_SQL_MULTISTRING_PRFIX', '');
-      define('ENANO_SQL_BOOLEAN_TRUE', 'true');
-      define('ENANO_SQL_BOOLEAN_FALSE', 'false');
-    }
-    
-    if ( !$manual_credentials )
-    {
-      if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
-      {
-        @include(ENANO_ROOT.'/config.new.php');
-      }
-      else
-      {
-        @include(ENANO_ROOT.'/config.php');
-      }
-      
-      if ( isset($crypto_key) )
-        unset($crypto_key); // Get this sucker out of memory fast
-      if ( empty($dbport) )
-        $dbport = 3306;
-      
-      if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
-      {
-        // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
-        if ( !defined('scriptPath') )
-        {
-          if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
-          {
-            $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
-          }
-          if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
-          {
-            // user requested http://foo/enano as opposed to http://foo/enano/index.php
-            $_SERVER['REQUEST_URI'] .= '/index.php';
-          }
-          $sp = dirname($_SERVER['REQUEST_URI']);
-          if($sp == '/' || $sp == '\\') $sp = '';
-          define('scriptPath', $sp);
-          define('contentPath', "$sp/index.php?title=");
-        }
-        $loc = scriptPath . '/install/index.php';
-        define('IN_ENANO_INSTALL', 1);
-        $GLOBALS['lang'] = new Language('eng');
-        global $lang;
-        $lang->load_file('./language/english/core.json');
-        $lang->load_file('./language/english/install.json');
-        // header("Location: $loc");
-        redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 0);
-        exit;
-      }
-    }
-    
-    if ( !$dbport )
-      $dbport = 3306;
-    
-    if ( $dbhost && !empty($dbport) && $dbport != 3306 )
-      $dbhost = '127.0.0.1';
-    
-    $host_line = ( preg_match('/^:/', $dbhost) ) ? $dbhost : "{$dbhost}:{$dbport}";
-    
-    $this->_conn = @mysql_connect($host_line, $dbuser, $dbpasswd);
-    unset($dbuser);
-    unset($dbpasswd); // Security
-    
-    if ( !$this->_conn && !$manual_credentials )
-    {
-      grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to MySQL.<br />'.mysql_error().'</p>');
-    }
-    else if ( !$this->_conn && $manual_credentials )
-    {
-      return false;
-    }
-    
-    // Reset some variables
-    $this->query_backtrace = array();
-    $this->query_times = array();
-    $this->query_sources = array();
-    $this->num_queries = 0;
-    
-    $this->debug = ( defined('ENANO_DEBUG') );
-    
-    $q = @mysql_select_db($dbname);
-    
-    if ( !$q )
-    {
-      if ( $manual_credentials )
-        return false;
-      $this->_die('The database could not be selected.');
-    }
-    
-    // We're in!
-    return true;
-  }
-  
-  /**
-   * Make a SQL query.
-   * @param string Query
-   * @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
-   * @return resource or false on failure
-   */
-  
-  function sql_query($q, $log_query = true)
-  {
-    if ( $this->debug && function_exists('debug_backtrace') )
-    {
-      $backtrace = @debug_backtrace();
-      if ( is_array($backtrace) )
-      {
-        $bt = $backtrace[0];
-        if ( isset($backtrace[1]['class']) )
-        {
-          if ( $backtrace[1]['class'] == 'sessionManager' )
-          {
-            $bt = $backtrace[1];
-          }
-        }
-        $this->query_sources[$q] = substr($bt['file'], strlen(ENANO_ROOT) + 1) . ', line ' . $bt['line'];
-      }
-      unset($backtrace);
-    }
-    
-    $this->num_queries++;
-    if ( $log_query || defined('ENANO_DEBUG') )
-    {
-      $this->query_backtrace[] = $q;
-      $this->latest_query = $q;
-    }
-    // First make sure we have a connection
-    if ( !$this->_conn )
-    {
-      $this->_die('A database connection has not yet been established.');
-    }
-    // Start the timer
-    if ( $log_query || defined('ENANO_DEBUG') )
-      $time_start = microtime_float();
-    // Does this query look malicious?
-    if ( $log_query || defined('ENANO_DEBUG') )
-    {
-      if ( !$this->check_query($q) )
-      {
-        $this->report_query($q);
-        $debug = ( defined('ENANO_DEBUG') ) ? '<p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>' : '';
-        grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p>' . $debug);
-      }
-    }
-    
-    $r = mysql_query($q, $this->_conn);
-    
-    if ( $log_query )
-      $this->query_times[$q] = microtime_float() - $time_start;
-    
-    $this->latest_result = $r;
-    
-    return $r;
-  }
-  
-  /**
-   * Make a SQL query, but do not have PHP buffer all the results. Useful for queries that are expected to return a huge number of results.
-   * @param string Query
-   * @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
-   * @return resource or false on failure
-   */
-  
-  function sql_unbuffered_query($q, $log_query = true)
-  {
-    $this->num_queries++;
-    if ( $log_query || defined('ENANO_DEBUG') )
-      $this->query_backtrace[] = '(UNBUFFERED) ' . $q;
-    $this->latest_query = $q;
-    // First make sure we have a connection
-    if ( !$this->_conn )
-    {
-      $this->_die('A database connection has not yet been established.');
-    }
-    // Does this query look malicious?
-    if ( !$this->check_query($q) )
-    {
-      $this->report_query($q);
-      $debug = ( defined('ENANO_DEBUG') ) ? '<p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>' : '';
-      grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p>' . $debug);
-    }
-    
-    $time_start = microtime_float();
-    $r = @mysql_unbuffered_query($q, $this->_conn);
-    $this->query_times[$q] = microtime_float() - $time_start;
-    $this->latest_result = $r;
-    return $r;
-  }
-  
-  /**
-   * Performs heuristic analysis on a SQL query to check for known attack patterns.
-   * @param string $q the query to check
-   * @return bool true if query passed check, otherwise false
-   */
-  
-  function check_query($q, $debug = false)
-  {
-    global $db_sql_parse_time;
-    $ts = microtime_float();
-    
-    // remove properly escaped quotes
-    $q = str_replace('\\\\', '', $q);
-    $q = str_replace(array("\\\"", "\\'"), '', $q);
-    
-    // make sure quotes match
-    foreach ( array("'", '"') as $quote )
-    {
-      $n_quotes = get_char_count($q, $quote);
-      if ( $n_quotes % 2 == 1 )
-      {
-        // mismatched quotes
-        if ( $debug ) echo "Found mismatched quotes in query; parsed:\n$q\n";
-        return false;
-      }
-      // this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
-      $q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
-    }
-    $q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
-    
-    // quotes are now matched out. does this string have a comment marker in it?
-    if ( strstr($q, '--') )
-    {
-      return false;
-    }
-    
-    if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
-    {
-      if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
-      return false;
-    }
-    
-    $ts = microtime_float() - $ts;
-    $db_sql_parse_time += $ts;
-    return true;
-  }
-  
-  /**
-   * Set the internal result pointer to X
-   * @param int $pos The number of the row
-   * @param resource $result The MySQL result resource - if not given, the latest cached query is assumed
-   * @return true on success, false on failure
-   */
-   
-  function sql_data_seek($pos, $result = false)
-  {
-    if ( !$result )
-      $result = $this->latest_result;
-    if ( !$result )
-      return false;
-    
-    return mysql_data_seek($result, $pos) ? true : false;
-  }
-  
-  /**
-   * Reports a bad query to the admin
-   * @param string $query the naughty query
-   * @access private
-   */
-   
-  function report_query($query)
-  {
-    global $session;
-    if ( is_object($session) && defined('ENANO_MAINSTREAM') )
-    {
-      $username = $session->username;
-      $user_id = $session->user_id;
-    }
-    else
-    {
-      $username = 'Unavailable';
-      $user_id = 1;
-    } 
-    
-    $query = $this->escape($query);
-    $q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type,     action,         time_id,    date_string, page_text,      author,            author_uid,       edit_summary)
-                                                     VALUES(\'security\', \'sql_inject\', '.time().', \'\',        \''.$query.'\', \''.$username.'\', ' . $user_id . ', \''.$_SERVER['REMOTE_ADDR'].'\');');
-  }
-  
-  /**
-   * Returns the ID of the row last inserted.
-   * @return int
-   */
-  
-  function insert_id()
-  {
-    return @mysql_insert_id();
-  }
-  
-  /**
-   * Fetch one row from the given query as an associative array.
-   * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
-   * @return array
-   */
-  
-  function fetchrow($r = false)
-  {
-    if ( !$this->_conn )
-      return false;
-    
-    if ( !$r )
-      $r = $this->latest_result;
-    
-    if ( !$r )
-      $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
-    
-    $row = mysql_fetch_assoc($r);
-    
-    return integerize_array($row);
-  }
-  
-  /**
-   * Fetch one row from the given query as a numeric array.
-   * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
-   * @return array
-   */
-  
-  function fetchrow_num($r = false)
-  {
-    if ( !$r )
-      $r = $this->latest_result;
-    if ( !$r )
-      $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
-    
-    $row = mysql_fetch_row($r);
-    return integerize_array($row);
-  }
-  
-  /**
-   * Get the number of results for a given query.
-   * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
-   * @return array
-   */
-  
-  function numrows($r = false)
-  {
-    if ( !$r )
-      $r = $this->latest_result;
-    if ( !$r )
-      $this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
-    
-    return mysql_num_rows($r);
-  }
-  
-  /**
-   * Escape a string so that it may safely be included in a SQL query.
-   * @param string String to escape
-   * @return string Escaped string
-   */
-  
-  function escape($str)
-  {
-    $str = mysql_real_escape_string($str);
-    return $str;
-  }
-  
-  /**
-   * Free the given result from memory. Use this when completely finished with a result resource.
-   * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
-   * @return null
-   */
-  
-  function free_result($result = false)
-  {
-    if ( !$result )
-      $result = $this->latest_result;
-    if ( !$result )
-      return null;
-    
-    @mysql_free_result($result);
-    return null;
-  }
-  
-  /**
-   * Returns the number of rows affected.
-   * @return int
-   */
-  
-  function sql_affectedrows()
-  {
-    return mysql_affected_rows($this->_conn);
-  }
-  
-  /**
-   * Close the database connection
-   */
-  
-  function close()
-  {
-    @mysql_close($this->_conn);
-    unset($this->_conn);
-  }
-  
-  /**
-   * Get a list of columns in the given table
-   * @param string Table
-   * @return array
-   */
-  
-  function columns_in($table)
-  {
-    if ( !is_string($table) )
-      return false;
-    $q = $this->sql_query("SHOW COLUMNS IN $table;");
-    if ( !$q )
-      $this->_die();
-    
-    $columns = array();
-    while ( $row = $this->fetchrow_num() )
-    {
-      $columns[] = $row[0];
-    }
-    return $columns;
-  }
-  
-  /**
-   * Get the text of the most recent error.
-   * @return string
-   */
-  
-  function sql_error()
+	function _die($t = '')
+	{
+		if ( defined('ENANO_HEADERS_SENT') )
+			ob_clean();
+		
+		$internal_text = $this->get_error($t);
+		
+		if ( defined('ENANO_CONFIG_FETCHED') )
+			// config is in, we can show a slightly nicer looking error page
+			die_semicritical('Database error', $internal_text);
+		else
+			// no config, display using no-DB template engine
+			grinding_halt('Database error', $internal_text);
+		
+		exit;
+	}
+	
+	/**
+ 	* Get the internal text used for a database error message.
+ 	* @param string Description or location of error; defaults to none
+ 	*/
+	
+	function get_error($t = '')
+	{
+		@header('HTTP/1.1 500 Internal Server Error');
+		
+		$bt = $this->latest_query;
+		$e = htmlspecialchars($this->sql_error());
+		if ( empty($e) )
+			$e = '&lt;none&gt;';
+		
+		global $email;
+		
+		// As long as the admin's e-mail is accessible, display it.
+		$email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) )
+										? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;'
+										: '';
+		
+		$internal_text = "<h3>The site was unable to finish serving your request.</h3>
+											<p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site{$email_info}.</p>
+											<p>Description or location of error: $t<br />
+											Error returned by $this->dbms_name extension: $e</p>
+											<p>Most recent SQL query:</p>
+											<pre>$bt</pre>";
+		return $internal_text;
+	}
+	
+	/**
+ 	* Exit Enano and output a JSON format datbase error.
+ 	* @param string Description or location of error; defaults to none
+ 	*/
+	
+	function die_json($loc = false)
+	{
+		$e = str_replace("\n", "\\n", addslashes(htmlspecialchars($this->sql_error())));
+		$q = str_replace("\n", "\\n", addslashes($this->latest_query));
+		$loc = ( $loc ) ? addslashes("\n\nDescription or location of error: $loc") : "";
+		$loc .= "\n\nPlease report the full text of this error to the administrator of the site. If you believe that this is a bug with the software, please contact support@enanocms.org.";
+		$loc = str_replace("\n", "\\n", $loc);
+		$t = "{\"mode\":\"error\",\"error\":\"An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by {$this->dbms_name}: $e$loc\"}";
+		die($t);
+	}
+	
+	/**
+ 	* Connect to the database.
+ 	* @param bool If true, enables all other parameters. Defaults to false, which emans that you can call this function with no arguments and it will fetch information from the config file.
+ 	* @param string Database server hostname
+ 	* @param string Database server username
+ 	* @param string Database server password
+ 	* @param string Name of the database
+ 	* @param int Optional port number to connect over
+ 	*/
+	
+	function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
+	{
+		if ( !defined('ENANO_SQL_CONSTANTS') )
+		{
+			define('ENANO_SQL_CONSTANTS', '');
+			define('ENANO_DBLAYER', 'MYSQL');
+			define('ENANO_SQLFUNC_LOWERCASE', 'lcase');
+			define('ENANO_SQL_MULTISTRING_PRFIX', '');
+			define('ENANO_SQL_BOOLEAN_TRUE', 'true');
+			define('ENANO_SQL_BOOLEAN_FALSE', 'false');
+		}
+		
+		if ( !$manual_credentials )
+		{
+			if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
+			{
+				@include(ENANO_ROOT.'/config.new.php');
+			}
+			else
+			{
+				@include(ENANO_ROOT.'/config.php');
+			}
+			
+			if ( isset($crypto_key) )
+				unset($crypto_key); // Get this sucker out of memory fast
+			if ( empty($dbport) )
+				$dbport = 3306;
+			
+			if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
+			{
+				// scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
+				if ( !defined('scriptPath') )
+				{
+					if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
+					{
+						$_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
+					}
+					if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
+					{
+						// user requested http://foo/enano as opposed to http://foo/enano/index.php
+						$_SERVER['REQUEST_URI'] .= '/index.php';
+					}
+					$sp = dirname($_SERVER['REQUEST_URI']);
+					if($sp == '/' || $sp == '\\') $sp = '';
+					define('scriptPath', $sp);
+					define('contentPath', "$sp/index.php?title=");
+				}
+				$loc = scriptPath . '/install/index.php';
+				define('IN_ENANO_INSTALL', 1);
+				$GLOBALS['lang'] = new Language('eng');
+				global $lang;
+				$lang->load_file('./language/english/core.json');
+				$lang->load_file('./language/english/install.json');
+				// header("Location: $loc");
+				redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 0);
+				exit;
+			}
+		}
+		
+		if ( !$dbport )
+			$dbport = 3306;
+		
+		if ( $dbhost && !empty($dbport) && $dbport != 3306 )
+			$dbhost = '127.0.0.1';
+		
+		$host_line = ( preg_match('/^:/', $dbhost) ) ? $dbhost : "{$dbhost}:{$dbport}";
+		
+		$this->_conn = @mysql_connect($host_line, $dbuser, $dbpasswd);
+		unset($dbuser);
+		unset($dbpasswd); // Security
+		
+		if ( !$this->_conn && !$manual_credentials )
+		{
+			grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to MySQL.<br />'.mysql_error().'</p>');
+		}
+		else if ( !$this->_conn && $manual_credentials )
+		{
+			return false;
+		}
+		
+		// Reset some variables
+		$this->query_backtrace = array();
+		$this->query_times = array();
+		$this->query_sources = array();
+		$this->num_queries = 0;
+		
+		$this->debug = ( defined('ENANO_DEBUG') );
+		
+		$q = @mysql_select_db($dbname);
+		
+		if ( !$q )
+		{
+			if ( $manual_credentials )
+				return false;
+			$this->_die('The database could not be selected.');
+		}
+		
+		// We're in!
+		return true;
+	}
+	
+	/**
+ 	* Make a SQL query.
+ 	* @param string Query
+ 	* @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
+ 	* @return resource or false on failure
+ 	*/
+	
+	function sql_query($q, $log_query = true)
+	{
+		if ( $this->debug && function_exists('debug_backtrace') )
+		{
+			$backtrace = @debug_backtrace();
+			if ( is_array($backtrace) )
+			{
+				$bt = $backtrace[0];
+				if ( isset($backtrace[1]['class']) )
+				{
+					if ( $backtrace[1]['class'] == 'sessionManager' )
+					{
+						$bt = $backtrace[1];
+					}
+				}
+				$this->query_sources[$q] = substr($bt['file'], strlen(ENANO_ROOT) + 1) . ', line ' . $bt['line'];
+			}
+			unset($backtrace);
+		}
+		
+		$this->num_queries++;
+		if ( $log_query || defined('ENANO_DEBUG') )
+		{
+			$this->query_backtrace[] = $q;
+			$this->latest_query = $q;
+		}
+		// First make sure we have a connection
+		if ( !$this->_conn )
+		{
+			$this->_die('A database connection has not yet been established.');
+		}
+		// Start the timer
+		if ( $log_query || defined('ENANO_DEBUG') )
+			$time_start = microtime_float();
+		// Does this query look malicious?
+		if ( $log_query || defined('ENANO_DEBUG') )
+		{
+			if ( !$this->check_query($q) )
+			{
+				$this->report_query($q);
+				$debug = ( defined('ENANO_DEBUG') ) ? '<p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>' : '';
+				grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p>' . $debug);
+			}
+		}
+		
+		$r = mysql_query($q, $this->_conn);
+		
+		if ( $log_query )
+			$this->query_times[$q] = microtime_float() - $time_start;
+		
+		$this->latest_result = $r;
+		
+		return $r;
+	}
+	
+	/**
+ 	* Make a SQL query, but do not have PHP buffer all the results. Useful for queries that are expected to return a huge number of results.
+ 	* @param string Query
+ 	* @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
+ 	* @return resource or false on failure
+ 	*/
+	
+	function sql_unbuffered_query($q, $log_query = true)
+	{
+		$this->num_queries++;
+		if ( $log_query || defined('ENANO_DEBUG') )
+			$this->query_backtrace[] = '(UNBUFFERED) ' . $q;
+		$this->latest_query = $q;
+		// First make sure we have a connection
+		if ( !$this->_conn )
+		{
+			$this->_die('A database connection has not yet been established.');
+		}
+		// Does this query look malicious?
+		if ( !$this->check_query($q) )
+		{
+			$this->report_query($q);
+			$debug = ( defined('ENANO_DEBUG') ) ? '<p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>' : '';
+			grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p>' . $debug);
+		}
+		
+		$time_start = microtime_float();
+		$r = @mysql_unbuffered_query($q, $this->_conn);
+		$this->query_times[$q] = microtime_float() - $time_start;
+		$this->latest_result = $r;
+		return $r;
+	}
+	
+	/**
+ 	* Performs heuristic analysis on a SQL query to check for known attack patterns.
+ 	* @param string $q the query to check
+ 	* @return bool true if query passed check, otherwise false
+ 	*/
+	
+	function check_query($q, $debug = false)
 	{
-    return mysql_error();
+		global $db_sql_parse_time;
+		$ts = microtime_float();
+		
+		// remove properly escaped quotes
+		$q = str_replace('\\\\', '', $q);
+		$q = str_replace(array("\\\"", "\\'"), '', $q);
+		
+		// make sure quotes match
+		foreach ( array("'", '"') as $quote )
+		{
+			$n_quotes = get_char_count($q, $quote);
+			if ( $n_quotes % 2 == 1 )
+			{
+				// mismatched quotes
+				if ( $debug ) echo "Found mismatched quotes in query; parsed:\n$q\n";
+				return false;
+			}
+			// this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
+			$q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
+		}
+		$q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
+		
+		// quotes are now matched out. does this string have a comment marker in it?
+		if ( strstr($q, '--') )
+		{
+			return false;
+		}
+		
+		if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
+		{
+			if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
+			return false;
+		}
+		
+		$ts = microtime_float() - $ts;
+		$db_sql_parse_time += $ts;
+		return true;
+	}
+	
+	/**
+ 	* Set the internal result pointer to X
+ 	* @param int $pos The number of the row
+ 	* @param resource $result The MySQL result resource - if not given, the latest cached query is assumed
+ 	* @return true on success, false on failure
+ 	*/
+ 	
+	function sql_data_seek($pos, $result = false)
+	{
+		if ( !$result )
+			$result = $this->latest_result;
+		if ( !$result )
+			return false;
+		
+		return mysql_data_seek($result, $pos) ? true : false;
+	}
+	
+	/**
+ 	* Reports a bad query to the admin
+ 	* @param string $query the naughty query
+ 	* @access private
+ 	*/
+ 	
+	function report_query($query)
+	{
+		global $session;
+		if ( is_object($session) && defined('ENANO_MAINSTREAM') )
+		{
+			$username = $session->username;
+			$user_id = $session->user_id;
+		}
+		else
+		{
+			$username = 'Unavailable';
+			$user_id = 1;
+		} 
+		
+		$query = $this->escape($query);
+		$q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type,     action,         time_id,    date_string, page_text,      author,            author_uid,       edit_summary)
+ 																										VALUES(\'security\', \'sql_inject\', '.time().', \'\',        \''.$query.'\', \''.$username.'\', ' . $user_id . ', \''.$_SERVER['REMOTE_ADDR'].'\');');
+	}
+	
+	/**
+ 	* Returns the ID of the row last inserted.
+ 	* @return int
+ 	*/
+	
+	function insert_id()
+	{
+		return @mysql_insert_id();
+	}
+	
+	/**
+ 	* Fetch one row from the given query as an associative array.
+ 	* @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
+ 	* @return array
+ 	*/
+	
+	function fetchrow($r = false)
+	{
+		if ( !$this->_conn )
+			return false;
+		
+		if ( !$r )
+			$r = $this->latest_result;
+		
+		if ( !$r )
+			$this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
+		
+		$row = mysql_fetch_assoc($r);
+		
+		return integerize_array($row);
+	}
+	
+	/**
+ 	* Fetch one row from the given query as a numeric array.
+ 	* @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
+ 	* @return array
+ 	*/
+	
+	function fetchrow_num($r = false)
+	{
+		if ( !$r )
+			$r = $this->latest_result;
+		if ( !$r )
+			$this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
+		
+		$row = mysql_fetch_row($r);
+		return integerize_array($row);
+	}
+	
+	/**
+ 	* Get the number of results for a given query.
+ 	* @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
+ 	* @return array
+ 	*/
+	
+	function numrows($r = false)
+	{
+		if ( !$r )
+			$r = $this->latest_result;
+		if ( !$r )
+			$this->_die('$db->fetchrow(): an invalid MySQL resource was passed.');
+		
+		return mysql_num_rows($r);
 	}
-  
-  /**
-   * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
-   */
-  
-  function sql_report()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( !$session->get_permissions('mod_misc') )
-    {
-      die_friendly('Access denied', '<p>You are not authorized to generate a SQL backtrace.</p>');
-    }
-    // Create copies of variables that may be changed after header is called
-    $backtrace = $this->query_backtrace;
-    $times = $this->query_times;
-    $template->header();
-    echo '<h3>SQL query log and timetable</h3>';
-    echo '<div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">';
-    $i = 0;
-    foreach ( $backtrace as $query )
-    {
-      $i++;
-      $unbuffered = false;
-      if ( substr($query, 0, 13) == '(UNBUFFERED) ' )
-      {
-        $query = substr($query, 13);
-        $unbuffered = true;
-      }
-      if ( $i == 1 )
-      {
-        echo '<tr>
-                <th colspan="2">SQL backtrace for a normal page load of ' . htmlspecialchars($paths->cpage['urlname']) . '</th>
-              </tr>';
-      }
-      else
-      {
-        echo '<tr>
-                <th class="subhead" colspan="2">&nbsp;</th>
-              </tr>';
-      }
-      echo '<tr>
-              <td class="row2">Query:</td>
-              <td class="row1"><pre>' . htmlspecialchars($query) . '</pre></td>
-            </tr>
-            <tr>
-              <td class="row2">Time:</td>
-              <td class="row1">' . number_format($this->query_times[$query], 6) . ' seconds</td>
-            </tr>
-            <tr>
-              <td class="row2">Unbuffered:</td>
-              <td class="row1">' . ( $unbuffered ? 'Yes' : 'No' ) . '</td>
-            </tr>';
-      if ( isset($this->query_sources[$query]) )
-      {
-        echo '<tr>
-                <td class="row2">Called from:</td>
-                <td class="row1">' . $this->query_sources[$query] . '</td>
-              </tr>';
-      }
-    }
-    if ( function_exists('array_sum') )
-    {
-      $query_time_total = array_sum($this->query_times);
-      echo '<tr>
-              <th class="subhead" colspan="2">
-                Total time taken for SQL queries: ' . round( $query_time_total, 6 ) . ' seconds
-              </th>
-            </tr>';
-    }
-    echo '  </table>
-          </div>';
-    $template->footer();
-  }
+	
+	/**
+ 	* Escape a string so that it may safely be included in a SQL query.
+ 	* @param string String to escape
+ 	* @return string Escaped string
+ 	*/
+	
+	function escape($str)
+	{
+		$str = mysql_real_escape_string($str);
+		return $str;
+	}
+	
+	/**
+ 	* Free the given result from memory. Use this when completely finished with a result resource.
+ 	* @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
+ 	* @return null
+ 	*/
+	
+	function free_result($result = false)
+	{
+		if ( !$result )
+			$result = $this->latest_result;
+		if ( !$result )
+			return null;
+		
+		@mysql_free_result($result);
+		return null;
+	}
+	
+	/**
+ 	* Returns the number of rows affected.
+ 	* @return int
+ 	*/
+	
+	function sql_affectedrows()
+	{
+		return mysql_affected_rows($this->_conn);
+	}
+	
+	/**
+ 	* Close the database connection
+ 	*/
+	
+	function close()
+	{
+		@mysql_close($this->_conn);
+		unset($this->_conn);
+	}
+	
+	/**
+ 	* Get a list of columns in the given table
+ 	* @param string Table
+ 	* @return array
+ 	*/
+	
+	function columns_in($table)
+	{
+		if ( !is_string($table) )
+			return false;
+		$q = $this->sql_query("SHOW COLUMNS IN $table;");
+		if ( !$q )
+			$this->_die();
+		
+		$columns = array();
+		while ( $row = $this->fetchrow_num() )
+		{
+			$columns[] = $row[0];
+		}
+		return $columns;
+	}
+	
+	/**
+ 	* Get the text of the most recent error.
+ 	* @return string
+ 	*/
+	
+	function sql_error()
+	{
+		return mysql_error();
+	}
+	
+	/**
+ 	* Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
+ 	*/
+	
+	function sql_report()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( !$session->get_permissions('mod_misc') )
+		{
+			die_friendly('Access denied', '<p>You are not authorized to generate a SQL backtrace.</p>');
+		}
+		// Create copies of variables that may be changed after header is called
+		$backtrace = $this->query_backtrace;
+		$times = $this->query_times;
+		$template->header();
+		echo '<h3>SQL query log and timetable</h3>';
+		echo '<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">';
+		$i = 0;
+		foreach ( $backtrace as $query )
+		{
+			$i++;
+			$unbuffered = false;
+			if ( substr($query, 0, 13) == '(UNBUFFERED) ' )
+			{
+				$query = substr($query, 13);
+				$unbuffered = true;
+			}
+			if ( $i == 1 )
+			{
+				echo '<tr>
+								<th colspan="2">SQL backtrace for a normal page load of ' . htmlspecialchars($paths->cpage['urlname']) . '</th>
+							</tr>';
+			}
+			else
+			{
+				echo '<tr>
+								<th class="subhead" colspan="2">&nbsp;</th>
+							</tr>';
+			}
+			echo '<tr>
+							<td class="row2">Query:</td>
+							<td class="row1"><pre>' . htmlspecialchars($query) . '</pre></td>
+						</tr>
+						<tr>
+							<td class="row2">Time:</td>
+							<td class="row1">' . number_format($this->query_times[$query], 6) . ' seconds</td>
+						</tr>
+						<tr>
+							<td class="row2">Unbuffered:</td>
+							<td class="row1">' . ( $unbuffered ? 'Yes' : 'No' ) . '</td>
+						</tr>';
+			if ( isset($this->query_sources[$query]) )
+			{
+				echo '<tr>
+								<td class="row2">Called from:</td>
+								<td class="row1">' . $this->query_sources[$query] . '</td>
+							</tr>';
+			}
+		}
+		if ( function_exists('array_sum') )
+		{
+			$query_time_total = array_sum($this->query_times);
+			echo '<tr>
+							<th class="subhead" colspan="2">
+								Total time taken for SQL queries: ' . round( $query_time_total, 6 ) . ' seconds
+							</th>
+						</tr>';
+		}
+		echo '  </table>
+					</div>';
+		$template->footer();
+	}
 }
 
 class postgresql
 {
-  var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
-  var $row = array();
+	var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
+	var $row = array();
 	var $rowset = array();
-  var $errhandler;
-  var $dbms_name = 'PostgreSQL';
-  
-  /**
-   * Get a flat textual list of queries that have been made.
-   */
-  
-  function sql_backtrace()
-  {
-    return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
-  }
-  
-  /**
-   * Connect to the database, but only if a connection isn't already up.
-   */
-  
-  function ensure_connection()
-  {
-    if(!$this->_conn)
-    {
-      $this->connect();
-    }
-  }
-  
-  /**
-   * Exit Enano, dumping out a friendly error message indicating a database error on the way out.
-   * @param string Description or location of error; defaults to none
-   */
+	var $errhandler;
+	var $dbms_name = 'PostgreSQL';
+	
+	/**
+ 	* Get a flat textual list of queries that have been made.
+ 	*/
+	
+	function sql_backtrace()
+	{
+		return implode("\n-------------------------------------------------------------------\n", $this->query_backtrace);
+	}
+	
+	/**
+ 	* Connect to the database, but only if a connection isn't already up.
+ 	*/
+	
+	function ensure_connection()
+	{
+		if(!$this->_conn)
+		{
+			$this->connect();
+		}
+	}
+	
+	/**
+ 	* Exit Enano, dumping out a friendly error message indicating a database error on the way out.
+ 	* @param string Description or location of error; defaults to none
+ 	*/
  
-  function _die($t = '')
-  {
-    if ( defined('ENANO_HEADERS_SENT') )
-      ob_clean();
-    
-    $internal_text = $this->get_error($t);
-    
-    if ( defined('ENANO_CONFIG_FETCHED') )
-      // config is in, we can show a slightly nicer looking error page
-      die_semicritical('Database error', $internal_text);
-    else
-      // no config, display using no-DB template engine
-      grinding_halt('Database error', $internal_text);
-    
-    exit;
-  }
-  
-  /**
-   * Get the internal text used for a database error message.
-   * @param string Description or location of error; defaults to none
-   */
-  
-  function get_error($t = '')
-  {
-    @header('HTTP/1.1 500 Internal Server Error');
-    
-    $bt = $this->latest_query;
-    $e = htmlspecialchars($this->sql_error());
-    if ( empty($e) )
-      $e = '&lt;none&gt;';
-    
-    global $email;
-    
-    // As long as the admin's e-mail is accessible, display it.
-    $email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) )
-                    ? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;'
-                    : '';
-    
-    $internal_text = "<h3>The site was unable to finish serving your request.</h3>
-                      <p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site{$email_info}.</p>
-                      <p>Description or location of error: $t<br />
-                      Error returned by $this->dbms_name extension: $e</p>
-                      <p>Most recent SQL query:</p>
-                      <pre>$bt</pre>";
-    return $internal_text;
-  }
-  
-  /**
-   * Exit Enano and output a JSON format datbase error.
-   * @param string Description or location of error; defaults to none
-   */
-  
-  function die_json($loc = false)
-  {
-    $e = str_replace("\n", "\\n", addslashes(htmlspecialchars($this->sql_error())));
-    $q = str_replace("\n", "\\n", addslashes($this->latest_query));
-    $loc = ( $loc ) ? addslashes("\n\nDescription or location of error: $loc") : "";
-    $loc .= "\n\nPlease report the full text of this error to the administrator of the site. If you believe that this is a bug with the software, please contact support@enanocms.org.";
-    $loc = str_replace("\n", "\\n", $loc);
-    $t = "{\"mode\":\"error\",\"error\":\"An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by {$this->dbms_name}: $e$loc\"}";
-    die($t);
-  }
-  
-  /**
-   * Connect to the database.
-   * @param bool If true, enables all other parameters. Defaults to false, which emans that you can call this function with no arguments and it will fetch information from the config file.
-   * @param string Database server hostname
-   * @param string Database server username
-   * @param string Database server password
-   * @param string Name of the database
-   * @param int Optional port number to connect over
-   */
-  
-  function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
-  {
-    if ( !defined('ENANO_SQL_CONSTANTS') )
-    {
-      define('ENANO_SQL_CONSTANTS', '');
-      define('ENANO_DBLAYER', 'PGSQL');
-      define('ENANO_SQLFUNC_LOWERCASE', 'lower');
-      define('ENANO_SQL_MULTISTRING_PRFIX', 'E');
-      define('ENANO_SQL_BOOLEAN_TRUE', '1');
-      define('ENANO_SQL_BOOLEAN_FALSE', '0');
-    }
-    
-    if ( !$manual_credentials )
-    {
-      if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
-      {
-        @include(ENANO_ROOT.'/config.new.php');
-      }
-      else
-      {
-        @include(ENANO_ROOT.'/config.php');
-      }
-        
-      if ( isset($crypto_key) )
-        unset($crypto_key); // Get this sucker out of memory fast
-      if ( empty($dbport) )
-        $dbport = 5432;
-      
-      if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
-      {
-        // scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
-        if ( !defined('scriptPath') )
-        {
-          if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
-          {
-            $_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
-          }
-          if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
-          {
-            // user requested http://foo/enano as opposed to http://foo/enano/index.php
-            $_SERVER['REQUEST_URI'] .= '/index.php';
-          }
-          $sp = dirname($_SERVER['REQUEST_URI']);
-          if($sp == '/' || $sp == '\\') $sp = '';
-          define('scriptPath', $sp);
-          define('contentPath', "$sp/index.php?title=");
-        }
-        $loc = scriptPath . '/install.php';
-        // header("Location: $loc");
-        redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
-        exit;
-      }
-    }
-    
-    if ( empty($dbport) )
-      $dbport = 5432;
-    
-    $this->_conn = @pg_connect("host=$dbhost port=$dbport dbname=$dbname user=$dbuser password=$dbpasswd");
-    unset($dbuser);
-    unset($dbpasswd); // Security
-    
-    if ( !$this->_conn && !$manual_credentials )
-    {
-      grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to PostgreSQL.<br />'.pg_last_error().'</p>');
-    }
-    else if ( !$this->_conn && $manual_credentials )
-    {
-      return false;
-    }
-    
-    // Reset some variables
-    $this->query_backtrace = array();
-    $this->query_times = array();
-    $this->query_sources = array();
-    $this->num_queries = 0;
-    
-    $this->debug = ( defined('ENANO_DEBUG') );
-    
-    // We're in!
-    return true;
-  }
-  
-  /**
-   * Make a SQL query.
-   * @param string Query
-   * @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
-   * @return resource or false on failure
-   */
-  
-  function sql_query($q)
-  {
-    if ( $this->debug && function_exists('debug_backtrace') )
-    {
-      $backtrace = @debug_backtrace();
-      if ( is_array($backtrace) )
-      {
-        $bt = $backtrace[0];
-        if ( isset($backtrace[1]['class']) )
-        {
-          if ( $backtrace[1]['class'] == 'sessionManager' )
-          {
-            $bt = $backtrace[1];
-          }
-        }
-        $this->query_sources[$q] = substr($bt['file'], strlen(ENANO_ROOT) + 1) . ', line ' . $bt['line'];
-      }
-      unset($backtrace);
-    }
-    
-    $this->num_queries++;
-    $this->query_backtrace[] = $q;
-    $this->latest_query = $q;
-    // First make sure we have a connection
-    if ( !$this->_conn )
-    {
-      $this->_die('A database connection has not yet been established.');
-    }
-    // Does this query look malicious?
-    if ( !$this->check_query($q) )
-    {
-      $this->report_query($q);
-      grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>');
-    }
-    
-    $time_start = microtime_float();
-    $r = @pg_query($this->_conn, $q);
-    $this->query_times[$q] = microtime_float() - $time_start;
-    $this->latest_result = $r;
-    return $r;
-  }
-  
-  /**
-   * Make a SQL query, but do not have PHP buffer all the results. Useful for queries that are expected to return a huge number of results.
-   * @param string Query
-   * @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
-   * @return resource or false on failure
-   */
-  
-  function sql_unbuffered_query($q)
-  {
-    return $this->sql_query($q);
-  }
-  
-  /**
-   * Checks a SQL query for possible signs of injection attempts
-   * @param string $q the query to check
-   * @return bool true if query passed check, otherwise false
-   */
-  
-  function check_query($q, $debug = false)
-  {
-    global $db_sql_parse_time;
-    $ts = microtime_float();
-    
-    // remove properly escaped quotes
-    $q = str_replace(array("\\\"", "\\'"), '', $q);
-    
-    // make sure quotes match
-    foreach ( array("'", '"') as $quote )
-    {
-      if ( get_char_count($q, $quote) % 2 == 1 )
-      {
-        // mismatched quotes
-        return false;
-      }
-      // this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
-      $q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
-    }
-    $q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
-    
-    // quotes are now matched out. does this string have a comment marker in it?
-    if ( strstr($q, '--') )
-    {
-      return false;
-    }
-    
-    if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
-    {
-      if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
-      return false;
-    }
-    
-    $ts = microtime_float() - $ts;
-    $db_sql_parse_time += $ts;
-    return true;
-  }
-  
-  /**
-   * Set the internal result pointer to X
-   * @param int $pos The number of the row
-   * @param resource $result The PostgreSQL result resource - if not given, the latest cached query is assumed
-   * @return true on success, false on failure
-   */
-   
-  function sql_data_seek($pos, $result = false)
-  {
-    if ( !$result )
-      $result = $this->latest_result;
-    if ( !$result )
-      return false;
-    
-    return pg_result_seek($result, $pos) ? true : false;
-  }
-  
-  /**
-   * Reports a bad query to the admin
-   * @param string $query the naughty query
-   * @access private
-   */
-   
-  function report_query($query)
-  {
-    global $session;
-    if ( is_object($session) && defined('ENANO_MAINSTREAM') )
-    {
-      $username = $session->username;
-      $user_id = $session->user_id;
-    }
-    else
-    {
-      $username = 'Unavailable';
-      $user_id = 1;
-    } 
-    
-    $query = $this->escape($query);
-    $q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type,     action,         time_id,    date_string, page_text,      author,            author_uid,       edit_summary)
-                                                     VALUES(\'security\', \'sql_inject\', '.time().', \'\',        \''.$query.'\', \''.$username.'\', ' . $user_id . ', \''.$_SERVER['REMOTE_ADDR'].'\');');
-  }
-  
-  /**
-   * Returns the ID of the row last inserted.
-   * @return int
-   */
-  
-  function insert_id()
-  {
-    // list of primary keys in Enano tables
-    // this is a bit hackish, but not much choice.
-    static $primary_keys = false;
-    if ( !is_array($primary_keys) )
-    {
-      $primary_keys = array(
-        table_prefix . 'comments' => 'comment_id',
-        table_prefix . 'logs' => 'log_id',
-        table_prefix . 'users' => 'user_id',
-        table_prefix . 'banlist' => 'ban_id',
-        table_prefix . 'files' => 'file_id',
-        table_prefix . 'buddies' => 'buddy_id',
-        table_prefix . 'privmsgs' => 'message_id',
-        table_prefix . 'sidebar' => 'item_id',
-        table_prefix . 'hits' => 'hit_id',
-        table_prefix . 'groups' => 'group_id',
-        table_prefix . 'group_members' => 'member_id',
-        table_prefix . 'acl' => 'rule_id',
-        table_prefix . 'page_groups' => 'pg_id',
-        table_prefix . 'page_group_members' => 'pg_member_id',
-        table_prefix . 'tags' => 'tag_id',
-        table_prefix . 'lockout' => 'id',
-        table_prefix . 'language' => 'lang_id',
-        table_prefix . 'language_strings' => 'string_id',
-        table_prefix . 'ranks' => 'rank_id',
-        table_prefix . 'captcha' => 'code_id',
-        table_prefix . 'diffiehellman' => 'key_id',
-        table_prefix . 'plugins' => 'plugin_id'
-      );
-      // allow plugins to patch this if needed
-      global $plugins;
-      $code = $plugins->setHook('pgsql_set_serial_list');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-    }
-    $last_was_insert = preg_match('/^INSERT INTO ([a-z0-9_]+)/i', $this->latest_query, $match);
-    if ( $last_was_insert )
-    {
-      // trick based on PunBB's PostgreSQL driver
-      $table =& $match[1];
-      if ( isset($primary_keys[$table]) )
-      {
-        $primary_key = "{$table}_{$primary_keys[$table]}_seq";
-        $q = pg_query("SELECT CURRVAL('$primary_key');");
-        return ( $q ) ? intval(@pg_fetch_result($q, 0)) : false;
-      }
-    }
-    return false;
-  }
-  
-  /**
-   * Fetch one row from the given query as an associative array.
-   * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
-   * @return array
-   */
-  
-  function fetchrow($r = false)
-  {
-    if ( !$this->_conn )
-      return false;
-    if ( !$r )
-      $r = $this->latest_result;
-    if ( !$r )
-      $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
-    
-    $row = pg_fetch_assoc($r);
-    return integerize_array($row);
-  }
-  
-  /**
-   * Fetch one row from the given query as a numeric array.
-   * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
-   * @return array
-   */
-  
-  function fetchrow_num($r = false)
-  {
-    if ( !$r )
-      $r = $this->latest_result;
-    if ( !$r )
-      $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
-    
-    $row = pg_fetch_row($r);
-    return integerize_array($row);
-  }
-  
-  /**
-   * Get the number of results for a given query.
-   * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
-   * @return array
-   */
-  
-  function numrows($r = false)
-  {
-    if ( !$r )
-      $r = $this->latest_result;
-    if ( !$r )
-      $this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
-    
-    $n = pg_num_rows($r);
-    return $n;
-  }
-  
-  /**
-   * Escape a string so that it may safely be included in a SQL query.
-   * @param string String to escape
-   * @return string Escaped string
-   */
-  
-  function escape($str)
-  {
-    $str = pg_escape_string($this->_conn, $str);
-    return $str;
-  }
-  
-  /**
-   * Free the given result from memory. Use this when completely finished with a result resource.
-   * @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
-   * @return null
-   */
-  
-  function free_result($result = false)
-  {
-    if ( !$result )
-      $result = $this->latest_result;
-    
-    if ( !$result )
-      return null;
-    
-    @pg_free_result($result);
-    return null;
-  }
-  
-  /**
-   * Close the database connection
-   */
-  
-  function close()
-  {
-    @pg_close($this->_conn);
-    unset($this->_conn);
-  }
-  
-  /**
-   * Get a list of columns in the given table
-   * @param string Table
-   * @return array
-   */
-  
-  function columns_in($table)
-  {
-    if ( !is_string($table) )
-      return false;
-    $q = $this->sql_query("SELECT * FROM $table LIMIT 1 OFFSET 0;");
-    if ( !$q )
-      $this->_die();
-    if ( $this->numrows() < 1 )
-    {
-      // FIXME: Have another way to do this if the table is empty
-      return false;
-    }
-    
-    $row = $this->fetchrow();
-    $this->free_result();
-    return array_keys($row);
-  }
-  
+	function _die($t = '')
+	{
+		if ( defined('ENANO_HEADERS_SENT') )
+			ob_clean();
+		
+		$internal_text = $this->get_error($t);
+		
+		if ( defined('ENANO_CONFIG_FETCHED') )
+			// config is in, we can show a slightly nicer looking error page
+			die_semicritical('Database error', $internal_text);
+		else
+			// no config, display using no-DB template engine
+			grinding_halt('Database error', $internal_text);
+		
+		exit;
+	}
+	
+	/**
+ 	* Get the internal text used for a database error message.
+ 	* @param string Description or location of error; defaults to none
+ 	*/
+	
+	function get_error($t = '')
+	{
+		@header('HTTP/1.1 500 Internal Server Error');
+		
+		$bt = $this->latest_query;
+		$e = htmlspecialchars($this->sql_error());
+		if ( empty($e) )
+			$e = '&lt;none&gt;';
+		
+		global $email;
+		
+		// As long as the admin's e-mail is accessible, display it.
+		$email_info = ( defined('ENANO_CONFIG_FETCHED') && is_object($email) )
+										? ', at &lt;' . $email->jscode() . $email->encryptEmail(getConfig('contact_email')) . '&gt;'
+										: '';
+		
+		$internal_text = "<h3>The site was unable to finish serving your request.</h3>
+											<p>We apologize for the inconveience, but an error occurred in the Enano database layer. Please report the full text of this page to the administrator of this site{$email_info}.</p>
+											<p>Description or location of error: $t<br />
+											Error returned by $this->dbms_name extension: $e</p>
+											<p>Most recent SQL query:</p>
+											<pre>$bt</pre>";
+		return $internal_text;
+	}
+	
+	/**
+ 	* Exit Enano and output a JSON format datbase error.
+ 	* @param string Description or location of error; defaults to none
+ 	*/
+	
+	function die_json($loc = false)
+	{
+		$e = str_replace("\n", "\\n", addslashes(htmlspecialchars($this->sql_error())));
+		$q = str_replace("\n", "\\n", addslashes($this->latest_query));
+		$loc = ( $loc ) ? addslashes("\n\nDescription or location of error: $loc") : "";
+		$loc .= "\n\nPlease report the full text of this error to the administrator of the site. If you believe that this is a bug with the software, please contact support@enanocms.org.";
+		$loc = str_replace("\n", "\\n", $loc);
+		$t = "{\"mode\":\"error\",\"error\":\"An error occurred during database query.\\nQuery was:\\n  $q\\n\\nError returned by {$this->dbms_name}: $e$loc\"}";
+		die($t);
+	}
+	
+	/**
+ 	* Connect to the database.
+ 	* @param bool If true, enables all other parameters. Defaults to false, which emans that you can call this function with no arguments and it will fetch information from the config file.
+ 	* @param string Database server hostname
+ 	* @param string Database server username
+ 	* @param string Database server password
+ 	* @param string Name of the database
+ 	* @param int Optional port number to connect over
+ 	*/
+	
+	function connect($manual_credentials = false, $dbhost = false, $dbuser = false, $dbpasswd = false, $dbname = false, $dbport = false)
+	{
+		if ( !defined('ENANO_SQL_CONSTANTS') )
+		{
+			define('ENANO_SQL_CONSTANTS', '');
+			define('ENANO_DBLAYER', 'PGSQL');
+			define('ENANO_SQLFUNC_LOWERCASE', 'lower');
+			define('ENANO_SQL_MULTISTRING_PRFIX', 'E');
+			define('ENANO_SQL_BOOLEAN_TRUE', '1');
+			define('ENANO_SQL_BOOLEAN_FALSE', '0');
+		}
+		
+		if ( !$manual_credentials )
+		{
+			if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
+			{
+				@include(ENANO_ROOT.'/config.new.php');
+			}
+			else
+			{
+				@include(ENANO_ROOT.'/config.php');
+			}
+				
+			if ( isset($crypto_key) )
+				unset($crypto_key); // Get this sucker out of memory fast
+			if ( empty($dbport) )
+				$dbport = 5432;
+			
+			if ( !defined('ENANO_INSTALLED') && !defined('MIDGET_INSTALLED') && !defined('IN_ENANO_INSTALL') )
+			{
+				// scriptPath isn't set yet - we need to autodetect it to avoid infinite redirects
+				if ( !defined('scriptPath') )
+				{
+					if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
+					{
+						$_SERVER['REQUEST_URI'] = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $_SERVER['REQUEST_URI']);
+					}
+					if ( !preg_match('/\.php$/', $_SERVER['REQUEST_URI']) )
+					{
+						// user requested http://foo/enano as opposed to http://foo/enano/index.php
+						$_SERVER['REQUEST_URI'] .= '/index.php';
+					}
+					$sp = dirname($_SERVER['REQUEST_URI']);
+					if($sp == '/' || $sp == '\\') $sp = '';
+					define('scriptPath', $sp);
+					define('contentPath', "$sp/index.php?title=");
+				}
+				$loc = scriptPath . '/install.php';
+				// header("Location: $loc");
+				redirect($loc, 'Enano not installed', 'We can\'t seem to find an Enano installation (valid config file). You will be transferred to the installation wizard momentarily...', 3);
+				exit;
+			}
+		}
+		
+		if ( empty($dbport) )
+			$dbport = 5432;
+		
+		$this->_conn = @pg_connect("host=$dbhost port=$dbport dbname=$dbname user=$dbuser password=$dbpasswd");
+		unset($dbuser);
+		unset($dbpasswd); // Security
+		
+		if ( !$this->_conn && !$manual_credentials )
+		{
+			grinding_halt('Enano is having a problem', '<p>Error: couldn\'t connect to PostgreSQL.<br />'.pg_last_error().'</p>');
+		}
+		else if ( !$this->_conn && $manual_credentials )
+		{
+			return false;
+		}
+		
+		// Reset some variables
+		$this->query_backtrace = array();
+		$this->query_times = array();
+		$this->query_sources = array();
+		$this->num_queries = 0;
+		
+		$this->debug = ( defined('ENANO_DEBUG') );
+		
+		// We're in!
+		return true;
+	}
+	
+	/**
+ 	* Make a SQL query.
+ 	* @param string Query
+ 	* @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
+ 	* @return resource or false on failure
+ 	*/
+	
+	function sql_query($q)
+	{
+		if ( $this->debug && function_exists('debug_backtrace') )
+		{
+			$backtrace = @debug_backtrace();
+			if ( is_array($backtrace) )
+			{
+				$bt = $backtrace[0];
+				if ( isset($backtrace[1]['class']) )
+				{
+					if ( $backtrace[1]['class'] == 'sessionManager' )
+					{
+						$bt = $backtrace[1];
+					}
+				}
+				$this->query_sources[$q] = substr($bt['file'], strlen(ENANO_ROOT) + 1) . ', line ' . $bt['line'];
+			}
+			unset($backtrace);
+		}
+		
+		$this->num_queries++;
+		$this->query_backtrace[] = $q;
+		$this->latest_query = $q;
+		// First make sure we have a connection
+		if ( !$this->_conn )
+		{
+			$this->_die('A database connection has not yet been established.');
+		}
+		// Does this query look malicious?
+		if ( !$this->check_query($q) )
+		{
+			$this->report_query($q);
+			grinding_halt('SQL Injection attempt', '<p>Enano has caught and prevented an SQL injection attempt. Your IP address has been recorded and the administrator has been notified.</p><p>Query was:</p><pre>'.htmlspecialchars($q).'</pre>');
+		}
+		
+		$time_start = microtime_float();
+		$r = @pg_query($this->_conn, $q);
+		$this->query_times[$q] = microtime_float() - $time_start;
+		$this->latest_result = $r;
+		return $r;
+	}
+	
+	/**
+ 	* Make a SQL query, but do not have PHP buffer all the results. Useful for queries that are expected to return a huge number of results.
+ 	* @param string Query
+ 	* @param bool If false, skips all checks and logging stages. If you're doing a ton of queries, set this to true; in all other cases, leave at the default of false.
+ 	* @return resource or false on failure
+ 	*/
+	
+	function sql_unbuffered_query($q)
+	{
+		return $this->sql_query($q);
+	}
+	
+	/**
+ 	* Checks a SQL query for possible signs of injection attempts
+ 	* @param string $q the query to check
+ 	* @return bool true if query passed check, otherwise false
+ 	*/
+	
+	function check_query($q, $debug = false)
+	{
+		global $db_sql_parse_time;
+		$ts = microtime_float();
+		
+		// remove properly escaped quotes
+		$q = str_replace(array("\\\"", "\\'"), '', $q);
+		
+		// make sure quotes match
+		foreach ( array("'", '"') as $quote )
+		{
+			if ( get_char_count($q, $quote) % 2 == 1 )
+			{
+				// mismatched quotes
+				return false;
+			}
+			// this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
+			$q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
+		}
+		$q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
+		
+		// quotes are now matched out. does this string have a comment marker in it?
+		if ( strstr($q, '--') )
+		{
+			return false;
+		}
+		
+		if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
+		{
+			if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
+			return false;
+		}
+		
+		$ts = microtime_float() - $ts;
+		$db_sql_parse_time += $ts;
+		return true;
+	}
+	
+	/**
+ 	* Set the internal result pointer to X
+ 	* @param int $pos The number of the row
+ 	* @param resource $result The PostgreSQL result resource - if not given, the latest cached query is assumed
+ 	* @return true on success, false on failure
+ 	*/
+ 	
+	function sql_data_seek($pos, $result = false)
+	{
+		if ( !$result )
+			$result = $this->latest_result;
+		if ( !$result )
+			return false;
+		
+		return pg_result_seek($result, $pos) ? true : false;
+	}
+	
+	/**
+ 	* Reports a bad query to the admin
+ 	* @param string $query the naughty query
+ 	* @access private
+ 	*/
+ 	
+	function report_query($query)
+	{
+		global $session;
+		if ( is_object($session) && defined('ENANO_MAINSTREAM') )
+		{
+			$username = $session->username;
+			$user_id = $session->user_id;
+		}
+		else
+		{
+			$username = 'Unavailable';
+			$user_id = 1;
+		} 
+		
+		$query = $this->escape($query);
+		$q = $this->sql_query('INSERT INTO '.table_prefix.'logs(log_type,     action,         time_id,    date_string, page_text,      author,            author_uid,       edit_summary)
+ 																										VALUES(\'security\', \'sql_inject\', '.time().', \'\',        \''.$query.'\', \''.$username.'\', ' . $user_id . ', \''.$_SERVER['REMOTE_ADDR'].'\');');
+	}
+	
+	/**
+ 	* Returns the ID of the row last inserted.
+ 	* @return int
+ 	*/
+	
+	function insert_id()
+	{
+		// list of primary keys in Enano tables
+		// this is a bit hackish, but not much choice.
+		static $primary_keys = false;
+		if ( !is_array($primary_keys) )
+		{
+			$primary_keys = array(
+				table_prefix . 'comments' => 'comment_id',
+				table_prefix . 'logs' => 'log_id',
+				table_prefix . 'users' => 'user_id',
+				table_prefix . 'banlist' => 'ban_id',
+				table_prefix . 'files' => 'file_id',
+				table_prefix . 'buddies' => 'buddy_id',
+				table_prefix . 'privmsgs' => 'message_id',
+				table_prefix . 'sidebar' => 'item_id',
+				table_prefix . 'hits' => 'hit_id',
+				table_prefix . 'groups' => 'group_id',
+				table_prefix . 'group_members' => 'member_id',
+				table_prefix . 'acl' => 'rule_id',
+				table_prefix . 'page_groups' => 'pg_id',
+				table_prefix . 'page_group_members' => 'pg_member_id',
+				table_prefix . 'tags' => 'tag_id',
+				table_prefix . 'lockout' => 'id',
+				table_prefix . 'language' => 'lang_id',
+				table_prefix . 'language_strings' => 'string_id',
+				table_prefix . 'ranks' => 'rank_id',
+				table_prefix . 'captcha' => 'code_id',
+				table_prefix . 'diffiehellman' => 'key_id',
+				table_prefix . 'plugins' => 'plugin_id'
+			);
+			// allow plugins to patch this if needed
+			global $plugins;
+			$code = $plugins->setHook('pgsql_set_serial_list');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+		}
+		$last_was_insert = preg_match('/^INSERT INTO ([a-z0-9_]+)/i', $this->latest_query, $match);
+		if ( $last_was_insert )
+		{
+			// trick based on PunBB's PostgreSQL driver
+			$table =& $match[1];
+			if ( isset($primary_keys[$table]) )
+			{
+				$primary_key = "{$table}_{$primary_keys[$table]}_seq";
+				$q = pg_query("SELECT CURRVAL('$primary_key');");
+				return ( $q ) ? intval(@pg_fetch_result($q, 0)) : false;
+			}
+		}
+		return false;
+	}
+	
+	/**
+ 	* Fetch one row from the given query as an associative array.
+ 	* @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
+ 	* @return array
+ 	*/
+	
+	function fetchrow($r = false)
+	{
+		if ( !$this->_conn )
+			return false;
+		if ( !$r )
+			$r = $this->latest_result;
+		if ( !$r )
+			$this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
+		
+		$row = pg_fetch_assoc($r);
+		return integerize_array($row);
+	}
+	
+	/**
+ 	* Fetch one row from the given query as a numeric array.
+ 	* @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
+ 	* @return array
+ 	*/
+	
+	function fetchrow_num($r = false)
+	{
+		if ( !$r )
+			$r = $this->latest_result;
+		if ( !$r )
+			$this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
+		
+		$row = pg_fetch_row($r);
+		return integerize_array($row);
+	}
+	
+	/**
+ 	* Get the number of results for a given query.
+ 	* @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
+ 	* @return array
+ 	*/
+	
+	function numrows($r = false)
+	{
+		if ( !$r )
+			$r = $this->latest_result;
+		if ( !$r )
+			$this->_die('$db->fetchrow(): an invalid ' . $this->dbms_name . ' resource was passed.');
+		
+		$n = pg_num_rows($r);
+		return $n;
+	}
+	
+	/**
+ 	* Escape a string so that it may safely be included in a SQL query.
+ 	* @param string String to escape
+ 	* @return string Escaped string
+ 	*/
+	
+	function escape($str)
+	{
+		$str = pg_escape_string($this->_conn, $str);
+		return $str;
+	}
+	
+	/**
+ 	* Free the given result from memory. Use this when completely finished with a result resource.
+ 	* @param resource The resource returned from sql_query; if this isn't provided, the last result resource is used.
+ 	* @return null
+ 	*/
+	
+	function free_result($result = false)
+	{
+		if ( !$result )
+			$result = $this->latest_result;
+		
+		if ( !$result )
+			return null;
+		
+		@pg_free_result($result);
+		return null;
+	}
+	
+	/**
+ 	* Close the database connection
+ 	*/
+	
+	function close()
+	{
+		@pg_close($this->_conn);
+		unset($this->_conn);
+	}
+	
+	/**
+ 	* Get a list of columns in the given table
+ 	* @param string Table
+ 	* @return array
+ 	*/
+	
+	function columns_in($table)
+	{
+		if ( !is_string($table) )
+			return false;
+		$q = $this->sql_query("SELECT * FROM $table LIMIT 1 OFFSET 0;");
+		if ( !$q )
+			$this->_die();
+		if ( $this->numrows() < 1 )
+		{
+			// FIXME: Have another way to do this if the table is empty
+			return false;
+		}
+		
+		$row = $this->fetchrow();
+		$this->free_result();
+		return array_keys($row);
+	}
+	
 	function sql_error()
 	{
 		if ( $this->_conn )
@@ -1172,88 +1172,88 @@
 	}
 	
 	/**
-   * Returns the number of rows affected.
-   * @return int
-   */
-  
-  function sql_affectedrows()
-  {
-    return pg_affected_rows($this->latest_result);
-  }
+ 	* Returns the number of rows affected.
+ 	* @return int
+ 	*/
+	
+	function sql_affectedrows()
+	{
+		return pg_affected_rows($this->latest_result);
+	}
 
-  /**
-   * Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
-   */
-  
-  function sql_report()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( !$session->get_permissions('mod_misc') )
-    {
-      die_friendly('Access denied', '<p>You are not authorized to generate a SQL backtrace.</p>');
-    }
-    // Create copies of variables that may be changed after header is called
-    $backtrace = $this->query_backtrace;
-    $times = $this->query_times;
-    $template->header();
-    echo '<h3>SQL query log and timetable</h3>';
-    echo '<div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">';
-    $i = 0;
-    foreach ( $backtrace as $query )
-    {
-      $i++;
-      $unbuffered = false;
-      if ( substr($query, 0, 13) == '(UNBUFFERED) ' )
-      {
-        $query = substr($query, 13);
-        $unbuffered = true;
-      }
-      if ( $i == 1 )
-      {
-        echo '<tr>
-                <th colspan="2">SQL backtrace for a normal page load of ' . htmlspecialchars($paths->cpage['urlname']) . '</th>
-              </tr>';
-      }
-      else
-      {
-        echo '<tr>
-                <th class="subhead" colspan="2">&nbsp;</th>
-              </tr>';
-      }
-      echo '<tr>
-              <td class="row2">Query:</td>
-              <td class="row1"><pre>' . htmlspecialchars($query) . '</pre></td>
-            </tr>
-            <tr>
-              <td class="row2">Time:</td>
-              <td class="row1">' . number_format($this->query_times[$query], 6) . ' seconds</td>
-            </tr>
-            <tr>
-              <td class="row2">Unbuffered:</td>
-              <td class="row1">' . ( $unbuffered ? 'Yes' : 'No' ) . '</td>
-            </tr>';
-      if ( isset($this->query_sources[$query]) )
-      {
-        echo '<tr>
-                <td class="row2">Called from:</td>
-                <td class="row1">' . $this->query_sources[$query] . '</td>
-              </tr>';
-      }
-    }
-    if ( function_exists('array_sum') )
-    {
-      $query_time_total = array_sum($this->query_times);
-      echo '<tr>
-              <th class="subhead" colspan="2">
-                Total time taken for SQL queries: ' . round( $query_time_total, 6 ) . ' seconds
-              </th>
-            </tr>';
-    }
-    echo '  </table>
-          </div>';
-    $template->footer();
-  }
+	/**
+ 	* Generates and outputs a report of all the SQL queries made during execution. Should only be called after everything's over with.
+ 	*/
+	
+	function sql_report()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( !$session->get_permissions('mod_misc') )
+		{
+			die_friendly('Access denied', '<p>You are not authorized to generate a SQL backtrace.</p>');
+		}
+		// Create copies of variables that may be changed after header is called
+		$backtrace = $this->query_backtrace;
+		$times = $this->query_times;
+		$template->header();
+		echo '<h3>SQL query log and timetable</h3>';
+		echo '<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">';
+		$i = 0;
+		foreach ( $backtrace as $query )
+		{
+			$i++;
+			$unbuffered = false;
+			if ( substr($query, 0, 13) == '(UNBUFFERED) ' )
+			{
+				$query = substr($query, 13);
+				$unbuffered = true;
+			}
+			if ( $i == 1 )
+			{
+				echo '<tr>
+								<th colspan="2">SQL backtrace for a normal page load of ' . htmlspecialchars($paths->cpage['urlname']) . '</th>
+							</tr>';
+			}
+			else
+			{
+				echo '<tr>
+								<th class="subhead" colspan="2">&nbsp;</th>
+							</tr>';
+			}
+			echo '<tr>
+							<td class="row2">Query:</td>
+							<td class="row1"><pre>' . htmlspecialchars($query) . '</pre></td>
+						</tr>
+						<tr>
+							<td class="row2">Time:</td>
+							<td class="row1">' . number_format($this->query_times[$query], 6) . ' seconds</td>
+						</tr>
+						<tr>
+							<td class="row2">Unbuffered:</td>
+							<td class="row1">' . ( $unbuffered ? 'Yes' : 'No' ) . '</td>
+						</tr>';
+			if ( isset($this->query_sources[$query]) )
+			{
+				echo '<tr>
+								<td class="row2">Called from:</td>
+								<td class="row1">' . $this->query_sources[$query] . '</td>
+							</tr>';
+			}
+		}
+		if ( function_exists('array_sum') )
+		{
+			$query_time_total = array_sum($this->query_times);
+			echo '<tr>
+							<th class="subhead" colspan="2">
+								Total time taken for SQL queries: ' . round( $query_time_total, 6 ) . ' seconds
+							</th>
+						</tr>';
+		}
+		echo '  </table>
+					</div>';
+		$template->footer();
+	}
 }
 
 ?>
--- a/includes/diffengine/Engine/native.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/diffengine/Engine/native.php	Sun Mar 28 23:10:46 2010 -0400
@@ -27,409 +27,409 @@
  */
 class Text_Diff_Engine_native {
 
-    function diff($from_lines, $to_lines)
-    {
-        array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
-        array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
+		function diff($from_lines, $to_lines)
+		{
+				array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
+				array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
 
-        $n_from = count($from_lines);
-        $n_to = count($to_lines);
+				$n_from = count($from_lines);
+				$n_to = count($to_lines);
 
-        $this->xchanged = $this->ychanged = array();
-        $this->xv = $this->yv = array();
-        $this->xind = $this->yind = array();
-        unset($this->seq);
-        unset($this->in_seq);
-        unset($this->lcs);
+				$this->xchanged = $this->ychanged = array();
+				$this->xv = $this->yv = array();
+				$this->xind = $this->yind = array();
+				unset($this->seq);
+				unset($this->in_seq);
+				unset($this->lcs);
 
-        // Skip leading common lines.
-        for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
-            if ($from_lines[$skip] !== $to_lines[$skip]) {
-                break;
-            }
-            $this->xchanged[$skip] = $this->ychanged[$skip] = false;
-        }
+				// Skip leading common lines.
+				for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) {
+						if ($from_lines[$skip] !== $to_lines[$skip]) {
+								break;
+						}
+						$this->xchanged[$skip] = $this->ychanged[$skip] = false;
+				}
 
-        // Skip trailing common lines.
-        $xi = $n_from; $yi = $n_to;
-        for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
-            if ($from_lines[$xi] !== $to_lines[$yi]) {
-                break;
-            }
-            $this->xchanged[$xi] = $this->ychanged[$yi] = false;
-        }
+				// Skip trailing common lines.
+				$xi = $n_from; $yi = $n_to;
+				for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) {
+						if ($from_lines[$xi] !== $to_lines[$yi]) {
+								break;
+						}
+						$this->xchanged[$xi] = $this->ychanged[$yi] = false;
+				}
 
-        // Ignore lines which do not exist in both files.
-        for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
-            $xhash[$from_lines[$xi]] = 1;
-        }
-        for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
-            $line = $to_lines[$yi];
-            if (($this->ychanged[$yi] = empty($xhash[$line]))) {
-                continue;
-            }
-            $yhash[$line] = 1;
-            $this->yv[] = $line;
-            $this->yind[] = $yi;
-        }
-        for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
-            $line = $from_lines[$xi];
-            if (($this->xchanged[$xi] = empty($yhash[$line]))) {
-                continue;
-            }
-            $this->xv[] = $line;
-            $this->xind[] = $xi;
-        }
+				// Ignore lines which do not exist in both files.
+				for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
+						$xhash[$from_lines[$xi]] = 1;
+				}
+				for ($yi = $skip; $yi < $n_to - $endskip; $yi++) {
+						$line = $to_lines[$yi];
+						if (($this->ychanged[$yi] = empty($xhash[$line]))) {
+								continue;
+						}
+						$yhash[$line] = 1;
+						$this->yv[] = $line;
+						$this->yind[] = $yi;
+				}
+				for ($xi = $skip; $xi < $n_from - $endskip; $xi++) {
+						$line = $from_lines[$xi];
+						if (($this->xchanged[$xi] = empty($yhash[$line]))) {
+								continue;
+						}
+						$this->xv[] = $line;
+						$this->xind[] = $xi;
+				}
 
-        // Find the LCS.
-        $this->_compareseq(0, count($this->xv), 0, count($this->yv));
+				// Find the LCS.
+				$this->_compareseq(0, count($this->xv), 0, count($this->yv));
 
-        // Merge edits when possible.
-        $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged);
-        $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged);
+				// Merge edits when possible.
+				$this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged);
+				$this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged);
 
-        // Compute the edit operations.
-        $edits = array();
-        $xi = $yi = 0;
-        while ($xi < $n_from || $yi < $n_to) {
-            assert($yi < $n_to || $this->xchanged[$xi]);
-            assert($xi < $n_from || $this->ychanged[$yi]);
+				// Compute the edit operations.
+				$edits = array();
+				$xi = $yi = 0;
+				while ($xi < $n_from || $yi < $n_to) {
+						assert($yi < $n_to || $this->xchanged[$xi]);
+						assert($xi < $n_from || $this->ychanged[$yi]);
 
-            // Skip matching "snake".
-            $copy = array();
-            while ($xi < $n_from && $yi < $n_to
-                   && !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
-                $copy[] = $from_lines[$xi++];
-                ++$yi;
-            }
-            if ($copy) {
-                $edits[] = &new Text_Diff_Op_copy($copy);
-            }
+						// Skip matching "snake".
+						$copy = array();
+						while ($xi < $n_from && $yi < $n_to
+ 									&& !$this->xchanged[$xi] && !$this->ychanged[$yi]) {
+								$copy[] = $from_lines[$xi++];
+								++$yi;
+						}
+						if ($copy) {
+								$edits[] = &new Text_Diff_Op_copy($copy);
+						}
 
-            // Find deletes & adds.
-            $delete = array();
-            while ($xi < $n_from && $this->xchanged[$xi]) {
-                $delete[] = $from_lines[$xi++];
-            }
+						// Find deletes & adds.
+						$delete = array();
+						while ($xi < $n_from && $this->xchanged[$xi]) {
+								$delete[] = $from_lines[$xi++];
+						}
 
-            $add = array();
-            while ($yi < $n_to && $this->ychanged[$yi]) {
-                $add[] = $to_lines[$yi++];
-            }
+						$add = array();
+						while ($yi < $n_to && $this->ychanged[$yi]) {
+								$add[] = $to_lines[$yi++];
+						}
 
-            if ($delete && $add) {
-                $edits[] = &new Text_Diff_Op_change($delete, $add);
-            } elseif ($delete) {
-                $edits[] = &new Text_Diff_Op_delete($delete);
-            } elseif ($add) {
-                $edits[] = &new Text_Diff_Op_add($add);
-            }
-        }
+						if ($delete && $add) {
+								$edits[] = &new Text_Diff_Op_change($delete, $add);
+						} elseif ($delete) {
+								$edits[] = &new Text_Diff_Op_delete($delete);
+						} elseif ($add) {
+								$edits[] = &new Text_Diff_Op_add($add);
+						}
+				}
 
-        return $edits;
-    }
+				return $edits;
+		}
 
-    /**
-     * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF,
-     * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized
-     * segments.
-     *
-     * Returns (LCS, PTS).  LCS is the length of the LCS. PTS is an array of
-     * NCHUNKS+1 (X, Y) indexes giving the diving points between sub
-     * sequences.  The first sub-sequence is contained in (X0, X1), (Y0, Y1),
-     * the second in (X1, X2), (Y1, Y2) and so on.  Note that (X0, Y0) ==
-     * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
-     *
-     * This function assumes that the first lines of the specified portions of
-     * the two files do not match, and likewise that the last lines do not
-     * match.  The caller must trim matching lines from the beginning and end
-     * of the portions it is going to specify.
-     */
-    function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks)
-    {
-        $flip = false;
+		/**
+ 		* Divides the Largest Common Subsequence (LCS) of the sequences (XOFF,
+ 		* XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized
+ 		* segments.
+ 		*
+ 		* Returns (LCS, PTS).  LCS is the length of the LCS. PTS is an array of
+ 		* NCHUNKS+1 (X, Y) indexes giving the diving points between sub
+ 		* sequences.  The first sub-sequence is contained in (X0, X1), (Y0, Y1),
+ 		* the second in (X1, X2), (Y1, Y2) and so on.  Note that (X0, Y0) ==
+ 		* (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM).
+ 		*
+ 		* This function assumes that the first lines of the specified portions of
+ 		* the two files do not match, and likewise that the last lines do not
+ 		* match.  The caller must trim matching lines from the beginning and end
+ 		* of the portions it is going to specify.
+ 		*/
+		function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks)
+		{
+				$flip = false;
 
-        if ($xlim - $xoff > $ylim - $yoff) {
-            /* Things seems faster (I'm not sure I understand why) when the
-             * shortest sequence is in X. */
-            $flip = true;
-            list ($xoff, $xlim, $yoff, $ylim)
-                = array($yoff, $ylim, $xoff, $xlim);
-        }
+				if ($xlim - $xoff > $ylim - $yoff) {
+						/* Things seems faster (I'm not sure I understand why) when the
+ 						* shortest sequence is in X. */
+						$flip = true;
+						list ($xoff, $xlim, $yoff, $ylim)
+								= array($yoff, $ylim, $xoff, $xlim);
+				}
 
-        if ($flip) {
-            for ($i = $ylim - 1; $i >= $yoff; $i--) {
-                $ymatches[$this->xv[$i]][] = $i;
-            }
-        } else {
-            for ($i = $ylim - 1; $i >= $yoff; $i--) {
-                $ymatches[$this->yv[$i]][] = $i;
-            }
-        }
+				if ($flip) {
+						for ($i = $ylim - 1; $i >= $yoff; $i--) {
+								$ymatches[$this->xv[$i]][] = $i;
+						}
+				} else {
+						for ($i = $ylim - 1; $i >= $yoff; $i--) {
+								$ymatches[$this->yv[$i]][] = $i;
+						}
+				}
 
-        $this->lcs = 0;
-        $this->seq[0]= $yoff - 1;
-        $this->in_seq = array();
-        $ymids[0] = array();
+				$this->lcs = 0;
+				$this->seq[0]= $yoff - 1;
+				$this->in_seq = array();
+				$ymids[0] = array();
 
-        $numer = $xlim - $xoff + $nchunks - 1;
-        $x = $xoff;
-        for ($chunk = 0; $chunk < $nchunks; $chunk++) {
-            if ($chunk > 0) {
-                for ($i = 0; $i <= $this->lcs; $i++) {
-                    $ymids[$i][$chunk - 1] = $this->seq[$i];
-                }
-            }
+				$numer = $xlim - $xoff + $nchunks - 1;
+				$x = $xoff;
+				for ($chunk = 0; $chunk < $nchunks; $chunk++) {
+						if ($chunk > 0) {
+								for ($i = 0; $i <= $this->lcs; $i++) {
+										$ymids[$i][$chunk - 1] = $this->seq[$i];
+								}
+						}
 
-            $x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
-            for (; $x < $x1; $x++) {
-                $line = $flip ? $this->yv[$x] : $this->xv[$x];
-                if (empty($ymatches[$line])) {
-                    continue;
-                }
-                $matches = $ymatches[$line];
-                foreach ($matches as $y) {
-                    if (empty($this->in_seq[$y])) {
-                        $k = $this->_lcsPos($y);
-                        assert($k > 0);
-                        $ymids[$k] = $ymids[$k - 1];
-                        break;
-                    }
-                }
+						$x1 = $xoff + (int)(($numer + ($xlim-$xoff)*$chunk) / $nchunks);
+						for (; $x < $x1; $x++) {
+								$line = $flip ? $this->yv[$x] : $this->xv[$x];
+								if (empty($ymatches[$line])) {
+										continue;
+								}
+								$matches = $ymatches[$line];
+								foreach ($matches as $y) {
+										if (empty($this->in_seq[$y])) {
+												$k = $this->_lcsPos($y);
+												assert($k > 0);
+												$ymids[$k] = $ymids[$k - 1];
+												break;
+										}
+								}
 
-                while (list($junk, $y) = each($matches)) {
-                    if ($y > $this->seq[$k - 1]) {
-                        assert($y < $this->seq[$k]);
-                        /* Optimization: this is a common case: next match is
-                         * just replacing previous match. */
-                        $this->in_seq[$this->seq[$k]] = false;
-                        $this->seq[$k] = $y;
-                        $this->in_seq[$y] = 1;
-                    } elseif (empty($this->in_seq[$y])) {
-                        $k = $this->_lcsPos($y);
-                        assert($k > 0);
-                        $ymids[$k] = $ymids[$k - 1];
-                    }
-                }
-            }
-        }
+								while (list($junk, $y) = each($matches)) {
+										if ($y > $this->seq[$k - 1]) {
+												assert($y < $this->seq[$k]);
+												/* Optimization: this is a common case: next match is
+ 												* just replacing previous match. */
+												$this->in_seq[$this->seq[$k]] = false;
+												$this->seq[$k] = $y;
+												$this->in_seq[$y] = 1;
+										} elseif (empty($this->in_seq[$y])) {
+												$k = $this->_lcsPos($y);
+												assert($k > 0);
+												$ymids[$k] = $ymids[$k - 1];
+										}
+								}
+						}
+				}
 
-        $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
-        $ymid = $ymids[$this->lcs];
-        for ($n = 0; $n < $nchunks - 1; $n++) {
-            $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
-            $y1 = $ymid[$n] + 1;
-            $seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
-        }
-        $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
+				$seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff);
+				$ymid = $ymids[$this->lcs];
+				for ($n = 0; $n < $nchunks - 1; $n++) {
+						$x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks);
+						$y1 = $ymid[$n] + 1;
+						$seps[] = $flip ? array($y1, $x1) : array($x1, $y1);
+				}
+				$seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim);
 
-        return array($this->lcs, $seps);
-    }
+				return array($this->lcs, $seps);
+		}
 
-    function _lcsPos($ypos)
-    {
-        $end = $this->lcs;
-        if ($end == 0 || $ypos > $this->seq[$end]) {
-            $this->seq[++$this->lcs] = $ypos;
-            $this->in_seq[$ypos] = 1;
-            return $this->lcs;
-        }
+		function _lcsPos($ypos)
+		{
+				$end = $this->lcs;
+				if ($end == 0 || $ypos > $this->seq[$end]) {
+						$this->seq[++$this->lcs] = $ypos;
+						$this->in_seq[$ypos] = 1;
+						return $this->lcs;
+				}
 
-        $beg = 1;
-        while ($beg < $end) {
-            $mid = (int)(($beg + $end) / 2);
-            if ($ypos > $this->seq[$mid]) {
-                $beg = $mid + 1;
-            } else {
-                $end = $mid;
-            }
-        }
+				$beg = 1;
+				while ($beg < $end) {
+						$mid = (int)(($beg + $end) / 2);
+						if ($ypos > $this->seq[$mid]) {
+								$beg = $mid + 1;
+						} else {
+								$end = $mid;
+						}
+				}
 
-        assert($ypos != $this->seq[$end]);
+				assert($ypos != $this->seq[$end]);
 
-        $this->in_seq[$this->seq[$end]] = false;
-        $this->seq[$end] = $ypos;
-        $this->in_seq[$ypos] = 1;
-        return $end;
-    }
+				$this->in_seq[$this->seq[$end]] = false;
+				$this->seq[$end] = $ypos;
+				$this->in_seq[$ypos] = 1;
+				return $end;
+		}
 
-    /**
-     * Finds LCS of two sequences.
-     *
-     * The results are recorded in the vectors $this->{x,y}changed[], by
-     * storing a 1 in the element for each line that is an insertion or
-     * deletion (ie. is not in the LCS).
-     *
-     * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1.
-     *
-     * Note that XLIM, YLIM are exclusive bounds.  All line numbers are
-     * origin-0 and discarded lines are not counted.
-     */
-    function _compareseq ($xoff, $xlim, $yoff, $ylim)
-    {
-        /* Slide down the bottom initial diagonal. */
-        while ($xoff < $xlim && $yoff < $ylim
-               && $this->xv[$xoff] == $this->yv[$yoff]) {
-            ++$xoff;
-            ++$yoff;
-        }
+		/**
+ 		* Finds LCS of two sequences.
+ 		*
+ 		* The results are recorded in the vectors $this->{x,y}changed[], by
+ 		* storing a 1 in the element for each line that is an insertion or
+ 		* deletion (ie. is not in the LCS).
+ 		*
+ 		* The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1.
+ 		*
+ 		* Note that XLIM, YLIM are exclusive bounds.  All line numbers are
+ 		* origin-0 and discarded lines are not counted.
+ 		*/
+		function _compareseq ($xoff, $xlim, $yoff, $ylim)
+		{
+				/* Slide down the bottom initial diagonal. */
+				while ($xoff < $xlim && $yoff < $ylim
+ 							&& $this->xv[$xoff] == $this->yv[$yoff]) {
+						++$xoff;
+						++$yoff;
+				}
 
-        /* Slide up the top initial diagonal. */
-        while ($xlim > $xoff && $ylim > $yoff
-               && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
-            --$xlim;
-            --$ylim;
-        }
+				/* Slide up the top initial diagonal. */
+				while ($xlim > $xoff && $ylim > $yoff
+ 							&& $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) {
+						--$xlim;
+						--$ylim;
+				}
 
-        if ($xoff == $xlim || $yoff == $ylim) {
-            $lcs = 0;
-        } else {
-            /* This is ad hoc but seems to work well.  $nchunks =
-             * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks =
-             * max(2,min(8,(int)$nchunks)); */
-            $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
-            list($lcs, $seps)
-                = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
-        }
+				if ($xoff == $xlim || $yoff == $ylim) {
+						$lcs = 0;
+				} else {
+						/* This is ad hoc but seems to work well.  $nchunks =
+ 						* sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks =
+ 						* max(2,min(8,(int)$nchunks)); */
+						$nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1;
+						list($lcs, $seps)
+								= $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks);
+				}
 
-        if ($lcs == 0) {
-            /* X and Y sequences have no common subsequence: mark all
-             * changed. */
-            while ($yoff < $ylim) {
-                $this->ychanged[$this->yind[$yoff++]] = 1;
-            }
-            while ($xoff < $xlim) {
-                $this->xchanged[$this->xind[$xoff++]] = 1;
-            }
-        } else {
-            /* Use the partitions to split this problem into subproblems. */
-            reset($seps);
-            $pt1 = $seps[0];
-            while ($pt2 = next($seps)) {
-                $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
-                $pt1 = $pt2;
-            }
-        }
-    }
+				if ($lcs == 0) {
+						/* X and Y sequences have no common subsequence: mark all
+ 						* changed. */
+						while ($yoff < $ylim) {
+								$this->ychanged[$this->yind[$yoff++]] = 1;
+						}
+						while ($xoff < $xlim) {
+								$this->xchanged[$this->xind[$xoff++]] = 1;
+						}
+				} else {
+						/* Use the partitions to split this problem into subproblems. */
+						reset($seps);
+						$pt1 = $seps[0];
+						while ($pt2 = next($seps)) {
+								$this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]);
+								$pt1 = $pt2;
+						}
+				}
+		}
 
-    /**
-     * Adjusts inserts/deletes of identical lines to join changes as much as
-     * possible.
-     *
-     * We do something when a run of changed lines include a line at one end
-     * and has an excluded, identical line at the other.  We are free to
-     * choose which identical line is included.  `compareseq' usually chooses
-     * the one at the beginning, but usually it is cleaner to consider the
-     * following identical line to be the "change".
-     *
-     * This is extracted verbatim from analyze.c (GNU diffutils-2.7).
-     */
-    function _shiftBoundaries($lines, &$changed, $other_changed)
-    {
-        $i = 0;
-        $j = 0;
+		/**
+ 		* Adjusts inserts/deletes of identical lines to join changes as much as
+ 		* possible.
+ 		*
+ 		* We do something when a run of changed lines include a line at one end
+ 		* and has an excluded, identical line at the other.  We are free to
+ 		* choose which identical line is included.  `compareseq' usually chooses
+ 		* the one at the beginning, but usually it is cleaner to consider the
+ 		* following identical line to be the "change".
+ 		*
+ 		* This is extracted verbatim from analyze.c (GNU diffutils-2.7).
+ 		*/
+		function _shiftBoundaries($lines, &$changed, $other_changed)
+		{
+				$i = 0;
+				$j = 0;
 
-        assert('count($lines) == count($changed)');
-        $len = count($lines);
-        $other_len = count($other_changed);
+				assert('count($lines) == count($changed)');
+				$len = count($lines);
+				$other_len = count($other_changed);
 
-        while (1) {
-            /* Scan forward to find the beginning of another run of
-             * changes. Also keep track of the corresponding point in the
-             * other file.
-             *
-             * Throughout this code, $i and $j are adjusted together so that
-             * the first $i elements of $changed and the first $j elements of
-             * $other_changed both contain the same number of zeros (unchanged
-             * lines).
-             *
-             * Furthermore, $j is always kept so that $j == $other_len or
-             * $other_changed[$j] == false. */
-            while ($j < $other_len && $other_changed[$j]) {
-                $j++;
-            }
+				while (1) {
+						/* Scan forward to find the beginning of another run of
+ 						* changes. Also keep track of the corresponding point in the
+ 						* other file.
+ 						*
+ 						* Throughout this code, $i and $j are adjusted together so that
+ 						* the first $i elements of $changed and the first $j elements of
+ 						* $other_changed both contain the same number of zeros (unchanged
+ 						* lines).
+ 						*
+ 						* Furthermore, $j is always kept so that $j == $other_len or
+ 						* $other_changed[$j] == false. */
+						while ($j < $other_len && $other_changed[$j]) {
+								$j++;
+						}
 
-            while ($i < $len && ! $changed[$i]) {
-                assert('$j < $other_len && ! $other_changed[$j]');
-                $i++; $j++;
-                while ($j < $other_len && $other_changed[$j]) {
-                    $j++;
-                }
-            }
+						while ($i < $len && ! $changed[$i]) {
+								assert('$j < $other_len && ! $other_changed[$j]');
+								$i++; $j++;
+								while ($j < $other_len && $other_changed[$j]) {
+										$j++;
+								}
+						}
 
-            if ($i == $len) {
-                break;
-            }
+						if ($i == $len) {
+								break;
+						}
 
-            $start = $i;
+						$start = $i;
 
-            /* Find the end of this run of changes. */
-            while (++$i < $len && $changed[$i]) {
-                continue;
-            }
+						/* Find the end of this run of changes. */
+						while (++$i < $len && $changed[$i]) {
+								continue;
+						}
 
-            do {
-                /* Record the length of this run of changes, so that we can
-                 * later determine whether the run has grown. */
-                $runlength = $i - $start;
+						do {
+								/* Record the length of this run of changes, so that we can
+ 								* later determine whether the run has grown. */
+								$runlength = $i - $start;
 
-                /* Move the changed region back, so long as the previous
-                 * unchanged line matches the last changed one.  This merges
-                 * with previous changed regions. */
-                while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
-                    $changed[--$start] = 1;
-                    $changed[--$i] = false;
-                    while ($start > 0 && $changed[$start - 1]) {
-                        $start--;
-                    }
-                    assert('$j > 0');
-                    while ($other_changed[--$j]) {
-                        continue;
-                    }
-                    assert('$j >= 0 && !$other_changed[$j]');
-                }
+								/* Move the changed region back, so long as the previous
+ 								* unchanged line matches the last changed one.  This merges
+ 								* with previous changed regions. */
+								while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) {
+										$changed[--$start] = 1;
+										$changed[--$i] = false;
+										while ($start > 0 && $changed[$start - 1]) {
+												$start--;
+										}
+										assert('$j > 0');
+										while ($other_changed[--$j]) {
+												continue;
+										}
+										assert('$j >= 0 && !$other_changed[$j]');
+								}
 
-                /* Set CORRESPONDING to the end of the changed run, at the
-                 * last point where it corresponds to a changed run in the
-                 * other file. CORRESPONDING == LEN means no such point has
-                 * been found. */
-                $corresponding = $j < $other_len ? $i : $len;
+								/* Set CORRESPONDING to the end of the changed run, at the
+ 								* last point where it corresponds to a changed run in the
+ 								* other file. CORRESPONDING == LEN means no such point has
+ 								* been found. */
+								$corresponding = $j < $other_len ? $i : $len;
 
-                /* Move the changed region forward, so long as the first
-                 * changed line matches the following unchanged one.  This
-                 * merges with following changed regions.  Do this second, so
-                 * that if there are no merges, the changed region is moved
-                 * forward as far as possible. */
-                while ($i < $len && $lines[$start] == $lines[$i]) {
-                    $changed[$start++] = false;
-                    $changed[$i++] = 1;
-                    while ($i < $len && $changed[$i]) {
-                        $i++;
-                    }
+								/* Move the changed region forward, so long as the first
+ 								* changed line matches the following unchanged one.  This
+ 								* merges with following changed regions.  Do this second, so
+ 								* that if there are no merges, the changed region is moved
+ 								* forward as far as possible. */
+								while ($i < $len && $lines[$start] == $lines[$i]) {
+										$changed[$start++] = false;
+										$changed[$i++] = 1;
+										while ($i < $len && $changed[$i]) {
+												$i++;
+										}
 
-                    assert('$j < $other_len && ! $other_changed[$j]');
-                    $j++;
-                    if ($j < $other_len && $other_changed[$j]) {
-                        $corresponding = $i;
-                        while ($j < $other_len && $other_changed[$j]) {
-                            $j++;
-                        }
-                    }
-                }
-            } while ($runlength != $i - $start);
+										assert('$j < $other_len && ! $other_changed[$j]');
+										$j++;
+										if ($j < $other_len && $other_changed[$j]) {
+												$corresponding = $i;
+												while ($j < $other_len && $other_changed[$j]) {
+														$j++;
+												}
+										}
+								}
+						} while ($runlength != $i - $start);
 
-            /* If possible, move the fully-merged run of changes back to a
-             * corresponding run in the other file. */
-            while ($corresponding < $i) {
-                $changed[--$start] = 1;
-                $changed[--$i] = 0;
-                assert('$j > 0');
-                while ($other_changed[--$j]) {
-                    continue;
-                }
-                assert('$j >= 0 && !$other_changed[$j]');
-            }
-        }
-    }
+						/* If possible, move the fully-merged run of changes back to a
+ 						* corresponding run in the other file. */
+						while ($corresponding < $i) {
+								$changed[--$start] = 1;
+								$changed[--$i] = 0;
+								assert('$j > 0');
+								while ($other_changed[--$j]) {
+										continue;
+								}
+								assert('$j >= 0 && !$other_changed[$j]');
+						}
+				}
+		}
 
 }
--- a/includes/diffengine/Engine/string.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/diffengine/Engine/string.php	Sun Mar 28 23:10:46 2010 -0400
@@ -18,199 +18,199 @@
  */
 class Text_Diff_Engine_string {
 
-    /**
-     * Parses a unified or context diff.
-     *
-     * First param contains the whole diff and the second can be used to force 
-     * a specific diff type. If the second parameter is 'autodetect', the 
-     * diff will be examined to find out which type of diff this is.
-     * 
-     * @param string $diff  The diff content.
-     * @param string $mode  The diff mode of the content in $diff. One of
-     *                      'context', 'unified', or 'autodetect'.
-     *
-     * @return array  List of all diff operations.
-     */
-    function diff($diff, $mode = 'autodetect')
-    {
-        if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
-            die_friendly('Text_Diff', '<p>Type of diff is unsupported</p>');
-        }
+		/**
+ 		* Parses a unified or context diff.
+ 		*
+ 		* First param contains the whole diff and the second can be used to force 
+ 		* a specific diff type. If the second parameter is 'autodetect', the 
+ 		* diff will be examined to find out which type of diff this is.
+ 		* 
+ 		* @param string $diff  The diff content.
+ 		* @param string $mode  The diff mode of the content in $diff. One of
+ 		*                      'context', 'unified', or 'autodetect'.
+ 		*
+ 		* @return array  List of all diff operations.
+ 		*/
+		function diff($diff, $mode = 'autodetect')
+		{
+				if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') {
+						die_friendly('Text_Diff', '<p>Type of diff is unsupported</p>');
+				}
 
-        if ($mode == 'autodetect') {
-            $context = strpos($diff, '***');
-            $unified = strpos($diff, '---');
-            if ($context === $unified) {
-                die_friendly('Text_Diff', '<p>Type of diff could not be detected</p>');
-            } elseif ($context === false || $context === false) {
-                $mode = $context !== false ? 'context' : 'unified';
-            } else {
-                $mode = $context < $unified ? 'context' : 'unified';
-            }
-        }
+				if ($mode == 'autodetect') {
+						$context = strpos($diff, '***');
+						$unified = strpos($diff, '---');
+						if ($context === $unified) {
+								die_friendly('Text_Diff', '<p>Type of diff could not be detected</p>');
+						} elseif ($context === false || $context === false) {
+								$mode = $context !== false ? 'context' : 'unified';
+						} else {
+								$mode = $context < $unified ? 'context' : 'unified';
+						}
+				}
 
-        // split by new line and remove the diff header
-        $diff = explode("\n", $diff);
-        array_shift($diff);
-        array_shift($diff);
+				// split by new line and remove the diff header
+				$diff = explode("\n", $diff);
+				array_shift($diff);
+				array_shift($diff);
 
-        if ($mode == 'context') {
-            return $this->parseContextDiff($diff);
-        } else {
-            return $this->parseUnifiedDiff($diff);
-        }
-    }
+				if ($mode == 'context') {
+						return $this->parseContextDiff($diff);
+				} else {
+						return $this->parseUnifiedDiff($diff);
+				}
+		}
 
-    /**
-     * Parses an array containing the unified diff.
-     *
-     * @param array $diff  Array of lines.
-     *
-     * @return array  List of all diff operations.
-     */
-    function parseUnifiedDiff($diff)
-    {
-        $edits = array();
-        $end = count($diff) - 1;
-        for ($i = 0; $i < $end;) {
-            $diff1 = array();
-            switch (substr($diff[$i], 0, 1)) {
-            case ' ':
-                do {
-                    $diff1[] = substr($diff[$i], 1);
-                } while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
-                $edits[] = &new Text_Diff_Op_copy($diff1);
-                break;
-            case '+':
-                // get all new lines
-                do {
-                    $diff1[] = substr($diff[$i], 1);
-                } while (++$i < $end && substr($diff[$i], 0, 1) == '+');
-                $edits[] = &new Text_Diff_Op_add($diff1);
-                break;
-            case '-':
-                // get changed or removed lines
-                $diff2 = array();
-                do {
-                    $diff1[] = substr($diff[$i], 1);
-                } while (++$i < $end && substr($diff[$i], 0, 1) == '-');
-                while ($i < $end && substr($diff[$i], 0, 1) == '+') {
-                    $diff2[] = substr($diff[$i++], 1);
-                }
-                if (count($diff2) == 0) {
-                    $edits[] = &new Text_Diff_Op_delete($diff1);
-                } else {
-                    $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
-                }
-                break;
-            default:
-                $i++;
-                break;
-            }
-        }
-        return $edits;
-    }
+		/**
+ 		* Parses an array containing the unified diff.
+ 		*
+ 		* @param array $diff  Array of lines.
+ 		*
+ 		* @return array  List of all diff operations.
+ 		*/
+		function parseUnifiedDiff($diff)
+		{
+				$edits = array();
+				$end = count($diff) - 1;
+				for ($i = 0; $i < $end;) {
+						$diff1 = array();
+						switch (substr($diff[$i], 0, 1)) {
+						case ' ':
+								do {
+										$diff1[] = substr($diff[$i], 1);
+								} while (++$i < $end && substr($diff[$i], 0, 1) == ' ');
+								$edits[] = &new Text_Diff_Op_copy($diff1);
+								break;
+						case '+':
+								// get all new lines
+								do {
+										$diff1[] = substr($diff[$i], 1);
+								} while (++$i < $end && substr($diff[$i], 0, 1) == '+');
+								$edits[] = &new Text_Diff_Op_add($diff1);
+								break;
+						case '-':
+								// get changed or removed lines
+								$diff2 = array();
+								do {
+										$diff1[] = substr($diff[$i], 1);
+								} while (++$i < $end && substr($diff[$i], 0, 1) == '-');
+								while ($i < $end && substr($diff[$i], 0, 1) == '+') {
+										$diff2[] = substr($diff[$i++], 1);
+								}
+								if (count($diff2) == 0) {
+										$edits[] = &new Text_Diff_Op_delete($diff1);
+								} else {
+										$edits[] = &new Text_Diff_Op_change($diff1, $diff2);
+								}
+								break;
+						default:
+								$i++;
+								break;
+						}
+				}
+				return $edits;
+		}
 
-    /**
-     * Parses an array containing the context diff.
-     *
-     * @param array $diff  Array of lines.
-     *
-     * @return array  List of all diff operations.
-     */
-    function parseContextDiff(&$diff)
-    {
-        $edits = array();
-        $i = $max_i = $j = $max_j = 0;
-        $end = count($diff) - 1;
-        while ($i < $end && $j < $end) {
-            while ($i >= $max_i && $j >= $max_j) {
-                // find the boundaries of the diff output of the two files
-                for ($i = $j;
-                     $i < $end && substr($diff[$i], 0, 3) == '***';
-                     $i++);
-                for ($max_i = $i;
-                     $max_i < $end && substr($diff[$max_i], 0, 3) != '---';
-                     $max_i++);
-                for ($j = $max_i;
-                     $j < $end && substr($diff[$j], 0, 3) == '---';
-                     $j++);
-                for ($max_j = $j;
-                     $max_j < $end && substr($diff[$max_j], 0, 3) != '***';
-                     $max_j++);
-            }
+		/**
+ 		* Parses an array containing the context diff.
+ 		*
+ 		* @param array $diff  Array of lines.
+ 		*
+ 		* @return array  List of all diff operations.
+ 		*/
+		function parseContextDiff(&$diff)
+		{
+				$edits = array();
+				$i = $max_i = $j = $max_j = 0;
+				$end = count($diff) - 1;
+				while ($i < $end && $j < $end) {
+						while ($i >= $max_i && $j >= $max_j) {
+								// find the boundaries of the diff output of the two files
+								for ($i = $j;
+ 										$i < $end && substr($diff[$i], 0, 3) == '***';
+ 										$i++);
+								for ($max_i = $i;
+ 										$max_i < $end && substr($diff[$max_i], 0, 3) != '---';
+ 										$max_i++);
+								for ($j = $max_i;
+ 										$j < $end && substr($diff[$j], 0, 3) == '---';
+ 										$j++);
+								for ($max_j = $j;
+ 										$max_j < $end && substr($diff[$max_j], 0, 3) != '***';
+ 										$max_j++);
+						}
 
-            // find what hasn't been changed
-            $array = array();
-            while ($i < $max_i &&
-                   $j < $max_j &&
-                   strcmp($diff[$i], $diff[$j]) == 0) {
-                $array[] = substr($diff[$i], 2);
-                $i++;
-                $j++;
-            }
-            while ($i < $max_i && ($max_j-$j) <= 1) {
-                if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
-                    break;
-                }
-                $array[] = substr($diff[$i++], 2);
-            }
-            while ($j < $max_j && ($max_i-$i) <= 1) {
-                if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
-                    break;
-                }
-                $array[] = substr($diff[$j++], 2);
-            }
-            if (count($array) > 0) {
-                $edits[] = &new Text_Diff_Op_copy($array);
-            }
+						// find what hasn't been changed
+						$array = array();
+						while ($i < $max_i &&
+ 									$j < $max_j &&
+ 									strcmp($diff[$i], $diff[$j]) == 0) {
+								$array[] = substr($diff[$i], 2);
+								$i++;
+								$j++;
+						}
+						while ($i < $max_i && ($max_j-$j) <= 1) {
+								if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') {
+										break;
+								}
+								$array[] = substr($diff[$i++], 2);
+						}
+						while ($j < $max_j && ($max_i-$i) <= 1) {
+								if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') {
+										break;
+								}
+								$array[] = substr($diff[$j++], 2);
+						}
+						if (count($array) > 0) {
+								$edits[] = &new Text_Diff_Op_copy($array);
+						}
 
-            if ($i < $max_i) {
-                $diff1 = array();
-                switch (substr($diff[$i], 0, 1)) {
-                case '!':
-                    $diff2 = array();
-                    do {
-                        $diff1[] = substr($diff[$i], 2);
-                        if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
-                            $diff2[] = substr($diff[$j++], 2);
-                        }
-                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
-                    $edits[] = &new Text_Diff_Op_change($diff1, $diff2);
-                    break;
-                case '+':
-                    do {
-                        $diff1[] = substr($diff[$i], 2);
-                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
-                    $edits[] = &new Text_Diff_Op_add($diff1);
-                    break;
-                case '-':
-                    do {
-                        $diff1[] = substr($diff[$i], 2);
-                    } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
-                    $edits[] = &new Text_Diff_Op_delete($diff1);
-                    break;
-                }
-            }
+						if ($i < $max_i) {
+								$diff1 = array();
+								switch (substr($diff[$i], 0, 1)) {
+								case '!':
+										$diff2 = array();
+										do {
+												$diff1[] = substr($diff[$i], 2);
+												if ($j < $max_j && substr($diff[$j], 0, 1) == '!') {
+														$diff2[] = substr($diff[$j++], 2);
+												}
+										} while (++$i < $max_i && substr($diff[$i], 0, 1) == '!');
+										$edits[] = &new Text_Diff_Op_change($diff1, $diff2);
+										break;
+								case '+':
+										do {
+												$diff1[] = substr($diff[$i], 2);
+										} while (++$i < $max_i && substr($diff[$i], 0, 1) == '+');
+										$edits[] = &new Text_Diff_Op_add($diff1);
+										break;
+								case '-':
+										do {
+												$diff1[] = substr($diff[$i], 2);
+										} while (++$i < $max_i && substr($diff[$i], 0, 1) == '-');
+										$edits[] = &new Text_Diff_Op_delete($diff1);
+										break;
+								}
+						}
 
-            if ($j < $max_j) {
-                $diff2 = array();
-                switch (substr($diff[$j], 0, 1)) {
-                case '+':
-                    do {
-                        $diff2[] = substr($diff[$j++], 2);
-                    } while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
-                    $edits[] = &new Text_Diff_Op_add($diff2);
-                    break;
-                case '-':
-                    do {
-                        $diff2[] = substr($diff[$j++], 2);
-                    } while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
-                    $edits[] = &new Text_Diff_Op_delete($diff2);
-                    break;
-                }
-            }
-        }
-        return $edits;
-    }
+						if ($j < $max_j) {
+								$diff2 = array();
+								switch (substr($diff[$j], 0, 1)) {
+								case '+':
+										do {
+												$diff2[] = substr($diff[$j++], 2);
+										} while ($j < $max_j && substr($diff[$j], 0, 1) == '+');
+										$edits[] = &new Text_Diff_Op_add($diff2);
+										break;
+								case '-':
+										do {
+												$diff2[] = substr($diff[$j++], 2);
+										} while ($j < $max_j && substr($diff[$j], 0, 1) == '-');
+										$edits[] = &new Text_Diff_Op_delete($diff2);
+										break;
+								}
+						}
+				}
+				return $edits;
+		}
 }
--- a/includes/diffengine/Engine/xdiff.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/diffengine/Engine/xdiff.php	Sun Mar 28 23:10:46 2010 -0400
@@ -11,45 +11,45 @@
  */
 class Text_Diff_Engine_xdiff {
 
-    function diff($from_lines, $to_lines)
-    {
-        array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
-        array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
+		function diff($from_lines, $to_lines)
+		{
+				array_walk($from_lines, array('Text_Diff', 'trimNewlines'));
+				array_walk($to_lines, array('Text_Diff', 'trimNewlines'));
 
-        /* Convert the two input arrays into strings for xdiff processing. */
-        $from_string = implode("\n", $from_lines);
-        $to_string = implode("\n", $to_lines);
+				/* Convert the two input arrays into strings for xdiff processing. */
+				$from_string = implode("\n", $from_lines);
+				$to_string = implode("\n", $to_lines);
 
-        /* Diff the two strings and convert the result to an array. */
-        $diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
-        $diff = explode("\n", $diff);
+				/* Diff the two strings and convert the result to an array. */
+				$diff = xdiff_string_diff($from_string, $to_string, count($to_lines));
+				$diff = explode("\n", $diff);
 
-        /* Walk through the diff one line at a time.  We build the $edits
-         * array of diff operations by reading the first character of the
-         * xdiff output (which is in the "unified diff" format).
-         *
-         * Note that we don't have enough information to detect "changed"
-         * lines using this approach, so we can't add Text_Diff_Op_changed
-         * instances to the $edits array.  The result is still perfectly
-         * valid, albeit a little less descriptive and efficient. */
-        $edits = array();
-        foreach ($diff as $line) {
-            switch ($line[0]) {
-            case ' ':
-                $edits[] = &new Text_Diff_Op_copy(array(substr($line, 1)));
-                break;
+				/* Walk through the diff one line at a time.  We build the $edits
+ 				* array of diff operations by reading the first character of the
+ 				* xdiff output (which is in the "unified diff" format).
+ 				*
+ 				* Note that we don't have enough information to detect "changed"
+ 				* lines using this approach, so we can't add Text_Diff_Op_changed
+ 				* instances to the $edits array.  The result is still perfectly
+ 				* valid, albeit a little less descriptive and efficient. */
+				$edits = array();
+				foreach ($diff as $line) {
+						switch ($line[0]) {
+						case ' ':
+								$edits[] = &new Text_Diff_Op_copy(array(substr($line, 1)));
+								break;
 
-            case '+':
-                $edits[] = &new Text_Diff_Op_add(array(substr($line, 1)));
-                break;
+						case '+':
+								$edits[] = &new Text_Diff_Op_add(array(substr($line, 1)));
+								break;
 
-            case '-':
-                $edits[] = &new Text_Diff_Op_delete(array(substr($line, 1)));
-                break;
-            }
-        }
+						case '-':
+								$edits[] = &new Text_Diff_Op_delete(array(substr($line, 1)));
+								break;
+						}
+				}
 
-        return $edits;
-    }
+				return $edits;
+		}
 
 }
--- a/includes/diffengine/Renderer.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/diffengine/Renderer.php	Sun Mar 28 23:10:46 2010 -0400
@@ -11,201 +11,201 @@
  */
 class Text_Diff_Renderer {
 
-    /**
-     * Number of leading context "lines" to preserve.
-     *
-     * This should be left at zero for this class, but subclasses may want to
-     * set this to other values.
-     */
-    var $_leading_context_lines = 0;
+		/**
+ 		* Number of leading context "lines" to preserve.
+ 		*
+ 		* This should be left at zero for this class, but subclasses may want to
+ 		* set this to other values.
+ 		*/
+		var $_leading_context_lines = 0;
 
-    /**
-     * Number of trailing context "lines" to preserve.
-     *
-     * This should be left at zero for this class, but subclasses may want to
-     * set this to other values.
-     */
-    var $_trailing_context_lines = 0;
+		/**
+ 		* Number of trailing context "lines" to preserve.
+ 		*
+ 		* This should be left at zero for this class, but subclasses may want to
+ 		* set this to other values.
+ 		*/
+		var $_trailing_context_lines = 0;
 
-    /**
-     * Constructor.
-     */
-    function Text_Diff_Renderer($params = array())
-    {
-        foreach ($params as $param => $value) {
-            $v = '_' . $param;
-            if (isset($this->$v)) {
-                $this->$v = $value;
-            }
-        }
-    }
+		/**
+ 		* Constructor.
+ 		*/
+		function Text_Diff_Renderer($params = array())
+		{
+				foreach ($params as $param => $value) {
+						$v = '_' . $param;
+						if (isset($this->$v)) {
+								$this->$v = $value;
+						}
+				}
+		}
 
-    /**
-     * Get any renderer parameters.
-     *
-     * @return array  All parameters of this renderer object.
-     */
-    function getParams()
-    {
-        $params = array();
-        foreach (get_object_vars($this) as $k => $v) {
-            if ($k[0] == '_') {
-                $params[substr($k, 1)] = $v;
-            }
-        }
+		/**
+ 		* Get any renderer parameters.
+ 		*
+ 		* @return array  All parameters of this renderer object.
+ 		*/
+		function getParams()
+		{
+				$params = array();
+				foreach (get_object_vars($this) as $k => $v) {
+						if ($k[0] == '_') {
+								$params[substr($k, 1)] = $v;
+						}
+				}
 
-        return $params;
-    }
+				return $params;
+		}
 
-    /**
-     * Renders a diff.
-     *
-     * @param Text_Diff $diff  A Text_Diff object.
-     *
-     * @return string  The formatted output.
-     */
-    function render($diff)
-    {
-        $xi = $yi = 1;
-        $block = false;
-        $context = array();
+		/**
+ 		* Renders a diff.
+ 		*
+ 		* @param Text_Diff $diff  A Text_Diff object.
+ 		*
+ 		* @return string  The formatted output.
+ 		*/
+		function render($diff)
+		{
+				$xi = $yi = 1;
+				$block = false;
+				$context = array();
 
-        $nlead = $this->_leading_context_lines;
-        $ntrail = $this->_trailing_context_lines;
+				$nlead = $this->_leading_context_lines;
+				$ntrail = $this->_trailing_context_lines;
 
-        $output = $this->_startDiff();
+				$output = $this->_startDiff();
 
-        $diffs = $diff->getDiff();
-        foreach ($diffs as $i => $edit) {
-            if (is_a($edit, 'Text_Diff_Op_copy')) {
-                if (is_array($block)) {
-                    $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
-                    if (count($edit->orig) <= $keep) {
-                        $block[] = $edit;
-                    } else {
-                        if ($ntrail) {
-                            $context = array_slice($edit->orig, 0, $ntrail);
-                            $block[] = &new Text_Diff_Op_copy($context);
-                        }
-                        $output .= $this->_block($x0, $ntrail + $xi - $x0,
-                                                 $y0, $ntrail + $yi - $y0,
-                                                 $block);
-                        $block = false;
-                    }
-                }
-                $context = $edit->orig;
-            } else {
-                if (!is_array($block)) {
-                    $context = array_slice($context, count($context) - $nlead);
-                    $x0 = $xi - count($context);
-                    $y0 = $yi - count($context);
-                    $block = array();
-                    if ($context) {
-                        $block[] = &new Text_Diff_Op_copy($context);
-                    }
-                }
-                $block[] = $edit;
-            }
+				$diffs = $diff->getDiff();
+				foreach ($diffs as $i => $edit) {
+						if (is_a($edit, 'Text_Diff_Op_copy')) {
+								if (is_array($block)) {
+										$keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
+										if (count($edit->orig) <= $keep) {
+												$block[] = $edit;
+										} else {
+												if ($ntrail) {
+														$context = array_slice($edit->orig, 0, $ntrail);
+														$block[] = &new Text_Diff_Op_copy($context);
+												}
+												$output .= $this->_block($x0, $ntrail + $xi - $x0,
+ 																								$y0, $ntrail + $yi - $y0,
+ 																								$block);
+												$block = false;
+										}
+								}
+								$context = $edit->orig;
+						} else {
+								if (!is_array($block)) {
+										$context = array_slice($context, count($context) - $nlead);
+										$x0 = $xi - count($context);
+										$y0 = $yi - count($context);
+										$block = array();
+										if ($context) {
+												$block[] = &new Text_Diff_Op_copy($context);
+										}
+								}
+								$block[] = $edit;
+						}
 
-            if ($edit->orig) {
-                $xi += count($edit->orig);
-            }
-            if ($edit->final) {
-                $yi += count($edit->final);
-            }
-        }
+						if ($edit->orig) {
+								$xi += count($edit->orig);
+						}
+						if ($edit->final) {
+								$yi += count($edit->final);
+						}
+				}
 
-        if (is_array($block)) {
-            $output .= $this->_block($x0, $xi - $x0,
-                                     $y0, $yi - $y0,
-                                     $block);
-        }
+				if (is_array($block)) {
+						$output .= $this->_block($x0, $xi - $x0,
+ 																		$y0, $yi - $y0,
+ 																		$block);
+				}
 
-        return $output . $this->_endDiff();
-    }
+				return $output . $this->_endDiff();
+		}
 
-    function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
-    {
-        $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
+		function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
+		{
+				$output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
 
-        foreach ($edits as $edit) {
-            switch (strtolower(get_class($edit))) {
-            case 'text_diff_op_copy':
-                $output .= $this->_context($edit->orig);
-                break;
+				foreach ($edits as $edit) {
+						switch (strtolower(get_class($edit))) {
+						case 'text_diff_op_copy':
+								$output .= $this->_context($edit->orig);
+								break;
 
-            case 'text_diff_op_add':
-                $output .= $this->_added($edit->final);
-                break;
+						case 'text_diff_op_add':
+								$output .= $this->_added($edit->final);
+								break;
 
-            case 'text_diff_op_delete':
-                $output .= $this->_deleted($edit->orig);
-                break;
+						case 'text_diff_op_delete':
+								$output .= $this->_deleted($edit->orig);
+								break;
 
-            case 'text_diff_op_change':
-                $output .= $this->_changed($edit->orig, $edit->final);
-                break;
-            }
-        }
+						case 'text_diff_op_change':
+								$output .= $this->_changed($edit->orig, $edit->final);
+								break;
+						}
+				}
 
-        return $output . $this->_endBlock();
-    }
+				return $output . $this->_endBlock();
+		}
 
-    function _startDiff()
-    {
-        return '';
-    }
+		function _startDiff()
+		{
+				return '';
+		}
 
-    function _endDiff()
-    {
-        return '';
-    }
+		function _endDiff()
+		{
+				return '';
+		}
 
-    function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
-    {
-        if ($xlen > 1) {
-            $xbeg .= ',' . ($xbeg + $xlen - 1);
-        }
-        if ($ylen > 1) {
-            $ybeg .= ',' . ($ybeg + $ylen - 1);
-        }
+		function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
+		{
+				if ($xlen > 1) {
+						$xbeg .= ',' . ($xbeg + $xlen - 1);
+				}
+				if ($ylen > 1) {
+						$ybeg .= ',' . ($ybeg + $ylen - 1);
+				}
 
-        return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
-    }
+				return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
+		}
 
-    function _startBlock($header)
-    {
-        return $header . "\n";
-    }
+		function _startBlock($header)
+		{
+				return $header . "\n";
+		}
 
-    function _endBlock()
-    {
-        return '';
-    }
+		function _endBlock()
+		{
+				return '';
+		}
 
-    function _lines($lines, $prefix = ' ')
-    {
-        return $prefix . implode("\n$prefix", $lines) . "\n";
-    }
+		function _lines($lines, $prefix = ' ')
+		{
+				return $prefix . implode("\n$prefix", $lines) . "\n";
+		}
 
-    function _context($lines)
-    {
-        return $this->_lines($lines);
-    }
+		function _context($lines)
+		{
+				return $this->_lines($lines);
+		}
 
-    function _added($lines)
-    {
-        return $this->_lines($lines, '>');
-    }
+		function _added($lines)
+		{
+				return $this->_lines($lines, '>');
+		}
 
-    function _deleted($lines)
-    {
-        return $this->_lines($lines, '<');
-    }
+		function _deleted($lines)
+		{
+				return $this->_lines($lines, '<');
+		}
 
-    function _changed($orig, $final)
-    {
-        return $this->_deleted($orig) . "---\n" . $this->_added($final);
-    }
+		function _changed($orig, $final)
+		{
+				return $this->_deleted($orig) . "---\n" . $this->_added($final);
+		}
 
 }
--- a/includes/diffengine/Renderer/inline.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/diffengine/Renderer/inline.php	Sun Mar 28 23:10:46 2010 -0400
@@ -12,147 +12,147 @@
  */
 class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
 
-    /**
-     * Number of leading context "lines" to preserve.
-     */
-    var $_leading_context_lines = 10000;
+		/**
+ 		* Number of leading context "lines" to preserve.
+ 		*/
+		var $_leading_context_lines = 10000;
 
-    /**
-     * Number of trailing context "lines" to preserve.
-     */
-    var $_trailing_context_lines = 10000;
+		/**
+ 		* Number of trailing context "lines" to preserve.
+ 		*/
+		var $_trailing_context_lines = 10000;
 
-    /**
-     * Prefix for inserted text.
-     */
-    var $_ins_prefix = '<ins>';
+		/**
+ 		* Prefix for inserted text.
+ 		*/
+		var $_ins_prefix = '<ins>';
 
-    /**
-     * Suffix for inserted text.
-     */
-    var $_ins_suffix = '</ins>';
+		/**
+ 		* Suffix for inserted text.
+ 		*/
+		var $_ins_suffix = '</ins>';
 
-    /**
-     * Prefix for deleted text.
-     */
-    var $_del_prefix = '<del>';
+		/**
+ 		* Prefix for deleted text.
+ 		*/
+		var $_del_prefix = '<del>';
 
-    /**
-     * Suffix for deleted text.
-     */
-    var $_del_suffix = '</del>';
+		/**
+ 		* Suffix for deleted text.
+ 		*/
+		var $_del_suffix = '</del>';
 
-    /**
-     * Header for each change block.
-     */
-    var $_block_header = '';
+		/**
+ 		* Header for each change block.
+ 		*/
+		var $_block_header = '';
 
-    /**
-     * What are we currently splitting on? Used to recurse to show word-level
-     * changes.
-     */
-    var $_split_level = 'words';
+		/**
+ 		* What are we currently splitting on? Used to recurse to show word-level
+ 		* changes.
+ 		*/
+		var $_split_level = 'words';
 
-    function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
-    {
-        return $this->_block_header;
-    }
+		function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
+		{
+				return $this->_block_header;
+		}
 
-    function _startBlock($header)
-    {
-        return $header;
-    }
+		function _startBlock($header)
+		{
+				return $header;
+		}
 
-    function _lines($lines, $prefix = ' ', $encode = true)
-    {
-        if ($encode) {
-            array_walk($lines, array(&$this, '_encode'));
-        }
+		function _lines($lines, $prefix = ' ', $encode = true)
+		{
+				if ($encode) {
+						array_walk($lines, array(&$this, '_encode'));
+				}
 
-        if ($this->_split_level == 'words') {
-            return implode('', $lines);
-        } else {
-            return implode("\n", $lines) . "\n";
-        }
-    }
+				if ($this->_split_level == 'words') {
+						return implode('', $lines);
+				} else {
+						return implode("\n", $lines) . "\n";
+				}
+		}
 
-    function _added($lines)
-    {
-        array_walk($lines, array(&$this, '_encode'));
-        $lines[0] = $this->_ins_prefix . $lines[0];
-        $lines[count($lines) - 1] .= $this->_ins_suffix;
-        return $this->_lines($lines, ' ', false);
-    }
+		function _added($lines)
+		{
+				array_walk($lines, array(&$this, '_encode'));
+				$lines[0] = $this->_ins_prefix . $lines[0];
+				$lines[count($lines) - 1] .= $this->_ins_suffix;
+				return $this->_lines($lines, ' ', false);
+		}
 
-    function _deleted($lines, $words = false)
-    {
-        array_walk($lines, array(&$this, '_encode'));
-        $lines[0] = $this->_del_prefix . $lines[0];
-        $lines[count($lines) - 1] .= $this->_del_suffix;
-        return $this->_lines($lines, ' ', false);
-    }
-    
-    function _context($lines)
-    {
-        return "<!-- Start context -->\n<tr><td></td><td class=\"diff-context\">".$this->_lines($lines).'</td></tr>'."\n<!-- End context -->\n\n";
-    }
+		function _deleted($lines, $words = false)
+		{
+				array_walk($lines, array(&$this, '_encode'));
+				$lines[0] = $this->_del_prefix . $lines[0];
+				$lines[count($lines) - 1] .= $this->_del_suffix;
+				return $this->_lines($lines, ' ', false);
+		}
+		
+		function _context($lines)
+		{
+				return "<!-- Start context -->\n<tr><td></td><td class=\"diff-context\">".$this->_lines($lines).'</td></tr>'."\n<!-- End context -->\n\n";
+		}
 
-    function _changed($orig, $final)
-    {
-        /* If we've already split on words, don't try to do so again - just
-         * display. */
-        if ($this->_split_level == 'words') {
-            $prefix = '';
-            while ($orig[0] !== false && $final[0] !== false &&
-                   substr($orig[0], 0, 1) == ' ' &&
-                   substr($final[0], 0, 1) == ' ') {
-                $prefix .= substr($orig[0], 0, 1);
-                $orig[0] = substr($orig[0], 1);
-                $final[0] = substr($final[0], 1);
-            }
-            return $prefix . $this->_deleted($orig) . $this->_added($final);
-        }
+		function _changed($orig, $final)
+		{
+				/* If we've already split on words, don't try to do so again - just
+ 				* display. */
+				if ($this->_split_level == 'words') {
+						$prefix = '';
+						while ($orig[0] !== false && $final[0] !== false &&
+ 									substr($orig[0], 0, 1) == ' ' &&
+ 									substr($final[0], 0, 1) == ' ') {
+								$prefix .= substr($orig[0], 0, 1);
+								$orig[0] = substr($orig[0], 1);
+								$final[0] = substr($final[0], 1);
+						}
+						return $prefix . $this->_deleted($orig) . $this->_added($final);
+				}
 
-        $text1 = implode("\n", $orig);
-        $text2 = implode("\n", $final);
+				$text1 = implode("\n", $orig);
+				$text2 = implode("\n", $final);
 
-        /* Non-printing newline marker. */
-        $nl = "\0";
+				/* Non-printing newline marker. */
+				$nl = "\0";
 
-        /* We want to split on word boundaries, but we need to
-         * preserve whitespace as well. Therefore we split on words,
-         * but include all blocks of whitespace in the wordlist. */
-        $diff = &new Text_Diff($this->_splitOnWords($text1, $nl),
-                               $this->_splitOnWords($text2, $nl));
+				/* We want to split on word boundaries, but we need to
+ 				* preserve whitespace as well. Therefore we split on words,
+ 				* but include all blocks of whitespace in the wordlist. */
+				$diff = &new Text_Diff($this->_splitOnWords($text1, $nl),
+ 															$this->_splitOnWords($text2, $nl));
 
-        /* Get the diff in inline format. */
-        $renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(),
-                                                               array('split_level' => 'words')));
+				/* Get the diff in inline format. */
+				$renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(),
+ 																															array('split_level' => 'words')));
 
-        /* Run the diff and get the output. */
-        return str_replace($nl, "\n", $renderer->render($diff)) . "\n";
-    }
+				/* Run the diff and get the output. */
+				return str_replace($nl, "\n", $renderer->render($diff)) . "\n";
+		}
 
-    function _splitOnWords($string, $newlineEscape = "\n")
-    {
-        $words = array();
-        $length = strlen($string);
-        $pos = 0;
+		function _splitOnWords($string, $newlineEscape = "\n")
+		{
+				$words = array();
+				$length = strlen($string);
+				$pos = 0;
 
-        while ($pos < $length) {
-            // Eat a word with any preceding whitespace.
-            $spaces = strspn(substr($string, $pos), " \n");
-            $nextpos = strcspn(substr($string, $pos + $spaces), " \n");
-            $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos));
-            $pos += $spaces + $nextpos;
-        }
+				while ($pos < $length) {
+						// Eat a word with any preceding whitespace.
+						$spaces = strspn(substr($string, $pos), " \n");
+						$nextpos = strcspn(substr($string, $pos + $spaces), " \n");
+						$words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos));
+						$pos += $spaces + $nextpos;
+				}
 
-        return $words;
-    }
+				return $words;
+		}
 
-    function _encode(&$string)
-    {
-        $string = htmlspecialchars($string);
-    }
+		function _encode(&$string)
+		{
+				$string = htmlspecialchars($string);
+		}
 
 }
--- a/includes/diffengine/Renderer/unified.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/diffengine/Renderer/unified.php	Sun Mar 28 23:10:46 2010 -0400
@@ -11,40 +11,40 @@
  */
 class Text_Diff_Renderer_unified extends Text_Diff_Renderer {
 
-    /**
-     * Number of leading context "lines" to preserve.
-     */
-    var $_leading_context_lines = 4;
+		/**
+ 		* Number of leading context "lines" to preserve.
+ 		*/
+		var $_leading_context_lines = 4;
 
-    /**
-     * Number of trailing context "lines" to preserve.
-     */
-    var $_trailing_context_lines = 4;
+		/**
+ 		* Number of trailing context "lines" to preserve.
+ 		*/
+		var $_trailing_context_lines = 4;
 
-    function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
-    {
-        if ($xlen != 1) {
-            $xbeg .= ',' . $xlen;
-        }
-        if ($ylen != 1) {
-            $ybeg .= ',' . $ylen;
-        }
-        return "@@ -$xbeg +$ybeg @@";
-    }
+		function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
+		{
+				if ($xlen != 1) {
+						$xbeg .= ',' . $xlen;
+				}
+				if ($ylen != 1) {
+						$ybeg .= ',' . $ylen;
+				}
+				return "@@ -$xbeg +$ybeg @@";
+		}
 
-    function _added($lines)
-    {
-        return $this->_lines($lines, '+');
-    }
+		function _added($lines)
+		{
+				return $this->_lines($lines, '+');
+		}
 
-    function _deleted($lines)
-    {
-        return $this->_lines($lines, '-');
-    }
+		function _deleted($lines)
+		{
+				return $this->_lines($lines, '-');
+		}
 
-    function _changed($orig, $final)
-    {
-        return $this->_deleted($orig) . $this->_added($final);
-    }
+		function _changed($orig, $final)
+		{
+				return $this->_deleted($orig) . $this->_added($final);
+		}
 
 }
--- a/includes/diffengine/Renderer/xhtml.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/diffengine/Renderer/xhtml.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,250 +13,250 @@
  */
 class Text_Diff_Renderer_xhtml extends Text_Diff_Renderer {
 
-    /**
-     * Number of leading context "lines" to preserve.
-     */
-    var $_leading_context_lines = 5;
+		/**
+ 		* Number of leading context "lines" to preserve.
+ 		*/
+		var $_leading_context_lines = 5;
 
-    /**
-     * Number of trailing context "lines" to preserve.
-     */
-    var $_trailing_context_lines = 3;
+		/**
+ 		* Number of trailing context "lines" to preserve.
+ 		*/
+		var $_trailing_context_lines = 3;
 
-    /**
-     * Prefix for inserted text.
-     */
-    var $_ins_prefix = "<!-- Start added text -->\n<tr><td style='width: 0px;'>+</td><td class=\"diff-added\" style='width: 100%;'>";
+		/**
+ 		* Prefix for inserted text.
+ 		*/
+		var $_ins_prefix = "<!-- Start added text -->\n<tr><td style='width: 0px;'>+</td><td class=\"diff-added\" style='width: 100%;'>";
 
-    /**
-     * Suffix for inserted text.
-     */
-    var $_ins_suffix = "</td></tr>\n<!-- End added text -->\n\n";
+		/**
+ 		* Suffix for inserted text.
+ 		*/
+		var $_ins_suffix = "</td></tr>\n<!-- End added text -->\n\n";
 
-    /**
-     * Prefix for deleted text.
-     */
-    var $_del_prefix = "<!-- Start deleted text -->\n<tr><td style='width: 0px;'>-</td><td class=\"diff-deleted\" style='width: 100%;'>";
+		/**
+ 		* Prefix for deleted text.
+ 		*/
+		var $_del_prefix = "<!-- Start deleted text -->\n<tr><td style='width: 0px;'>-</td><td class=\"diff-deleted\" style='width: 100%;'>";
 
-    /**
-     * Suffix for deleted text.
-     */
-    var $_del_suffix = "</td></tr>\n<!-- End deleted text -->\n\n";
+		/**
+ 		* Suffix for deleted text.
+ 		*/
+		var $_del_suffix = "</td></tr>\n<!-- End deleted text -->\n\n";
 
-    /**
-     * Header for each change block.
-     */
-    var $_block_header = '';
+		/**
+ 		* Header for each change block.
+ 		*/
+		var $_block_header = '';
 
-    /**
-     * What are we currently splitting on? Used to recurse to show word-level
-     * changes.
-     */
-    var $_split_level = 'lines';
-    
-    function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
-    {
-      return "<!-- Start block -->\n<tr><td colspan='2' class='diff-block'>Line $xbeg: {$this->_block_header}</td></tr>";
-    }
+		/**
+ 		* What are we currently splitting on? Used to recurse to show word-level
+ 		* changes.
+ 		*/
+		var $_split_level = 'lines';
+		
+		function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
+		{
+			return "<!-- Start block -->\n<tr><td colspan='2' class='diff-block'>Line $xbeg: {$this->_block_header}</td></tr>";
+		}
 
-    function _startBlock($header)
-    {
-        return $header;
-    }
+		function _startBlock($header)
+		{
+				return $header;
+		}
 
-    function _lines($lines, $prefix = ' ', $encode = true)
-    {
-        if ($encode) {
-            array_walk($lines, array(&$this, '_encode'));
-        }
+		function _lines($lines, $prefix = ' ', $encode = true)
+		{
+				if ($encode) {
+						array_walk($lines, array(&$this, '_encode'));
+				}
 
-        if ($this->_split_level == 'words') {
-            return implode('', $lines);
-        } else {
-            return implode("<br />", $lines) . "\n";
-        }
-    }
+				if ($this->_split_level == 'words') {
+						return implode('', $lines);
+				} else {
+						return implode("<br />", $lines) . "\n";
+				}
+		}
 
-    function _added($lines)
-    {
-        array_walk($lines, array(&$this, '_encode'));
-        $lines[0] = $this->_ins_prefix . $lines[0];
-        $lines[count($lines) - 1] .= $this->_ins_suffix;
-        return $this->_lines($lines, ' ', false);
-    }
+		function _added($lines)
+		{
+				array_walk($lines, array(&$this, '_encode'));
+				$lines[0] = $this->_ins_prefix . $lines[0];
+				$lines[count($lines) - 1] .= $this->_ins_suffix;
+				return $this->_lines($lines, ' ', false);
+		}
 
-    function _deleted($lines, $words = false)
-    {
-        array_walk($lines, array(&$this, '_encode'));
-        $lines[0] = $this->_del_prefix . $lines[0];
-        $lines[count($lines) - 1] .= $this->_del_suffix;
-        return $this->_lines($lines, ' ', false);
-    }
-    
-    function _context($lines)
-    {
-        return "<!-- Start context -->\n<tr><td></td><td class=\"diff-context\">".$this->_lines($lines).'</td></tr>'."\n<!-- End context -->\n\n";
-    }
+		function _deleted($lines, $words = false)
+		{
+				array_walk($lines, array(&$this, '_encode'));
+				$lines[0] = $this->_del_prefix . $lines[0];
+				$lines[count($lines) - 1] .= $this->_del_suffix;
+				return $this->_lines($lines, ' ', false);
+		}
+		
+		function _context($lines)
+		{
+				return "<!-- Start context -->\n<tr><td></td><td class=\"diff-context\">".$this->_lines($lines).'</td></tr>'."\n<!-- End context -->\n\n";
+		}
 
-    function _changed($orig, $final)
-    {
-        /* If we've already split on words, don't try to do so again - just display. */ 
-        if ($this->_split_level == 'words') {
-            $prefix = '';
-            while ($orig[0] !== false && $final[0] !== false &&
-                   substr($orig[0], 0, 1) == ' ' &&
-                   substr($final[0], 0, 1) == ' ') {
-                $prefix .= substr($orig[0], 0, 1);
-                $orig[0] = substr($orig[0], 1);
-                $final[0] = substr($final[0], 1);
-            }
-            $ret = $prefix . $this->_deleted($orig) . $this->_added($final) . "\n";
-            //echo 'DEBUG:<pre>'.htmlspecialchars($ret).'</pre>';
-            return $ret;
-        }
+		function _changed($orig, $final)
+		{
+				/* If we've already split on words, don't try to do so again - just display. */ 
+				if ($this->_split_level == 'words') {
+						$prefix = '';
+						while ($orig[0] !== false && $final[0] !== false &&
+ 									substr($orig[0], 0, 1) == ' ' &&
+ 									substr($final[0], 0, 1) == ' ') {
+								$prefix .= substr($orig[0], 0, 1);
+								$orig[0] = substr($orig[0], 1);
+								$final[0] = substr($final[0], 1);
+						}
+						$ret = $prefix . $this->_deleted($orig) . $this->_added($final) . "\n";
+						//echo 'DEBUG:<pre>'.htmlspecialchars($ret).'</pre>';
+						return $ret;
+				}
 
-        $text1 = implode("\n", $orig);
-        $text2 = implode("\n", $final);
+				$text1 = implode("\n", $orig);
+				$text2 = implode("\n", $final);
 
-        /* Non-printing newline marker. */
-        $nl = "\0";
+				/* Non-printing newline marker. */
+				$nl = "\0";
 
-        /* We want to split on word boundaries, but we need to
-         * preserve whitespace as well. Therefore we split on words,
-         * but include all blocks of whitespace in the wordlist. */
-        $diff = &new Text_Diff($this->_splitOnWords($text1, $nl),
-                               $this->_splitOnWords($text2, $nl));
+				/* We want to split on word boundaries, but we need to
+ 				* preserve whitespace as well. Therefore we split on words,
+ 				* but include all blocks of whitespace in the wordlist. */
+				$diff = &new Text_Diff($this->_splitOnWords($text1, $nl),
+ 															$this->_splitOnWords($text2, $nl));
 
-        /* Get the diff in inline format. */
-        $renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(),
-                                                               array('split_level' => 'words')));
+				/* Get the diff in inline format. */
+				$renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(),
+ 																															array('split_level' => 'words')));
 
-        /* Run the diff and get the output. */
-        $ret = str_replace($nl, "<br />", $renderer->render($diff));
-        //echo 'DEBUG:<pre>'.htmlspecialchars($ret).'</pre>';
-        return $ret . "\n";
-    }
+				/* Run the diff and get the output. */
+				$ret = str_replace($nl, "<br />", $renderer->render($diff));
+				//echo 'DEBUG:<pre>'.htmlspecialchars($ret).'</pre>';
+				return $ret . "\n";
+		}
 
-    function _splitOnWords($string, $newlineEscape = "<br />")
-    {
-        $words = array();
-        $length = strlen($string);
-        $pos = 0;
+		function _splitOnWords($string, $newlineEscape = "<br />")
+		{
+				$words = array();
+				$length = strlen($string);
+				$pos = 0;
 
-        while ($pos < $length) {
-            // Eat a word with any preceding whitespace.
-            $spaces = strspn(substr($string, $pos), " \n");
-            $nextpos = strcspn(substr($string, $pos + $spaces), " \n");
-            $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos));
-            $pos += $spaces + $nextpos;
-        }
+				while ($pos < $length) {
+						// Eat a word with any preceding whitespace.
+						$spaces = strspn(substr($string, $pos), " \n");
+						$nextpos = strcspn(substr($string, $pos + $spaces), " \n");
+						$words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos));
+						$pos += $spaces + $nextpos;
+				}
 
-        return $words;
-    }
+				return $words;
+		}
 
-    function _encode(&$string)
-    {
-        $string = htmlspecialchars($string);
-    }
-    
-    /**
-     * Renders a diff.
-     *
-     * @param Text_Diff $diff  A Text_Diff object.
-     *
-     * @return string  The formatted output.
-     */
-    
-    function render($diff)
-    {
-        $xi = $yi = 1;
-        $block = false;
-        $context = array();
+		function _encode(&$string)
+		{
+				$string = htmlspecialchars($string);
+		}
+		
+		/**
+ 		* Renders a diff.
+ 		*
+ 		* @param Text_Diff $diff  A Text_Diff object.
+ 		*
+ 		* @return string  The formatted output.
+ 		*/
+		
+		function render($diff)
+		{
+				$xi = $yi = 1;
+				$block = false;
+				$context = array();
 
-        $nlead = $this->_leading_context_lines;
-        $ntrail = $this->_trailing_context_lines;
+				$nlead = $this->_leading_context_lines;
+				$ntrail = $this->_trailing_context_lines;
 
-        $output = $this->_startDiff();
+				$output = $this->_startDiff();
 
-        $diffs = $diff->getDiff();
-        foreach ($diffs as $i => $edit) {
-            if (is_a($edit, 'Text_Diff_Op_copy')) {
-                if (is_array($block)) {
-                    $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
-                    if (count($edit->orig) <= $keep) {
-                        $block[] = $edit;
-                    } else {
-                        if ($ntrail) {
-                            $context = array_slice($edit->orig, 0, $ntrail);
-                            $block[] = &new Text_Diff_Op_copy($context);
-                        }
-                        $bk = $this->_block($x0, $ntrail + $xi - $x0,
-                                            $y0, $ntrail + $yi - $y0,
-                                            $block);
-                        $output .= $bk;
-                        $block = false;
-                    }
-                }
-                $context = $edit->orig;
-            } else {
-                if (!is_array($block)) {
-                    $context = array_slice($context, count($context) - $nlead);
-                    $x0 = $xi - count($context);
-                    $y0 = $yi - count($context);
-                    $block = array();
-                    if ($context) {
-                        $block[] = &new Text_Diff_Op_copy($context);
-                    }
-                }
-                $block[] = $edit;
-            }
+				$diffs = $diff->getDiff();
+				foreach ($diffs as $i => $edit) {
+						if (is_a($edit, 'Text_Diff_Op_copy')) {
+								if (is_array($block)) {
+										$keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
+										if (count($edit->orig) <= $keep) {
+												$block[] = $edit;
+										} else {
+												if ($ntrail) {
+														$context = array_slice($edit->orig, 0, $ntrail);
+														$block[] = &new Text_Diff_Op_copy($context);
+												}
+												$bk = $this->_block($x0, $ntrail + $xi - $x0,
+																						$y0, $ntrail + $yi - $y0,
+																						$block);
+												$output .= $bk;
+												$block = false;
+										}
+								}
+								$context = $edit->orig;
+						} else {
+								if (!is_array($block)) {
+										$context = array_slice($context, count($context) - $nlead);
+										$x0 = $xi - count($context);
+										$y0 = $yi - count($context);
+										$block = array();
+										if ($context) {
+												$block[] = &new Text_Diff_Op_copy($context);
+										}
+								}
+								$block[] = $edit;
+						}
 
-            if ($edit->orig) {
-                $xi += count($edit->orig);
-            }
-            if ($edit->final) {
-                $yi += count($edit->final);
-            }
-        }
+						if ($edit->orig) {
+								$xi += count($edit->orig);
+						}
+						if ($edit->final) {
+								$yi += count($edit->final);
+						}
+				}
 
-        if (is_array($block)) {
-            $bk = $this->_block($x0, $xi - $x0,
-                                $y0, $yi - $y0,
-                                $block);
-            $output .= $bk;
-        }
+				if (is_array($block)) {
+						$bk = $this->_block($x0, $xi - $x0,
+																$y0, $yi - $y0,
+																$block);
+						$output .= $bk;
+				}
 
-        $final = $output . $this->_endDiff();
-        if ($final == '') $final = '<tr><td class="diff-block">No differences.</td></tr>';
-        //$final = preg_replace('#('.preg_quote($this->_ins_suffix).'|'.preg_quote($this->_del_suffix).')(.+?)('.preg_quote($this->_ins_prefix).'|'.preg_quote($this->_ins_suffix).')#', '\\1<tr><td></td><td class="diff-context>\\2</td></tr>\\3', $final);
-        return '<table class="diff">'.$final.'</table>'."\n\n";
-    }
-    
-    function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
-    {
-        $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
+				$final = $output . $this->_endDiff();
+				if ($final == '') $final = '<tr><td class="diff-block">No differences.</td></tr>';
+				//$final = preg_replace('#('.preg_quote($this->_ins_suffix).'|'.preg_quote($this->_del_suffix).')(.+?)('.preg_quote($this->_ins_prefix).'|'.preg_quote($this->_ins_suffix).')#', '\\1<tr><td></td><td class="diff-context>\\2</td></tr>\\3', $final);
+				return '<table class="diff">'.$final.'</table>'."\n\n";
+		}
+		
+		function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
+		{
+				$output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
 
-        foreach ($edits as $edit) {
-            switch (strtolower(get_class($edit))) {
-            case 'text_diff_op_copy':
-                $output .= $this->_context($edit->orig);
-                break;
+				foreach ($edits as $edit) {
+						switch (strtolower(get_class($edit))) {
+						case 'text_diff_op_copy':
+								$output .= $this->_context($edit->orig);
+								break;
 
-            case 'text_diff_op_add':
-                $output .= $this->_added($edit->final);
-                break;
+						case 'text_diff_op_add':
+								$output .= $this->_added($edit->final);
+								break;
 
-            case 'text_diff_op_delete':
-                $output .= $this->_deleted($edit->orig);
-                break;
+						case 'text_diff_op_delete':
+								$output .= $this->_deleted($edit->orig);
+								break;
 
-            case 'text_diff_op_change':
-                $output .= $this->_changed($edit->orig, $edit->final);
-                break;
-            }
-        }
+						case 'text_diff_op_change':
+								$output .= $this->_changed($edit->orig, $edit->final);
+								break;
+						}
+				}
 
-        return $output . $this->_endBlock();
-    }
+				return $output . $this->_endBlock();
+		}
 
 }
--- a/includes/diffiehellman.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/diffiehellman.php	Sun Mar 28 23:10:46 2010 -0400
@@ -20,11 +20,11 @@
 $dh_supported = true;
 try
 {
-  $GLOBALS['_math'] = enanomath_create();
+	$GLOBALS['_math'] = enanomath_create();
 }
 catch ( Exception $e )
 {
-  $dh_supported = false;
+	$dh_supported = false;
 }
 // Our prime number as a base for operations.
 $GLOBALS['dh_prime'] = '7916586051748534588306961133067968196965257961415756656521818848750723547477673457670019632882524164647651492025728980571833579341743988603191694784406703';
@@ -40,8 +40,8 @@
 
 function dh_gen_private()
 {
-  global $_math;
-  return $_math->random(256);
+	global $_math;
+	return $_math->random(256);
 }
 
 /**
@@ -52,8 +52,8 @@
 
 function dh_gen_public($b)
 {
-  global $_math, $dh_g, $dh_prime;
-  return $_math->powmod($dh_g, $b, $dh_prime);
+	global $_math, $dh_g, $dh_prime;
+	return $_math->powmod($dh_g, $b, $dh_prime);
 }
 
 /**
@@ -65,8 +65,8 @@
 
 function dh_gen_shared_secret($a, $B)
 {
-  global $_math, $dh_g, $dh_prime;
-  return $_math->powmod($B, $a, $dh_prime);
+	global $_math, $dh_g, $dh_prime;
+	return $_math->powmod($B, $a, $dh_prime);
 }
 
 /*
@@ -80,13 +80,13 @@
 are permitted provided that the following conditions are met:
 
  * Redistributions of source code must retain the above copyright notice, this
-   list of conditions and the following disclaimer.
+ 	list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
+ 	this list of conditions and the following disclaimer in the documentation
+ 	and/or other materials provided with the distribution.
  * Neither the name of the <ORGANIZATION> nor the names of its contributors may
-   be used to endorse or promote products derived from this software without
-   specific prior written permission.
+ 	be used to endorse or promote products derived from this software without
+ 	specific prior written permission.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -101,165 +101,165 @@
 */
 class SHA256
 {
-  var $chrsz = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode  */
-  
-  function safe_add ($x, $y) {
-    $lsw = ($x & 0xFFFF) + ($y & 0xFFFF);
-    $msw = ($x >> 16) + ($y >> 16) + ($lsw >> 16);
-    // 2009-07-02 Added & 0xFFFFFFFF here to fix problem on PHP w/ native 64-bit integer support (rev. 1030)
-    return (($msw << 16) | ($lsw & 0xFFFF)) & 0xFFFFFFFF;
-  }
-  function rshz($X, $n)
-  {
-    // equivalent to $X >>> $n in javascript
-    // pulled from http://www.tapouillo.com/firefox_extension/sourcecode.txt, public domain
-    $z = hexdec(80000000); 
-    if ($z & $X) 
-    { 
-        $X = ($X>>1); 
-        $X &= (~$z); 
-        $X |= 0x40000000; 
-        $X = ($X>>($n-1)); 
-    } 
-    else 
-    { 
-        $X = ($X>>$n); 
-    } 
-    return $X; 
-  }
-  function S ($X, $n) {return ( $this->rshz($X, $n) ) | ($X << (32 - $n));}
-  function R ($X, $n) {return ( $this->rshz($X, $n) );}
-  function Ch($x, $y, $z)  {return (($x & $y) ^ ((~$x) & $z));}
-  function Maj($x, $y, $z) {return (($x & $y) ^ ($x & $z) ^ ($y & $z));}
-  function Sigma0256($x) {return ($this->S($x, 2)  ^ $this->S($x, 13) ^ $this->S($x, 22));}
-  function Sigma1256($x) {return ($this->S($x, 6)  ^ $this->S($x, 11) ^ $this->S($x, 25));}
-  function Gamma0256($x) {return ($this->S($x, 7)  ^ $this->S($x, 18) ^ $this->R($x, 3));}
-  function Gamma1256($x) {return ($this->S($x, 17) ^ $this->S($x, 19) ^ $this->R($x, 10));}
-  function core_sha256 ($m, $l) {
-      $K = Array(0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2);
-      $HASH = Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
-      $W = Array(64);
-      /* append padding */
-      if ( !isset($m[$l >> 5]) )
-        $m[$l >> 5] = 0;
-      $m[$l >> 5] |= 0x80 << (24 - $l % 32);
-      $m[(($l + 64 >> 9) << 4) + 15] = $l;
-      for ( $i = 0; $i<count($m); $i+=16 ) {
-          $a = $HASH[0];
-          $b = $HASH[1];
-          $c = $HASH[2];
-          $d = $HASH[3];
-          $e = $HASH[4];
-          $f = $HASH[5];
-          $g = $HASH[6];
-          $h = $HASH[7];
-          for ( $j = 0; $j<64; $j++)
-          {
-              if ( $j < 16 )
-              {
-                $W[$j] = ( isset($m[$j + $i]) ) ? $m[$j + $i] : 0;
-              }
-              else
-              {
-                $W[$j] = $this->safe_add(
-                  $this->safe_add(
-                    $this->safe_add(
-                      $this->Gamma1256($W[$j - 2]), $W[$j - 7]),
-                    $this->Gamma0256($W[$j - 15])),
-                  $W[$j - 16]);
-              }
-              $T1 = $this->safe_add(
-                $this->safe_add(
-                  $this->safe_add(
-                    $this->safe_add($h, $this->Sigma1256($e)
-                      ),
-                    $this->Ch($e, $f, $g)),
-                  $K[$j]),
-                $W[$j]);
-              $T2 = $this->safe_add($this->Sigma0256($a), $this->Maj($a, $b, $c));
-              $h = $g;
-              $g = $f;
-              $f = $e;
-              $e = $this->safe_add($d, $T1);
-              $d = $c;
-              $c = $b;
-              $b = $a;
-              $a = $this->safe_add($T1, $T2);
-          }
-          $HASH[0] = $this->safe_add($a, $HASH[0]);
-          $HASH[1] = $this->safe_add($b, $HASH[1]);
-          $HASH[2] = $this->safe_add($c, $HASH[2]);
-          $HASH[3] = $this->safe_add($d, $HASH[3]);
-          $HASH[4] = $this->safe_add($e, $HASH[4]);
-          $HASH[5] = $this->safe_add($f, $HASH[5]);
-          $HASH[6] = $this->safe_add($g, $HASH[6]);
-          $HASH[7] = $this->safe_add($h, $HASH[7]);
-      }
-      return $HASH;
-  }
-  function str2binb ($str) {
-    $bin = Array();
-    for ( $i = 0; $i < strlen($str); $i++ )
-    {
-      $byte = ord($str{$i});
-      $block = floor($i / 4);
-      $stage = $i % 4;
-      if ( $stage == 0 )
-      {
-        $bin[$block] = $byte;
-      }
-      else
-      {
-        $bin[$block] <<= 8;
-        $bin[$block] |= $byte;
-      }
-    }
-    while ( $stage < 3 )
-    {
-      $stage++;
-      $bin[$block] <<= 8;
-    }
-    return $bin;
-  }
-  function byte2hex($byte)
-  {
-    $b = dechex(ord($byte));
-    return ( strlen($b) < 2 ) ? "0$b" : $b;
-  }
-  function binb2hex ($binarray) {
-    $hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
-    $hex_tab = $hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
-    $str = "";
-    foreach ( $binarray as $bytes )
-    {
-      $str .= implode('', array(
-          $this->byte2hex(chr(( $bytes >> 24 ) & 0xFF)),
-          $this->byte2hex(chr(( $bytes >> 16 ) & 0xFF)),
-          $this->byte2hex(chr(( $bytes >> 8 ) & 0xFF)),
-          $this->byte2hex(chr($bytes & 0xFF))
-        ));
-    }
-    return $str;
-  }
-  function hex_sha256 ( $s )
-  {
-    return $this->binb2hex(
-      $this->core_sha256(
-        $this->str2binb($s),
-        strlen($s) * $this->chrsz)
-      );
-  }
+	var $chrsz = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode  */
+	
+	function safe_add ($x, $y) {
+		$lsw = ($x & 0xFFFF) + ($y & 0xFFFF);
+		$msw = ($x >> 16) + ($y >> 16) + ($lsw >> 16);
+		// 2009-07-02 Added & 0xFFFFFFFF here to fix problem on PHP w/ native 64-bit integer support (rev. 1030)
+		return (($msw << 16) | ($lsw & 0xFFFF)) & 0xFFFFFFFF;
+	}
+	function rshz($X, $n)
+	{
+		// equivalent to $X >>> $n in javascript
+		// pulled from http://www.tapouillo.com/firefox_extension/sourcecode.txt, public domain
+		$z = hexdec(80000000); 
+		if ($z & $X) 
+		{ 
+				$X = ($X>>1); 
+				$X &= (~$z); 
+				$X |= 0x40000000; 
+				$X = ($X>>($n-1)); 
+		} 
+		else 
+		{ 
+				$X = ($X>>$n); 
+		} 
+		return $X; 
+	}
+	function S ($X, $n) {return ( $this->rshz($X, $n) ) | ($X << (32 - $n));}
+	function R ($X, $n) {return ( $this->rshz($X, $n) );}
+	function Ch($x, $y, $z)  {return (($x & $y) ^ ((~$x) & $z));}
+	function Maj($x, $y, $z) {return (($x & $y) ^ ($x & $z) ^ ($y & $z));}
+	function Sigma0256($x) {return ($this->S($x, 2)  ^ $this->S($x, 13) ^ $this->S($x, 22));}
+	function Sigma1256($x) {return ($this->S($x, 6)  ^ $this->S($x, 11) ^ $this->S($x, 25));}
+	function Gamma0256($x) {return ($this->S($x, 7)  ^ $this->S($x, 18) ^ $this->R($x, 3));}
+	function Gamma1256($x) {return ($this->S($x, 17) ^ $this->S($x, 19) ^ $this->R($x, 10));}
+	function core_sha256 ($m, $l) {
+			$K = Array(0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2);
+			$HASH = Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
+			$W = Array(64);
+			/* append padding */
+			if ( !isset($m[$l >> 5]) )
+				$m[$l >> 5] = 0;
+			$m[$l >> 5] |= 0x80 << (24 - $l % 32);
+			$m[(($l + 64 >> 9) << 4) + 15] = $l;
+			for ( $i = 0; $i<count($m); $i+=16 ) {
+					$a = $HASH[0];
+					$b = $HASH[1];
+					$c = $HASH[2];
+					$d = $HASH[3];
+					$e = $HASH[4];
+					$f = $HASH[5];
+					$g = $HASH[6];
+					$h = $HASH[7];
+					for ( $j = 0; $j<64; $j++)
+					{
+							if ( $j < 16 )
+							{
+								$W[$j] = ( isset($m[$j + $i]) ) ? $m[$j + $i] : 0;
+							}
+							else
+							{
+								$W[$j] = $this->safe_add(
+									$this->safe_add(
+										$this->safe_add(
+											$this->Gamma1256($W[$j - 2]), $W[$j - 7]),
+										$this->Gamma0256($W[$j - 15])),
+									$W[$j - 16]);
+							}
+							$T1 = $this->safe_add(
+								$this->safe_add(
+									$this->safe_add(
+										$this->safe_add($h, $this->Sigma1256($e)
+											),
+										$this->Ch($e, $f, $g)),
+									$K[$j]),
+								$W[$j]);
+							$T2 = $this->safe_add($this->Sigma0256($a), $this->Maj($a, $b, $c));
+							$h = $g;
+							$g = $f;
+							$f = $e;
+							$e = $this->safe_add($d, $T1);
+							$d = $c;
+							$c = $b;
+							$b = $a;
+							$a = $this->safe_add($T1, $T2);
+					}
+					$HASH[0] = $this->safe_add($a, $HASH[0]);
+					$HASH[1] = $this->safe_add($b, $HASH[1]);
+					$HASH[2] = $this->safe_add($c, $HASH[2]);
+					$HASH[3] = $this->safe_add($d, $HASH[3]);
+					$HASH[4] = $this->safe_add($e, $HASH[4]);
+					$HASH[5] = $this->safe_add($f, $HASH[5]);
+					$HASH[6] = $this->safe_add($g, $HASH[6]);
+					$HASH[7] = $this->safe_add($h, $HASH[7]);
+			}
+			return $HASH;
+	}
+	function str2binb ($str) {
+		$bin = Array();
+		for ( $i = 0; $i < strlen($str); $i++ )
+		{
+			$byte = ord($str{$i});
+			$block = floor($i / 4);
+			$stage = $i % 4;
+			if ( $stage == 0 )
+			{
+				$bin[$block] = $byte;
+			}
+			else
+			{
+				$bin[$block] <<= 8;
+				$bin[$block] |= $byte;
+			}
+		}
+		while ( $stage < 3 )
+		{
+			$stage++;
+			$bin[$block] <<= 8;
+		}
+		return $bin;
+	}
+	function byte2hex($byte)
+	{
+		$b = dechex(ord($byte));
+		return ( strlen($b) < 2 ) ? "0$b" : $b;
+	}
+	function binb2hex ($binarray) {
+		$hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
+		$hex_tab = $hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+		$str = "";
+		foreach ( $binarray as $bytes )
+		{
+			$str .= implode('', array(
+					$this->byte2hex(chr(( $bytes >> 24 ) & 0xFF)),
+					$this->byte2hex(chr(( $bytes >> 16 ) & 0xFF)),
+					$this->byte2hex(chr(( $bytes >> 8 ) & 0xFF)),
+					$this->byte2hex(chr($bytes & 0xFF))
+				));
+		}
+		return $str;
+	}
+	function hex_sha256 ( $s )
+	{
+		return $this->binb2hex(
+			$this->core_sha256(
+				$this->str2binb($s),
+				strlen($s) * $this->chrsz)
+			);
+	}
 }
 
 if ( !function_exists('sha256') )
 {
-  function sha256($text)
-  {
-    static $sha_obj = false;
-    if ( !is_object($sha_obj) )
-      $sha_obj = new SHA256();
-    return $sha_obj->hex_sha256($text);
-  }
+	function sha256($text)
+	{
+		static $sha_obj = false;
+		if ( !is_object($sha_obj) )
+			$sha_obj = new SHA256();
+		return $sha_obj->hex_sha256($text);
+	}
 }
 
 ?>
--- a/includes/email.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/email.php	Sun Mar 28 23:10:46 2010 -0400
@@ -20,305 +20,305 @@
 //
 class emailer
 {
-  var $msg, $subject, $extra_headers;
-  var $addresses, $reply_to, $from;
-  var $use_smtp;
+	var $msg, $subject, $extra_headers;
+	var $addresses, $reply_to, $from;
+	var $use_smtp;
 
-  var $tpl_msg;
+	var $tpl_msg;
 
-  function __construct($use_smtp)
-  {
-    $this->reset();
-    $this->use_smtp = $use_smtp;
-    $this->reply_to = $this->from = '';
-  }
+	function __construct($use_smtp)
+	{
+		$this->reset();
+		$this->use_smtp = $use_smtp;
+		$this->reply_to = $this->from = '';
+	}
 
-  // Resets all the data (address, template file, etc etc to default
-  function reset()
-  {
-    $this->addresses = array();
-    $this->vars = $this->msg = $this->extra_headers = '';
-  }
+	// Resets all the data (address, template file, etc etc to default
+	function reset()
+	{
+		$this->addresses = array();
+		$this->vars = $this->msg = $this->extra_headers = '';
+	}
 
-  // Sets an email address to send to
-  function email_address($address)
-  {
-    $this->addresses['to'] = trim($address);
-  }
+	// Sets an email address to send to
+	function email_address($address)
+	{
+		$this->addresses['to'] = trim($address);
+	}
 
-  function cc($address)
-  {
-    $this->addresses['cc'][] = trim($address);
-  }
+	function cc($address)
+	{
+		$this->addresses['cc'][] = trim($address);
+	}
 
-  function bcc($address)
-  {
-    $this->addresses['bcc'][] = trim($address);
-  }
+	function bcc($address)
+	{
+		$this->addresses['bcc'][] = trim($address);
+	}
 
-  function replyto($address)
-  {
-    $this->reply_to = trim($address);
-  }
+	function replyto($address)
+	{
+		$this->reply_to = trim($address);
+	}
 
-  function from($address)
-  {
-    $this->from = trim($address);
-  }
+	function from($address)
+	{
+		$this->from = trim($address);
+	}
 
-  // set up subject for mail
-  function set_subject($subject = '')
-  {
-    $this->subject = trim(preg_replace('#[\n\r]+#s', '', $subject));
-  }
+	// set up subject for mail
+	function set_subject($subject = '')
+	{
+		$this->subject = trim(preg_replace('#[\n\r]+#s', '', $subject));
+	}
 
-  // set up extra mail headers
-  function extra_headers($headers)
-  {
-    $this->extra_headers .= trim($headers) . "\n";
-  }
+	// set up extra mail headers
+	function extra_headers($headers)
+	{
+		$this->extra_headers .= trim($headers) . "\n";
+	}
 
-  function use_template($template_code)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $this->tpl_msg = $template->makeParserText($template_code);
+	function use_template($template_code)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$this->tpl_msg = $template->makeParserText($template_code);
 
-    return true;
-  }
+		return true;
+	}
 
-  // assign variables
-  function assign_vars($vars)
-  {
-    if ( is_object($this->tpl_msg) )
-    {
-      $this->tpl_msg->assign_vars($vars);
-    }
-    else
-    {
-      die_friendly(GENERAL_ERROR, 'Can\'t set vars, the template is not set');
-    }
-  }
+	// assign variables
+	function assign_vars($vars)
+	{
+		if ( is_object($this->tpl_msg) )
+		{
+			$this->tpl_msg->assign_vars($vars);
+		}
+		else
+		{
+			die_friendly(GENERAL_ERROR, 'Can\'t set vars, the template is not set');
+		}
+	}
 
-  // Send the mail out to the recipients set previously in var $this->address
-  function send()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $this->msg = $this->tpl_msg->run();
-    if ( empty($this->msg) )
-    {
-      die_friendly(GENERAL_ERROR, 'Template for e-mail message returned a blank');
-    }
+	// Send the mail out to the recipients set previously in var $this->address
+	function send()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$this->msg = $this->tpl_msg->run();
+		if ( empty($this->msg) )
+		{
+			die_friendly(GENERAL_ERROR, 'Template for e-mail message returned a blank');
+		}
 
-    // We now try and pull a subject from the email body ... if it exists,
-    // do this here because the subject may contain a variable
-    $drop_header = '';
-    $match = array();
-    if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match))
-    {
-      $this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : 'No Subject');
-      $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#');
-    }
-    else
-    {
-      $this->subject = (($this->subject != '') ? $this->subject : 'No Subject');
-    }
+		// We now try and pull a subject from the email body ... if it exists,
+		// do this here because the subject may contain a variable
+		$drop_header = '';
+		$match = array();
+		if (preg_match('#^(Subject:(.*?))$#m', $this->msg, $match))
+		{
+			$this->subject = (trim($match[2]) != '') ? trim($match[2]) : (($this->subject != '') ? $this->subject : 'No Subject');
+			$drop_header .= '[\r\n]*?' . preg_quote($match[1], '#');
+		}
+		else
+		{
+			$this->subject = (($this->subject != '') ? $this->subject : 'No Subject');
+		}
 
-    if (preg_match('#^(Charset:(.*?))$#m', $this->msg, $match))
-    {
-      $this->encoding = (trim($match[2]) != '') ? trim($match[2]) : trim('iso-8859-1');
-      $drop_header .= '[\r\n]*?' . preg_quote($match[1], '#');
-    }
-    else
-    {
-      $this->encoding = trim('iso-8859-1');
-    }
+		if (preg_match('#^(Charset:(.*?))$#m', $this->msg, $match))
+		{
+			$this->encoding = (trim($match[2]) != '') ? trim($match[2]) : trim('iso-8859-1');
+			$drop_header .= '[\r\n]*?' . preg_quote($match[1], '#');
+		}
+		else
+		{
+			$this->encoding = trim('iso-8859-1');
+		}
 
-    if ($drop_header != '')
-    {
-      $this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg));
-    }
+		if ($drop_header != '')
+		{
+			$this->msg = trim(preg_replace('#' . $drop_header . '#s', '', $this->msg));
+		}
 
-    $to = $this->addresses['to'];
+		$to = $this->addresses['to'];
 
-    $cc = (count($this->addresses['cc'])) ? implode(', ', $this->addresses['cc']) : '';
-    $bcc = (count($this->addresses['bcc'])) ? implode(', ', $this->addresses['bcc']) : '';
+		$cc = (count($this->addresses['cc'])) ? implode(', ', $this->addresses['cc']) : '';
+		$bcc = (count($this->addresses['bcc'])) ? implode(', ', $this->addresses['bcc']) : '';
 
-    // Build header
-    $this->extra_headers = (($this->reply_to != '') ? "Reply-to: $this->reply_to\n" : '') .
-                           (($this->from != '') ? "From: $this->from\n" : "From: " . getConfig('contact_email') . "\n") .
-                           "Return-Path: " . getConfig('contact_email') .
-                           "\nMessage-ID: <" . md5(uniqid(time())) . "@" . $_SERVER['SERVER_NAME'] . ">\nMIME-Version: 1.0\nContent-type: text/plain; charset=" . $this->encoding .
-                           "\nContent-transfer-encoding: 8bit\nDate: " . enano_date('r', time()) .
-                           "\nX-Priority: 3\nX-MSMail-Priority: Normal\nX-Mailer: PHP\nX-MimeOLE: Produced By Enano CMS\n" .
-                           $this->extra_headers .
-                           (($cc != '') ? "Cc: $cc\n" : '')  .
-                           (($bcc != '') ? "Bcc: $bcc\n" : '');
-    
-    //die('<pre>'.print_r($this,true).'</pre>');
+		// Build header
+		$this->extra_headers = (($this->reply_to != '') ? "Reply-to: $this->reply_to\n" : '') .
+ 													(($this->from != '') ? "From: $this->from\n" : "From: " . getConfig('contact_email') . "\n") .
+ 													"Return-Path: " . getConfig('contact_email') .
+ 													"\nMessage-ID: <" . md5(uniqid(time())) . "@" . $_SERVER['SERVER_NAME'] . ">\nMIME-Version: 1.0\nContent-type: text/plain; charset=" . $this->encoding .
+ 													"\nContent-transfer-encoding: 8bit\nDate: " . enano_date('r', time()) .
+ 													"\nX-Priority: 3\nX-MSMail-Priority: Normal\nX-Mailer: PHP\nX-MimeOLE: Produced By Enano CMS\n" .
+ 													$this->extra_headers .
+ 													(($cc != '') ? "Cc: $cc\n" : '')  .
+ 													(($bcc != '') ? "Bcc: $bcc\n" : '');
+		
+		//die('<pre>'.print_r($this,true).'</pre>');
 
-    // Send message ... removed $this->encode() from subject for time being
-    if ( $this->use_smtp )
-    {
-      $result = smtp_send_email_core($to, $this->subject, $this->msg, $this->extra_headers);
-    }
-    else
-    {
-      $empty_to_header = ($to == '') ? TRUE : FALSE;
-      $to = ($to == '') ? ((getConfig('sendmail_fix')=='1') ? ' ' : 'Undisclosed-recipients:;') : $to;
-  
-      $result = @mail($to, $this->subject, preg_replace("#(?<!\r)\n#s", "\n", $this->msg), $this->extra_headers);
-      
-      if (!$result && !getConfig('sendmail_fix') && $empty_to_header)
-      {
-        $to = ' ';
+		// Send message ... removed $this->encode() from subject for time being
+		if ( $this->use_smtp )
+		{
+			$result = smtp_send_email_core($to, $this->subject, $this->msg, $this->extra_headers);
+		}
+		else
+		{
+			$empty_to_header = ($to == '') ? TRUE : FALSE;
+			$to = ($to == '') ? ((getConfig('sendmail_fix')=='1') ? ' ' : 'Undisclosed-recipients:;') : $to;
+	
+			$result = @mail($to, $this->subject, preg_replace("#(?<!\r)\n#s", "\n", $this->msg), $this->extra_headers);
+			
+			if (!$result && !getConfig('sendmail_fix') && $empty_to_header)
+			{
+				$to = ' ';
 
-        setConfig('sendmail_fix', '1');
-        
-        $result = @mail($to, $this->subject, preg_replace("#(?<!\r)\n#s", "\n", $this->msg), $this->extra_headers);
-      }
-    }
+				setConfig('sendmail_fix', '1');
+				
+				$result = @mail($to, $this->subject, preg_replace("#(?<!\r)\n#s", "\n", $this->msg), $this->extra_headers);
+			}
+		}
 
-    // Did it work?
-    if (!$result || ( $this->use_smtp && $result != 'success' ))
-    {
-      die_friendly(GENERAL_ERROR, 'Failed sending email :: ' . (($this->use_smtp) ? 'SMTP' : 'PHP') . ' :: ' . $result);
-    }
+		// Did it work?
+		if (!$result || ( $this->use_smtp && $result != 'success' ))
+		{
+			die_friendly(GENERAL_ERROR, 'Failed sending email :: ' . (($this->use_smtp) ? 'SMTP' : 'PHP') . ' :: ' . $result);
+		}
 
-    return true;
-  }
+		return true;
+	}
 
-  // Encodes the given string for proper display for this encoding ... nabbed 
-  // from php.net and modified. There is an alternative encoding method which 
-  // may produce lesd output but it's questionable as to its worth in this 
-  // scenario IMO
-  function encode($str)
-  {
-    if ($this->encoding == '')
-    {
-      return $str;
-    }
+	// Encodes the given string for proper display for this encoding ... nabbed 
+	// from php.net and modified. There is an alternative encoding method which 
+	// may produce lesd output but it's questionable as to its worth in this 
+	// scenario IMO
+	function encode($str)
+	{
+		if ($this->encoding == '')
+		{
+			return $str;
+		}
 
-    // define start delimimter, end delimiter and spacer
-    $end = "?=";
-    $start = "=?$this->encoding?B?";
-    $spacer = "$end\r\n $start";
+		// define start delimimter, end delimiter and spacer
+		$end = "?=";
+		$start = "=?$this->encoding?B?";
+		$spacer = "$end\r\n $start";
 
-    // determine length of encoded text within chunks and ensure length is even
-    $length = 75 - strlen($start) - strlen($end);
-    $length = floor($length / 2) * 2;
+		// determine length of encoded text within chunks and ensure length is even
+		$length = 75 - strlen($start) - strlen($end);
+		$length = floor($length / 2) * 2;
 
-    // encode the string and split it into chunks with spacers after each chunk
-    $str = chunk_split(base64_encode($str), $length, $spacer);
+		// encode the string and split it into chunks with spacers after each chunk
+		$str = chunk_split(base64_encode($str), $length, $spacer);
 
-    // remove trailing spacer and add start and end delimiters
-    $str = preg_replace('#' . preg_quote($spacer, '#') . '$#', '', $str);
+		// remove trailing spacer and add start and end delimiters
+		$str = preg_replace('#' . preg_quote($spacer, '#') . '$#', '', $str);
 
-    return $start . $str . $end;
-  }
+		return $start . $str . $end;
+	}
 
-  //
-  // Attach files via MIME.
-  //
-  function attachFile($filename, $mimetype = "application/octet-stream", $szFromAddress, $szFilenameToDisplay)
-  {
-    global $lang;
-    $mime_boundary = "--==================_846811060==_";
+	//
+	// Attach files via MIME.
+	//
+	function attachFile($filename, $mimetype = "application/octet-stream", $szFromAddress, $szFilenameToDisplay)
+	{
+		global $lang;
+		$mime_boundary = "--==================_846811060==_";
 
-    $this->msg = '--' . $mime_boundary . "\nContent-Type: text/plain;\n\tcharset=".'"' . $lang['ENCODING'] . '"'."\n\n" . $this->msg;
+		$this->msg = '--' . $mime_boundary . "\nContent-Type: text/plain;\n\tcharset=".'"' . $lang['ENCODING'] . '"'."\n\n" . $this->msg;
 
-    if ($mime_filename)
-    {
-      $filename = $mime_filename;
-      $encoded = $this->encode_file($filename);
-    }
+		if ($mime_filename)
+		{
+			$filename = $mime_filename;
+			$encoded = $this->encode_file($filename);
+		}
 
-    $fd = fopen($filename, "r");
-    $contents = fread($fd, filesize($filename));
+		$fd = fopen($filename, "r");
+		$contents = fread($fd, filesize($filename));
 
-    $this->mimeOut = "--" . $mime_boundary . "\n";
-    $this->mimeOut .= "Content-Type: " . $mimetype . ";\n\tname=".'"'."$szFilenameToDisplay".'"'."\n";
-    $this->mimeOut .= "Content-Transfer-Encoding: quoted-printable\n";
-    $this->mimeOut .= "Content-Disposition: attachment;\n\tfilename=".'"'."$szFilenameToDisplay".'"'."\n\n";
+		$this->mimeOut = "--" . $mime_boundary . "\n";
+		$this->mimeOut .= "Content-Type: " . $mimetype . ";\n\tname=".'"'."$szFilenameToDisplay".'"'."\n";
+		$this->mimeOut .= "Content-Transfer-Encoding: quoted-printable\n";
+		$this->mimeOut .= "Content-Disposition: attachment;\n\tfilename=".'"'."$szFilenameToDisplay".'"'."\n\n";
 
-    if ( $mimetype == "message/rfc822" )
-    {
-      $this->mimeOut .= "From: ".$szFromAddress."\n";
-      $this->mimeOut .= "To: ".$this->emailAddress."\n";
-      $this->mimeOut .= "Date: ".enano_date('r') . " UT\n";
-      $this->mimeOut .= "Reply-To:".$szFromAddress."\n";
-      $this->mimeOut .= "Subject: ".$this->mailSubject."\n";
-      $this->mimeOut .= "X-Mailer: PHP/".phpversion()."\n";
-      $this->mimeOut .= "MIME-Version: 1.0\n";
-    }
+		if ( $mimetype == "message/rfc822" )
+		{
+			$this->mimeOut .= "From: ".$szFromAddress."\n";
+			$this->mimeOut .= "To: ".$this->emailAddress."\n";
+			$this->mimeOut .= "Date: ".enano_date('r') . " UT\n";
+			$this->mimeOut .= "Reply-To:".$szFromAddress."\n";
+			$this->mimeOut .= "Subject: ".$this->mailSubject."\n";
+			$this->mimeOut .= "X-Mailer: PHP/".phpversion()."\n";
+			$this->mimeOut .= "MIME-Version: 1.0\n";
+		}
 
-    $this->mimeOut .= $contents."\n";
-    $this->mimeOut .= "--" . $mime_boundary . "--" . "\n";
+		$this->mimeOut .= $contents."\n";
+		$this->mimeOut .= "--" . $mime_boundary . "--" . "\n";
 
-    return $out;
-    // added -- to notify email client attachment is done
-  }
+		return $out;
+		// added -- to notify email client attachment is done
+	}
 
-  function getMimeHeaders($filename, $mime_filename="")
-  {
-    $mime_boundary = "--==================_846811060==_";
+	function getMimeHeaders($filename, $mime_filename="")
+	{
+		$mime_boundary = "--==================_846811060==_";
 
-    if ($mime_filename)
-    {
-      $filename = $mime_filename;
-    }
+		if ($mime_filename)
+		{
+			$filename = $mime_filename;
+		}
 
-    $out = "MIME-Version: 1.0\n";
-    $out .= "Content-Type: multipart/mixed;\n\tboundary=".'"'."$mime_boundary".'"'."\n\n";
-    $out .= "This message is in MIME format. Since your mail reader does not understand\n";
-    $out .= "this format, some or all of this message may not be legible.";
+		$out = "MIME-Version: 1.0\n";
+		$out .= "Content-Type: multipart/mixed;\n\tboundary=".'"'."$mime_boundary".'"'."\n\n";
+		$out .= "This message is in MIME format. Since your mail reader does not understand\n";
+		$out .= "this format, some or all of this message may not be legible.";
 
-    return $out;
-  }
+		return $out;
+	}
 
-  //
-   // Split string by RFC 2045 semantics (76 chars per line, end with \r\n).
-  //
-  function myChunkSplit($str)
-  {
-    $stmp = $str;
-    $len = strlen($stmp);
-    $out = "";
+	//
+ 	// Split string by RFC 2045 semantics (76 chars per line, end with \r\n).
+	//
+	function myChunkSplit($str)
+	{
+		$stmp = $str;
+		$len = strlen($stmp);
+		$out = "";
 
-    while ($len > 0)
-    {
-      if ($len >= 76)
-      {
-        $out .= substr($stmp, 0, 76) . "\r\n";
-        $stmp = substr($stmp, 76);
-        $len = $len - 76;
-      }
-      else
-      {
-        $out .= $stmp . "\r\n";
-        $stmp = "";
-        $len = 0;
-      }
-    }
-    return $out;
-  }
+		while ($len > 0)
+		{
+			if ($len >= 76)
+			{
+				$out .= substr($stmp, 0, 76) . "\r\n";
+				$stmp = substr($stmp, 76);
+				$len = $len - 76;
+			}
+			else
+			{
+				$out .= $stmp . "\r\n";
+				$stmp = "";
+				$len = 0;
+			}
+		}
+		return $out;
+	}
 
-  //
-   // Split the specified file up into a string and return it
-  //
-  function encode_file($sourcefile)
-  {
-    if (is_readable(@realpath($sourcefile)))
-    {
-      $fd = fopen($sourcefile, "r");
-      $contents = fread($fd, filesize($sourcefile));
-        $encoded = $this->myChunkSplit(base64_encode($contents));
-        fclose($fd);
-    }
+	//
+ 	// Split the specified file up into a string and return it
+	//
+	function encode_file($sourcefile)
+	{
+		if (is_readable(@realpath($sourcefile)))
+		{
+			$fd = fopen($sourcefile, "r");
+			$contents = fread($fd, filesize($sourcefile));
+				$encoded = $this->myChunkSplit(base64_encode($contents));
+				fclose($fd);
+		}
 
-    return $encoded;
-  }
+		return $encoded;
+	}
 
 } // class emailer
 
@@ -334,293 +334,293 @@
 class EmailEncryptor
 {
  
-  var $primes = Array(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199);
-  
-  function __construct()
-  {
-    $i = 0;
-    $this->p = 0;
-    $this->q = 0;
-    while($this->p * $this->q < 255 || $this->p == $this->q)
-    {
-      $this->p = $this->primes[mt_rand(0, sizeof($this->primes)-1)];
-      $this->q = $this->primes[mt_rand(0, sizeof($this->primes)-1)];
-    }
-  }
-  
-  function testAll() {
-    $size = sizeof($this->primes);
-    
-    $allCharacters = "";
-    for($c = 33; $c <= 126; $c++)
-      $allCharacters = $allCharacters . $this->fromCharCode($c);
-    
-    for($i = 0; $i < $size - 1; $i++) {
-      for($j = $i + 1; $j < $size; $j++) {
-        $this->p = $this->primes[$i];
-        $this->q = $this->primes[$j];
-        if($this->p*$this->q < 255)
-          break;
-        $k = $this->makeKey($allCharacters);
-        $encrypted = $k['X'];
-        $decrypted = $this->goForth($encrypted,$this->p*$this->q,$k['D']);
-        if($decrypted != $allCharacters) {
-          die('Test failed');
-        }
-      }
-    }
-    return 'GOOD';
-  }
-  
-  function charCodeAt($str, $i)
-  {
-    return ord(substr($str, $i, 1));
-  }
-  
-  function fromCharCode($str)
-  {
-    return chr($str);
-  }
-  
-  function MakeArray($l) {
-    $a = Array();
-    $i=0;
-    do {
-      $a[$i]=null;
-      $i++;
-    } while($i < $l);
-    return $a;
-  }
-  
-  function makeKey($addr,$subj = '',$body = '') {
-    $value = "";
-    
-    if($this->p * $this->q < 255)
-    {
-      return("P*Q must be greater than 255! P*Q = " . $this->p*$this->q);
-    }
-    elseif($this->p == $this->q)
-    {
-      return("P cannot be equal to Q!");
-    }
-    elseif($addr == "")
-    {
-      return("You must enter an address to encrypt!");
-    }
-    else
-    {
-      // Make the key
-      $c = 0;
-      $z = ($this->p-1)*($this->q-1);
-      $e = 0;
-      $n = $this->p*$this->q;
-      $d = 0;
-    
-      do {
-        $e++;
-        $d = $this->getKey($this->primes[$e],$z);
-      } while($d==1);
-      $e = $this->primes[$e];
-      
-      // Turn the string into an array of numbers < 255
-      $m = $addr;
-      $emailLength = strlen($m);
-      $justEmail = "";
-      $sep = ( strstr('?', $m) ) ? '&' : '?';
-      if($subj != "") {
-        $m = $m . "{$sep}subject=" . $subj;
-      }
-      $sep = ( strstr($m, '?') ) ? '&' : '?';
-      if($body != "") {
-        $m = $m . "{$sep}body=" . $body;
-      }
-    
-      $length = strlen($m);
-      $theString = $this->MakeArray($length);
-      for($i = 0; $i < $length; $i++) {
-        $theString[$i] = $this->charCodeAt($m, $i);
-      }
-      
-      // Encrypt each of the numbers
-      $theCode = $this->MakeArray($length);
-      $c = "";
-      $temp = 0;
-      for($i = 0; $i < $length; $i++) {
-        if($i != 0)
-          $c .= " ";
-        $temp = $this->myMod($theString[$i],$e,$n);
-        $theCode[$i] = $temp;
-        $c .= $temp;
-        if($i == $emailLength - 1)
-          $justEmail = $c;
-      }
-    }
-    return Array('X'=>$justEmail, 'N'=>$n, 'D'=>$d, 'E'=>$e, 'C'=>$c, 'M'=>$m);
-  }
-    
-  // Finds x^e % y for large values of (x^e)
-  function myMod($x,$e,$y) {
-    if ($e % 2 == 0) {
-      $answer = 1;
-      for($i = 1; $i <= e/2; $i++) {
-        $temp = ($x*$x) % $y;
-        $answer = ($temp*$answer) % $y;
-      }
-    } else {
-      $answer = $x;
-      for($i = 1; $i <= $e/2; $i++) {
-        $temp = ($x*$x) % $y;
-        $answer = ($temp*$answer) % $y;
-      }
-    }
-    return $answer;
-  }
-  
-  
-  function getKey($e,$z) {
-    $A = 1;
-    $B = 0;
-    $C = $z;
-    $F = 0;
-    $G = 1;
-    $bar = $e;    
-    // Euclid's Algorithm:
-    while ($bar != 0) {
-      $foo = floor($C/$bar);
-      $K = $A - $foo * $F;
-      $L = $B - $foo * $G;
-      $M = $C - $foo * $bar;
-      $A = $F;
-      $B = $G;
-      $C = $bar;
-      $F = $K;
-      $G = $L;
-      $bar = $M;
-    }
-    if ($B < 0)
-    {
-      return ($B + $z);
-    }
-    else
-    {
-      return ($B);
-    }
-  }
-  
-  function goForth($c,$n,$d) {
-    $c .= " "; 
-    $length = strlen($c);
-    $number = 0;
-    $bar = 0;
-    $answer = "";
-  
-    for($i = 0; $i < $length; $i++) {
-      $number = 0;
-      $bar = 0;
-      while($this->charCodeAt($c, $i) != 32) { 
-        $number = $number * 10;
-        $number = $number + $this->charCodeAt($c, $i)-48;
-        $i++;
-      }
-      $answer .= $this->fromCharCode($this->decrypt($number,$n,$d));
-    }
-    return $answer;
-  }
-  
-  function decrypt($c,$n,$d) {
-    // Split exponents up
-    if ($d % 2== 0) {
-      $bar = 1;
-      for($i = 1; $i <= $d/2; $i++) {
-        $foo = ($c*$c) % $n;
-        $bar = ($foo*$bar) % $n;
-      }
-    } else {
-      $bar = $c;
-      for($i = 1; $i <= $d/2; $i++) {
-        $foo = ($c*$c) % $n;
-        $bar = ($foo*$bar) % $n;
-      }
-    }
-    return $bar;
-  }
-  
-  function writeOptions() {
-    $size = sizeof($this->primes);
-    for($i = 0; $i < $size; $i++)
-      echo("<option value=".'"'.""+$this->primes[$i]+"".'"'.">"+$this->primes[$i]+"</option>");
-  }
-  
-  function jscode() {
-    return "<script type='text/javascript'>\n// <![CDATA[\nfunction dive(absorption,alchemy,friendship) { absorption += ' '; var file = absorption.length; var sand = 0; var closet = ''; for(var assistant = 0; assistant < file; assistant++) { sand = 0; while(absorption.charCodeAt(assistant) != 32) { sand = sand * 10; sand = sand + absorption.charCodeAt(assistant)-48; assistant++; } closet += String.fromCharCode(say(sand,alchemy,friendship)); } parent.location = 'm'+'a'+'i'+'l'+'t'+'o'+':'+closet; }; function forbid(landing,atmosphere,aviation) { landing += ' '; var kiss = landing.length; var coordinated = 0; for(var day = 0; day < kiss; day++) { coordinated = 0; while(landing.charCodeAt(day) != 32) { coordinated = coordinated * 10; coordinated = coordinated + landing.charCodeAt(day)-48; day++; } document.write(String.fromCharCode(say(coordinated,atmosphere,aviation))); }; }; function say(scene,photograph,fraction) { if (fraction % 2 == 0) { integrity = 1; for(var male = 1; male <= fraction/2; male++) { moon = (scene*scene) % photograph; integrity = (moon*integrity) % photograph; } } else { integrity = scene; for(var night = 1; night <= fraction/2; night++) { moon = (scene*scene) % photograph; integrity = (moon*integrity) % photograph; }; }; return integrity; };\n// ]]>\n</script>";
-  }
-  
-  /**
-   * Wrapper - spits out ready-to-use HTML
-   * @param string $address The e-mail address
-   * @param string $subject The subject of the e-mail. OPTIONAL.
-   * @param string $body The main content of the e-mail. OPTIONAL and doesn't work in many e-mail clients.
-   * @param string $text The text to be shown on the e-mail link. Leave as false to make the e-mail address be shown in the link (but still fully encrypted)
-   */
-  
-  function encryptEmail($address, $subject = '', $body = '', $text = false)
-  {
-    $key = $this->makeKey($address, $subject, $body);
-    if ( $text )
-    {
-      if(preg_match('/^(mailto:)?(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/', $text))
-      {
-        // This is a mailto link and normal obfuscation should be used
-        $text = false;
-      }
-    }
-    $text1 = ( $text ) ? '<script type="text/javascript">document.write(unescape(\''.rawurlencode($text).'\'));</script>' : '<script type=\'text/javascript\'>forbid("'.$key['X'].'",'.$key['N'].','.$key['D'].')</script>';
-    $text2 = ( $text ) ? "$text &lt;".$this->obfuscate_text($this->mask_address($address))."&gt;" : $this->obfuscate_text($this->mask_address($address));
-    $email = '<a href="#" onclick=\'dive("'.$key['C'].'",'.$key['N'].','.$key['D'].'); return false;\' onmouseover="self.status=\'\'; return true;" onmouseout="self.status=\' \'; return true;">'.$text1.'</a><noscript><div style="display: inline">'.$text2.'</div></noscript>';
-    return $email;
-  }
-  
-  /** 
-   * Replace @ symbols with " <AT> " and dots with " <DOT> ".
-   * @param string $email An e-mail address.
-   * @return string
-   */
-   
-  function mask_address($email)
-  {
-    $at = array(' (AT) ', ' __AT__ ', ' *AT* ', ' [AT] ', ' <AT> ', ' <__AT__> ');
-    $dot = array(' (DOT) ', ' __DOT__ ', ' *DOT* ', ' [DOT] ', ' <DOT> ', ' <__DOT__> ');
-    while(strstr($email, '@'))
-    {
-      $my_at = $at[ array_rand($at) ];
-      $email = str_replace_once('@', $my_at, $email);
-    }
-    while(strstr($email, '.'))
-    {
-      $my_dot = $dot[ array_rand($dot) ];
-      $email = str_replace_once('.', $my_dot, $email);
-    }
-    return $email;
-  }
-  
-  /**
-   * Turn a string of text into hex-encoded HTML entities
-   * @param string $text the text to encode
-   * @return string
-   */
+	var $primes = Array(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199);
+	
+	function __construct()
+	{
+		$i = 0;
+		$this->p = 0;
+		$this->q = 0;
+		while($this->p * $this->q < 255 || $this->p == $this->q)
+		{
+			$this->p = $this->primes[mt_rand(0, sizeof($this->primes)-1)];
+			$this->q = $this->primes[mt_rand(0, sizeof($this->primes)-1)];
+		}
+	}
+	
+	function testAll() {
+		$size = sizeof($this->primes);
+		
+		$allCharacters = "";
+		for($c = 33; $c <= 126; $c++)
+			$allCharacters = $allCharacters . $this->fromCharCode($c);
+		
+		for($i = 0; $i < $size - 1; $i++) {
+			for($j = $i + 1; $j < $size; $j++) {
+				$this->p = $this->primes[$i];
+				$this->q = $this->primes[$j];
+				if($this->p*$this->q < 255)
+					break;
+				$k = $this->makeKey($allCharacters);
+				$encrypted = $k['X'];
+				$decrypted = $this->goForth($encrypted,$this->p*$this->q,$k['D']);
+				if($decrypted != $allCharacters) {
+					die('Test failed');
+				}
+			}
+		}
+		return 'GOOD';
+	}
+	
+	function charCodeAt($str, $i)
+	{
+		return ord(substr($str, $i, 1));
+	}
+	
+	function fromCharCode($str)
+	{
+		return chr($str);
+	}
+	
+	function MakeArray($l) {
+		$a = Array();
+		$i=0;
+		do {
+			$a[$i]=null;
+			$i++;
+		} while($i < $l);
+		return $a;
+	}
+	
+	function makeKey($addr,$subj = '',$body = '') {
+		$value = "";
+		
+		if($this->p * $this->q < 255)
+		{
+			return("P*Q must be greater than 255! P*Q = " . $this->p*$this->q);
+		}
+		elseif($this->p == $this->q)
+		{
+			return("P cannot be equal to Q!");
+		}
+		elseif($addr == "")
+		{
+			return("You must enter an address to encrypt!");
+		}
+		else
+		{
+			// Make the key
+			$c = 0;
+			$z = ($this->p-1)*($this->q-1);
+			$e = 0;
+			$n = $this->p*$this->q;
+			$d = 0;
+		
+			do {
+				$e++;
+				$d = $this->getKey($this->primes[$e],$z);
+			} while($d==1);
+			$e = $this->primes[$e];
+			
+			// Turn the string into an array of numbers < 255
+			$m = $addr;
+			$emailLength = strlen($m);
+			$justEmail = "";
+			$sep = ( strstr('?', $m) ) ? '&' : '?';
+			if($subj != "") {
+				$m = $m . "{$sep}subject=" . $subj;
+			}
+			$sep = ( strstr($m, '?') ) ? '&' : '?';
+			if($body != "") {
+				$m = $m . "{$sep}body=" . $body;
+			}
+		
+			$length = strlen($m);
+			$theString = $this->MakeArray($length);
+			for($i = 0; $i < $length; $i++) {
+				$theString[$i] = $this->charCodeAt($m, $i);
+			}
+			
+			// Encrypt each of the numbers
+			$theCode = $this->MakeArray($length);
+			$c = "";
+			$temp = 0;
+			for($i = 0; $i < $length; $i++) {
+				if($i != 0)
+					$c .= " ";
+				$temp = $this->myMod($theString[$i],$e,$n);
+				$theCode[$i] = $temp;
+				$c .= $temp;
+				if($i == $emailLength - 1)
+					$justEmail = $c;
+			}
+		}
+		return Array('X'=>$justEmail, 'N'=>$n, 'D'=>$d, 'E'=>$e, 'C'=>$c, 'M'=>$m);
+	}
+		
+	// Finds x^e % y for large values of (x^e)
+	function myMod($x,$e,$y) {
+		if ($e % 2 == 0) {
+			$answer = 1;
+			for($i = 1; $i <= e/2; $i++) {
+				$temp = ($x*$x) % $y;
+				$answer = ($temp*$answer) % $y;
+			}
+		} else {
+			$answer = $x;
+			for($i = 1; $i <= $e/2; $i++) {
+				$temp = ($x*$x) % $y;
+				$answer = ($temp*$answer) % $y;
+			}
+		}
+		return $answer;
+	}
+	
+	
+	function getKey($e,$z) {
+		$A = 1;
+		$B = 0;
+		$C = $z;
+		$F = 0;
+		$G = 1;
+		$bar = $e;    
+		// Euclid's Algorithm:
+		while ($bar != 0) {
+			$foo = floor($C/$bar);
+			$K = $A - $foo * $F;
+			$L = $B - $foo * $G;
+			$M = $C - $foo * $bar;
+			$A = $F;
+			$B = $G;
+			$C = $bar;
+			$F = $K;
+			$G = $L;
+			$bar = $M;
+		}
+		if ($B < 0)
+		{
+			return ($B + $z);
+		}
+		else
+		{
+			return ($B);
+		}
+	}
+	
+	function goForth($c,$n,$d) {
+		$c .= " "; 
+		$length = strlen($c);
+		$number = 0;
+		$bar = 0;
+		$answer = "";
+	
+		for($i = 0; $i < $length; $i++) {
+			$number = 0;
+			$bar = 0;
+			while($this->charCodeAt($c, $i) != 32) { 
+				$number = $number * 10;
+				$number = $number + $this->charCodeAt($c, $i)-48;
+				$i++;
+			}
+			$answer .= $this->fromCharCode($this->decrypt($number,$n,$d));
+		}
+		return $answer;
+	}
+	
+	function decrypt($c,$n,$d) {
+		// Split exponents up
+		if ($d % 2== 0) {
+			$bar = 1;
+			for($i = 1; $i <= $d/2; $i++) {
+				$foo = ($c*$c) % $n;
+				$bar = ($foo*$bar) % $n;
+			}
+		} else {
+			$bar = $c;
+			for($i = 1; $i <= $d/2; $i++) {
+				$foo = ($c*$c) % $n;
+				$bar = ($foo*$bar) % $n;
+			}
+		}
+		return $bar;
+	}
+	
+	function writeOptions() {
+		$size = sizeof($this->primes);
+		for($i = 0; $i < $size; $i++)
+			echo("<option value=".'"'.""+$this->primes[$i]+"".'"'.">"+$this->primes[$i]+"</option>");
+	}
+	
+	function jscode() {
+		return "<script type='text/javascript'>\n// <![CDATA[\nfunction dive(absorption,alchemy,friendship) { absorption += ' '; var file = absorption.length; var sand = 0; var closet = ''; for(var assistant = 0; assistant < file; assistant++) { sand = 0; while(absorption.charCodeAt(assistant) != 32) { sand = sand * 10; sand = sand + absorption.charCodeAt(assistant)-48; assistant++; } closet += String.fromCharCode(say(sand,alchemy,friendship)); } parent.location = 'm'+'a'+'i'+'l'+'t'+'o'+':'+closet; }; function forbid(landing,atmosphere,aviation) { landing += ' '; var kiss = landing.length; var coordinated = 0; for(var day = 0; day < kiss; day++) { coordinated = 0; while(landing.charCodeAt(day) != 32) { coordinated = coordinated * 10; coordinated = coordinated + landing.charCodeAt(day)-48; day++; } document.write(String.fromCharCode(say(coordinated,atmosphere,aviation))); }; }; function say(scene,photograph,fraction) { if (fraction % 2 == 0) { integrity = 1; for(var male = 1; male <= fraction/2; male++) { moon = (scene*scene) % photograph; integrity = (moon*integrity) % photograph; } } else { integrity = scene; for(var night = 1; night <= fraction/2; night++) { moon = (scene*scene) % photograph; integrity = (moon*integrity) % photograph; }; }; return integrity; };\n// ]]>\n</script>";
+	}
+	
+	/**
+ 	* Wrapper - spits out ready-to-use HTML
+ 	* @param string $address The e-mail address
+ 	* @param string $subject The subject of the e-mail. OPTIONAL.
+ 	* @param string $body The main content of the e-mail. OPTIONAL and doesn't work in many e-mail clients.
+ 	* @param string $text The text to be shown on the e-mail link. Leave as false to make the e-mail address be shown in the link (but still fully encrypted)
+ 	*/
+	
+	function encryptEmail($address, $subject = '', $body = '', $text = false)
+	{
+		$key = $this->makeKey($address, $subject, $body);
+		if ( $text )
+		{
+			if(preg_match('/^(mailto:)?(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/', $text))
+			{
+				// This is a mailto link and normal obfuscation should be used
+				$text = false;
+			}
+		}
+		$text1 = ( $text ) ? '<script type="text/javascript">document.write(unescape(\''.rawurlencode($text).'\'));</script>' : '<script type=\'text/javascript\'>forbid("'.$key['X'].'",'.$key['N'].','.$key['D'].')</script>';
+		$text2 = ( $text ) ? "$text &lt;".$this->obfuscate_text($this->mask_address($address))."&gt;" : $this->obfuscate_text($this->mask_address($address));
+		$email = '<a href="#" onclick=\'dive("'.$key['C'].'",'.$key['N'].','.$key['D'].'); return false;\' onmouseover="self.status=\'\'; return true;" onmouseout="self.status=\' \'; return true;">'.$text1.'</a><noscript><div style="display: inline">'.$text2.'</div></noscript>';
+		return $email;
+	}
+	
+	/** 
+ 	* Replace @ symbols with " <AT> " and dots with " <DOT> ".
+ 	* @param string $email An e-mail address.
+ 	* @return string
+ 	*/
+ 	
+	function mask_address($email)
+	{
+		$at = array(' (AT) ', ' __AT__ ', ' *AT* ', ' [AT] ', ' <AT> ', ' <__AT__> ');
+		$dot = array(' (DOT) ', ' __DOT__ ', ' *DOT* ', ' [DOT] ', ' <DOT> ', ' <__DOT__> ');
+		while(strstr($email, '@'))
+		{
+			$my_at = $at[ array_rand($at) ];
+			$email = str_replace_once('@', $my_at, $email);
+		}
+		while(strstr($email, '.'))
+		{
+			$my_dot = $dot[ array_rand($dot) ];
+			$email = str_replace_once('.', $my_dot, $email);
+		}
+		return $email;
+	}
+	
+	/**
+ 	* Turn a string of text into hex-encoded HTML entities
+ 	* @param string $text the text to encode
+ 	* @return string
+ 	*/
 
-  function obfuscate_text($text)
-  {
-    $a = enano_str_split($text, 1);
-    $s = '';
-    foreach($a as $k => $c)
-    {
-      $ch = (string)dechex(ord($a[$k]));
-      if(strlen($ch) < 2) $ch = '0' . $ch;
-      $s .= '&#x'.$ch.';';
-    }
-    return $s;
-  }
+	function obfuscate_text($text)
+	{
+		$a = enano_str_split($text, 1);
+		$s = '';
+		foreach($a as $k => $c)
+		{
+			$ch = (string)dechex(ord($a[$k]));
+			if(strlen($ch) < 2) $ch = '0' . $ch;
+			$s .= '&#x'.$ch.';';
+		}
+		return $s;
+	}
 
 }
 
--- a/includes/functions.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/functions.php	Sun Mar 28 23:10:46 2010 -0400
@@ -20,15 +20,15 @@
 
 function getConfig($n, $default = false)
 {
-  global $enano_config;
-  if ( isset( $enano_config[ $n ] ) )
-  {
-    return $enano_config[$n];
-  }
-  else
-  {
-    return $default;
-  }
+	global $enano_config;
+	if ( isset( $enano_config[ $n ] ) )
+	{
+		return $enano_config[$n];
+	}
+	else
+	{
+		return $default;
+	}
 }
 
 /**
@@ -40,37 +40,37 @@
 
 function setConfig($n, $v)
 {
-  global $enano_config, $db;
-  
-  if ( isset($enano_config[$n]) )
-  {
-    if ( $enano_config[$n] === $v )
-    {
-      // configuration already matches this value
-      return true;
-    }
-  }
-  
-  $enano_config[$n] = $v;
-  if ( $v === false )
-    unset($enano_config[$n]);
-  
-  $v = $db->escape($v);
-
-  $e = $db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';');
-  if ( !$e )
-  {
-    $db->_die('Error during generic setConfig() call row deletion.');
-  }
-
-  if ( $v !== false )
-  {
-    $e = $db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')');
-    if ( !$e )
-    {
-      $db->_die('Error during generic setConfig() call row insertion.');
-    }
-  }
+	global $enano_config, $db;
+	
+	if ( isset($enano_config[$n]) )
+	{
+		if ( $enano_config[$n] === $v )
+		{
+			// configuration already matches this value
+			return true;
+		}
+	}
+	
+	$enano_config[$n] = $v;
+	if ( $v === false )
+		unset($enano_config[$n]);
+	
+	$v = $db->escape($v);
+
+	$e = $db->sql_query('DELETE FROM '.table_prefix.'config WHERE config_name=\''.$n.'\';');
+	if ( !$e )
+	{
+		$db->_die('Error during generic setConfig() call row deletion.');
+	}
+
+	if ( $v !== false )
+	{
+		$e = $db->sql_query('INSERT INTO '.table_prefix.'config(config_name, config_value) VALUES(\''.$n.'\', \''.$v.'\')');
+		if ( !$e )
+		{
+			$db->_die('Error during generic setConfig() call row insertion.');
+		}
+	}
 }
 
 /**
@@ -83,42 +83,42 @@
 
 if ( !function_exists('makeUrl') )
 {
-  function makeUrl($t, $query = false, $escape = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $flags = '';
-    $sep = urlSeparator;
-    $t = sanitize_page_id($t);
-    if ( isset($_GET['printable'] ) )
-    {
-      $flags .= $sep . 'printable=yes';
-      $sep = '&';
-    }
-    if ( isset($_GET['theme'] ) )
-    {
-      $flags .= $sep . 'theme='.$session->theme;
-      $sep = '&';
-    }
-    if ( isset($_GET['style'] ) )
-    {
-      $flags .= $sep . 'style='.$session->style;
-      $sep = '&';
-    }
-    if ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) )
-    {
-      $flags .= $sep . 'lang=' . urlencode($_GET['lang']);
-      $sep = '&';
-    }
-  
-    $url = is_object($session) ? $session->append_sid(contentPath.$t.$flags) : contentPath . $t . $flags;
-    if($query)
-    {
-      $sep = strstr($url, '?') ? '&' : '?';
-      $url = $url . $sep . $query;
-    }
-  
-    return ($escape) ? htmlspecialchars($url) : $url;
-  }
+	function makeUrl($t, $query = false, $escape = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$flags = '';
+		$sep = urlSeparator;
+		$t = sanitize_page_id($t);
+		if ( isset($_GET['printable'] ) )
+		{
+			$flags .= $sep . 'printable=yes';
+			$sep = '&';
+		}
+		if ( isset($_GET['theme'] ) )
+		{
+			$flags .= $sep . 'theme='.$session->theme;
+			$sep = '&';
+		}
+		if ( isset($_GET['style'] ) )
+		{
+			$flags .= $sep . 'style='.$session->style;
+			$sep = '&';
+		}
+		if ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) )
+		{
+			$flags .= $sep . 'lang=' . urlencode($_GET['lang']);
+			$sep = '&';
+		}
+	
+		$url = is_object($session) ? $session->append_sid(contentPath.$t.$flags) : contentPath . $t . $flags;
+		if($query)
+		{
+			$sep = strstr($url, '?') ? '&' : '?';
+			$url = $url . $sep . $query;
+		}
+	
+		return ($escape) ? htmlspecialchars($url) : $url;
+	}
 }
 
 /**
@@ -132,72 +132,72 @@
 
 if ( !function_exists('makeUrlNS') )
 {
-  function makeUrlNS($n, $t, $query = false, $escape = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $flags = '';
-  
-    if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
-    {
-      $sep = urlSeparator;
-    }
-    else
-    {
-      $sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?';
-    }
-    if ( isset( $_GET['printable'] ) ) {
-      $flags .= $sep . 'printable';
-      $sep = '&';
-    }
-    if ( isset( $_GET['theme'] ) )
-    {
-      $flags .= $sep . 'theme='.$session->theme;
-      $sep = '&';
-    }
-    if ( isset( $_GET['style'] ) )
-    {
-      $flags .= $sep . 'style='.$session->style;
-      $sep = '&';
-    }
-    if ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) )
-    {
-      $flags .= $sep . 'lang=' . urlencode($_GET['lang']);
-      $sep = '&';
-    }
-    
-    $ns_prefix = "$n:";
-  
-    if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
-    {
-      $ns_prefix = ( isset($paths->nslist[$n]) ) ? $paths->nslist[$n] : $n . substr($paths->nslist['Special'], -1);
-      $url = contentPath . $ns_prefix . $t . $flags;
-    }
-    else
-    {
-      // If the path manager hasn't been initted yet, take an educated guess at what the URI should be
-      $url = contentPath . $n . ':' . $t . $flags;
-    }
-  
-    if($query)
-    {
-      if(strstr($url, '?'))
-      {
-        $sep =  '&';
-      }
-      else
-      {
-        $sep = '?';
-      }
-      $url = $url . $sep . $query . $flags;
-    }
-  
-    if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
-    {
-      $url = $session->append_sid($url);
-    }
-  
-    return ($escape) ? htmlspecialchars($url) : $url;
-  }
+	function makeUrlNS($n, $t, $query = false, $escape = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$flags = '';
+	
+		if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
+		{
+			$sep = urlSeparator;
+		}
+		else
+		{
+			$sep = (strstr($_SERVER['REQUEST_URI'], '?')) ? '&' : '?';
+		}
+		if ( isset( $_GET['printable'] ) ) {
+			$flags .= $sep . 'printable';
+			$sep = '&';
+		}
+		if ( isset( $_GET['theme'] ) )
+		{
+			$flags .= $sep . 'theme='.$session->theme;
+			$sep = '&';
+		}
+		if ( isset( $_GET['style'] ) )
+		{
+			$flags .= $sep . 'style='.$session->style;
+			$sep = '&';
+		}
+		if ( isset($_GET['lang']) && preg_match('/^[a-z0-9_]+$/', @$_GET['lang']) )
+		{
+			$flags .= $sep . 'lang=' . urlencode($_GET['lang']);
+			$sep = '&';
+		}
+		
+		$ns_prefix = "$n:";
+	
+		if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
+		{
+			$ns_prefix = ( isset($paths->nslist[$n]) ) ? $paths->nslist[$n] : $n . substr($paths->nslist['Special'], -1);
+			$url = contentPath . $ns_prefix . $t . $flags;
+		}
+		else
+		{
+			// If the path manager hasn't been initted yet, take an educated guess at what the URI should be
+			$url = contentPath . $n . ':' . $t . $flags;
+		}
+	
+		if($query)
+		{
+			if(strstr($url, '?'))
+			{
+				$sep =  '&';
+			}
+			else
+			{
+				$sep = '?';
+			}
+			$url = $url . $sep . $query . $flags;
+		}
+	
+		if(defined('ENANO_BASE_CLASSES_INITIALIZED'))
+		{
+			$url = $session->append_sid($url);
+		}
+	
+		return ($escape) ? htmlspecialchars($url) : $url;
+	}
 }
 
 /**
@@ -211,7 +211,7 @@
 
 function makeUrlComplete($n, $t, $query = false, $escape = false)
 {
-  return get_server_url() . makeUrlNS($n, $t, $query, $escape);
+	return get_server_url() . makeUrlNS($n, $t, $query, $escape);
 }
 
 /**
@@ -221,15 +221,15 @@
 
 function get_server_url()
 {
-  $server_name = false;
-  if ( isset($_SERVER['HTTP_HOST']) )
-  	$server_name = $_SERVER['HTTP_HOST'];
-  else if ( isset($_SERVER['SERVER_NAME']) )
-  	$server_name = $_SERVER['SERVER_NAME'];
-  else
-    $server_name = 'localhost';
-  
-  return 'http' . ( $GLOBALS['is_https'] ) . '://' . $server_name;
+	$server_name = false;
+	if ( isset($_SERVER['HTTP_HOST']) )
+		$server_name = $_SERVER['HTTP_HOST'];
+	else if ( isset($_SERVER['SERVER_NAME']) )
+		$server_name = $_SERVER['SERVER_NAME'];
+	else
+		$server_name = 'localhost';
+	
+	return 'http' . ( $GLOBALS['is_https'] ) . '://' . $server_name;
 }
 
 /**
@@ -239,18 +239,18 @@
 
 function get_main_page($force_logged_in = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  $logged_in = false;
-  if ( is_object($session) && !$force_logged_in )
-  {
-    $logged_in = $session->user_logged_in;
-  }
-  else if ( $force_logged_in )
-  {
-    $logged_in = true;
-  }
-  return $logged_in && getConfig('main_page_alt_enable', '0') == '1' ? getConfig('main_page_alt', getConfig('main_page', 'Main_Page')) : getConfig('main_page', 'Main_Page');
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	$logged_in = false;
+	if ( is_object($session) && !$force_logged_in )
+	{
+		$logged_in = $session->user_logged_in;
+	}
+	else if ( $force_logged_in )
+	{
+		$logged_in = true;
+	}
+	return $logged_in && getConfig('main_page_alt_enable', '0') == '1' ? getConfig('main_page_alt', getConfig('main_page', 'Main_Page')) : getConfig('main_page', 'Main_Page');
 }
 
 /**
@@ -262,44 +262,44 @@
 
 function get_title($sanitize = true, $chop_special = false)
 {
-  $title = '';
-  if ( isset($_GET['title']) )
-  {
-    $title = $_GET['title'];
-  }
-  else if ( isset($_SERVER['PATH_INFO']) )
-  {
-    // fix for apache + CGI (occurred on a GoDaddy server, thanks mm3)
-    if ( @substr(@$_SERVER['GATEWAY_INTERFACE'], 0, 3) === 'CGI' && $_SERVER['PATH_INFO'] == scriptPath . '/index.php' )
-    {
-      // do nothing; ignore PATH_INFO
-    }
-    else
-    {
-      $title = substr($_SERVER['PATH_INFO'], ( strpos($_SERVER['PATH_INFO'], '/') ) + 1 );
-    }
-  }
-  else
-  {
-    // This method really isn't supported because apache has a habit of passing dots as underscores, thus corrupting the request
-    // If you really want to try it, the URI format is yoursite.com/?/Page_title
-    if ( !empty($_SERVER['QUERY_STRING']) && substr($_SERVER['QUERY_STRING'], 0, 1) == '/' )
-    {
-      $pos = ( ($_ = strpos($_SERVER['QUERY_STRING'], '&')) !== false ) ? $_ - 1: 0x7FFFFFFF;
-      $title = substr($_SERVER['QUERY_STRING'], 1, $pos);
-    }
-  }
-  
-  if ( $chop_special )
-  {
-    list(, $ns) = RenderMan::strToPageID($title);
-    if ( $ns == 'Special' || $ns == 'Admin' )
-    {
-      list($title) = explode('/', $title);
-    }
-  }
-  
-  return ( $sanitize ) ? sanitize_page_id($title) : $title;
+	$title = '';
+	if ( isset($_GET['title']) )
+	{
+		$title = $_GET['title'];
+	}
+	else if ( isset($_SERVER['PATH_INFO']) )
+	{
+		// fix for apache + CGI (occurred on a GoDaddy server, thanks mm3)
+		if ( @substr(@$_SERVER['GATEWAY_INTERFACE'], 0, 3) === 'CGI' && $_SERVER['PATH_INFO'] == scriptPath . '/index.php' )
+		{
+			// do nothing; ignore PATH_INFO
+		}
+		else
+		{
+			$title = substr($_SERVER['PATH_INFO'], ( strpos($_SERVER['PATH_INFO'], '/') ) + 1 );
+		}
+	}
+	else
+	{
+		// This method really isn't supported because apache has a habit of passing dots as underscores, thus corrupting the request
+		// If you really want to try it, the URI format is yoursite.com/?/Page_title
+		if ( !empty($_SERVER['QUERY_STRING']) && substr($_SERVER['QUERY_STRING'], 0, 1) == '/' )
+		{
+			$pos = ( ($_ = strpos($_SERVER['QUERY_STRING'], '&')) !== false ) ? $_ - 1: 0x7FFFFFFF;
+			$title = substr($_SERVER['QUERY_STRING'], 1, $pos);
+		}
+	}
+	
+	if ( $chop_special )
+	{
+		list(, $ns) = RenderMan::strToPageID($title);
+		if ( $ns == 'Special' || $ns == 'Admin' )
+		{
+			list($title) = explode('/', $title);
+		}
+	}
+	
+	return ( $sanitize ) ? sanitize_page_id($title) : $title;
 }
 
 /**
@@ -309,7 +309,7 @@
 
 function have_blank_urlname_page()
 {
-  return getConfig('main_page', 'Main_Page') == '' || getConfig('main_page', getConfig('main_page', 'Main_Page')) == '';
+	return getConfig('main_page', 'Main_Page') == '' || getConfig('main_page', getConfig('main_page', 'Main_Page')) == '';
 }
 
 /**
@@ -321,78 +321,78 @@
 
 function enano_date($string, $timestamp = false)
 {
-  if ( !is_int($timestamp) && !is_double($timestamp) && strval(intval($timestamp)) !== $timestamp )
-    $timestamp = time();
-  
-  if ( is_int($string) )
-  {
-    global $session, $lang;
-    $date_fmt = is_object($session) ? $session->date_format : DATE_4;
-    $time_fmt = is_object($session) ? $session->time_format : TIME_24_NS;
-    
-    // within a week? use a relative date
-    if ( $timestamp + ( 86400 * 7 ) >= time() && $string & ED_DATE && is_object($lang) && is_object($session) && !($string & ED_DATE_FULL) )
-    {
-      $relative_date = get_relative_date($timestamp);
-      if ( $string === ED_DATE )
-        // why do more work if we're done?
-        return $relative_date;
-    }
-    
-    $flags = $string;
-    $string = array();
-    if ( $flags & ED_DATE && !isset($relative_date) )
-      $string[] = $date_fmt;
-    if ( $flags & ED_TIME )
-      $string[] = $time_fmt;
-    
-    $string = implode(' ', $string);
-  }
-  
-  // perform timestamp offset
-  global $timezone;
-  // it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
-  $timestamp = $timestamp + ( $timezone * 60 );
-  
-  // are we in DST?
-  global $dst_params;
-  $dst_offset = 0;
-  if ( check_timestamp_dst($timestamp, $dst_params[0], $dst_params[1], $dst_params[2], $dst_params[3]) )
-  {
-    // offset for DST
-    $timestamp += ( $dst_params[4] * 60 );
-    $dst_offset = $dst_params[4];
-  }
-  
-  // Does this date string include a timezone? If so, gmdate() will report UTC, which is wrong
-  // FIXME This is kind of a halfass replacement...
-  foreach ( array('e', 'T', 'O', 'P') as $char )
-  {
-    if ( ($pos = strpos($string, $char)) !== false )
-    {
-      if ( $string{ $pos - 1 } != '\\' )
-      {
-        // add in our own timezone string
-        // FIXME: l10n? (do we need to? does anyone really not know what "GMT" means? even uglier escaping?)
-        $tzi = '\\G\\M\\T';
-        $tzo = $timezone + $dst_offset;
-        $sign = $tzo > 0 ? '+' : '-';
-        $tzi .= $sign . (intval(abs($tzo / 60)));
-        if ( $tzo % 60 )
-          $tzi .= sprintf(":%02d", abs($tzo) % 60);
-        
-        $string = substr($string, 0, $pos) . $tzi . substr($string, $pos + 1);
-      }
-    }
-  }
-  
-  // Let PHP do the work for us =)
-  $result = gmdate($string, $timestamp);
-  if ( isset($relative_date) )
-  {
-    $result = "$relative_date, $result";
-  }
-  return $result;
+	if ( !is_int($timestamp) && !is_double($timestamp) && strval(intval($timestamp)) !== $timestamp )
+		$timestamp = time();
+	
+	if ( is_int($string) )
+	{
+		global $session, $lang;
+		$date_fmt = is_object($session) ? $session->date_format : DATE_4;
+		$time_fmt = is_object($session) ? $session->time_format : TIME_24_NS;
+		
+		// within a week? use a relative date
+		if ( $timestamp + ( 86400 * 7 ) >= time() && $string & ED_DATE && is_object($lang) && is_object($session) && !($string & ED_DATE_FULL) )
+		{
+			$relative_date = get_relative_date($timestamp);
+			if ( $string === ED_DATE )
+				// why do more work if we're done?
+				return $relative_date;
+		}
+		
+		$flags = $string;
+		$string = array();
+		if ( $flags & ED_DATE && !isset($relative_date) )
+			$string[] = $date_fmt;
+		if ( $flags & ED_TIME )
+			$string[] = $time_fmt;
+		
+		$string = implode(' ', $string);
+	}
+	
+	// perform timestamp offset
+	global $timezone;
+	// it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
+	$timestamp = $timestamp + ( $timezone * 60 );
+	
+	// are we in DST?
+	global $dst_params;
+	$dst_offset = 0;
+	if ( check_timestamp_dst($timestamp, $dst_params[0], $dst_params[1], $dst_params[2], $dst_params[3]) )
+	{
+		// offset for DST
+		$timestamp += ( $dst_params[4] * 60 );
+		$dst_offset = $dst_params[4];
+	}
+	
+	// Does this date string include a timezone? If so, gmdate() will report UTC, which is wrong
+	// FIXME This is kind of a halfass replacement...
+	foreach ( array('e', 'T', 'O', 'P') as $char )
+	{
+		if ( ($pos = strpos($string, $char)) !== false )
+		{
+			if ( $string{ $pos - 1 } != '\\' )
+			{
+				// add in our own timezone string
+				// FIXME: l10n? (do we need to? does anyone really not know what "GMT" means? even uglier escaping?)
+				$tzi = '\\G\\M\\T';
+				$tzo = $timezone + $dst_offset;
+				$sign = $tzo > 0 ? '+' : '-';
+				$tzi .= $sign . (intval(abs($tzo / 60)));
+				if ( $tzo % 60 )
+					$tzi .= sprintf(":%02d", abs($tzo) % 60);
+				
+				$string = substr($string, 0, $pos) . $tzi . substr($string, $pos + 1);
+			}
+		}
+	}
+	
+	// Let PHP do the work for us =)
+	$result = gmdate($string, $timestamp);
+	if ( isset($relative_date) )
+	{
+		$result = "$relative_date, $result";
+	}
+	return $result;
 }
 
 /**
@@ -403,47 +403,47 @@
 
 function get_relative_date($time)
 {
-  global $lang, $session;
-  // Our formatting string to pass to enano_date()
-  // This should not include minute/second info, only today's date in whatever format suits your fancy
-  $formatstring = $session->date_format;
-  // Today's date
-  $today = enano_date($formatstring);
-  // Yesterday's date
-  $yesterday = enano_date($formatstring, (time() - (24*60*60)));
-  // Date on the input
-  $then = enano_date($formatstring, $time);
-  // "X days ago" logic
-  for ( $i = 2; $i <= 6; $i++ )
-  {
-    // hours_in_day * minutes_in_hour * seconds_in_minute * num_days
-    $offset = 24 * 60 * 60 * $i;
-    $days_ago = enano_date($formatstring, (time() - $offset));
-    // so does the input timestamp match the date from $i days ago?
-    if ( $then == $days_ago )
-    {
-      // yes, return $i
-      return $lang->get('userfuncs_ml_date_daysago', array('days_ago' => $i));
-    }
-  }
-  // either yesterday, today, or before 6 days ago
-  switch($then)
-  {
-    case $today:
-      return $lang->get('userfuncs_ml_date_today');
-    case $yesterday:
-      return $lang->get('userfuncs_ml_date_yesterday');
-    default:
-      return $then;
-  }
-  //     .--.
-  //    |o_o |
-  //    |!_/ |
-  //   //   \ \
-  //  (|     | )
-  // /'\_   _/`\
-  // \___)=(___/
-  return 'Linux rocks!';
+	global $lang, $session;
+	// Our formatting string to pass to enano_date()
+	// This should not include minute/second info, only today's date in whatever format suits your fancy
+	$formatstring = $session->date_format;
+	// Today's date
+	$today = enano_date($formatstring);
+	// Yesterday's date
+	$yesterday = enano_date($formatstring, (time() - (24*60*60)));
+	// Date on the input
+	$then = enano_date($formatstring, $time);
+	// "X days ago" logic
+	for ( $i = 2; $i <= 6; $i++ )
+	{
+		// hours_in_day * minutes_in_hour * seconds_in_minute * num_days
+		$offset = 24 * 60 * 60 * $i;
+		$days_ago = enano_date($formatstring, (time() - $offset));
+		// so does the input timestamp match the date from $i days ago?
+		if ( $then == $days_ago )
+		{
+			// yes, return $i
+			return $lang->get('userfuncs_ml_date_daysago', array('days_ago' => $i));
+		}
+	}
+	// either yesterday, today, or before 6 days ago
+	switch($then)
+	{
+		case $today:
+			return $lang->get('userfuncs_ml_date_today');
+		case $yesterday:
+			return $lang->get('userfuncs_ml_date_yesterday');
+		default:
+			return $then;
+	}
+	//     .--.
+	//    |o_o |
+	//    |!_/ |
+	//   //   \ \
+	//  (|     | )
+	// /'\_   _/`\
+	// \___)=(___/
+	return 'Linux rocks!';
 }
 
 /**
@@ -458,31 +458,31 @@
 
 function check_timestamp_dst($time, $start_month, $start_sunday, $end_month, $end_sunday)
 {
-  static $sundays = array(FIRST_SUNDAY, SECOND_SUNDAY, THIRD_SUNDAY, LAST_SUNDAY);
-  
-  // perform timestamp offset
-  global $timezone;
-  // it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
-  $time = $time + ( $timezone * 60 );
-  $year = intval(gmdate('Y', $time));
-  
-  // one-pass validation
-  if ( !in_array($start_sunday, $sundays) || !in_array($end_sunday, $sundays) ||
-       $start_month < 1 || $start_month > 12 || $end_month < 1 || $end_month > 12 )
-    return false;
-    
-  // get timestamp of the selected sunday (start)
-  $dst_start = get_sunday_timestamp($start_month, $start_sunday, $year);
-  $dst_end   = get_sunday_timestamp($end_month, $end_sunday, $year);
-  
-  if ( $dst_start > $dst_end )
-  {
-    // start time is past the end time, this means we're in the southern hemisphere
-    // as a result, if we're within the range, DST is NOT in progress.
-    return !( $time >= $dst_start && $time <= $dst_end );
-  }
-  
-  return $time >= $dst_start && $time <= $dst_end;
+	static $sundays = array(FIRST_SUNDAY, SECOND_SUNDAY, THIRD_SUNDAY, LAST_SUNDAY);
+	
+	// perform timestamp offset
+	global $timezone;
+	// it's gonna be in minutes, so multiply by 60 to offset the unix timestamp
+	$time = $time + ( $timezone * 60 );
+	$year = intval(gmdate('Y', $time));
+	
+	// one-pass validation
+	if ( !in_array($start_sunday, $sundays) || !in_array($end_sunday, $sundays) ||
+ 			$start_month < 1 || $start_month > 12 || $end_month < 1 || $end_month > 12 )
+		return false;
+		
+	// get timestamp of the selected sunday (start)
+	$dst_start = get_sunday_timestamp($start_month, $start_sunday, $year);
+	$dst_end   = get_sunday_timestamp($end_month, $end_sunday, $year);
+	
+	if ( $dst_start > $dst_end )
+	{
+		// start time is past the end time, this means we're in the southern hemisphere
+		// as a result, if we're within the range, DST is NOT in progress.
+		return !( $time >= $dst_start && $time <= $dst_end );
+	}
+	
+	return $time >= $dst_start && $time <= $dst_end;
 }
 
 /**
@@ -495,48 +495,48 @@
 
 function get_sunday_timestamp($month, $sunday, $year)
 {
-  $days_in_month = array(
-    1 => 31,
-    2 => $year % 4 == 0 && ( $year % 100 != 0 || ( $year % 100 == 0 && $year % 400 == 0 ) ) ? 29 : 28,
-    3 => 31,
-    4 => 30,
-    5 => 31,
-    6 => 30,
-    7 => 31,
-    8 => 31,
-    9 => 30,
-    10 => 31,
-    11 => 30,
-    12 => 31
-  );
-  
-  $result = mktime(0, 0, 0, $month, 1, $year);
-  
-  // hack. allows a specific day of the month to be set instead of a sunday. not a good place to do this.
-  if ( is_string($sunday) && substr($sunday, -1) === 'd' )
-  {
-    $result += 86400 * ( intval($sunday) - 1);
-    return $result;
-  }
-  
-  $tick = 0;
-  $days_remaining = $days_in_month[$month];
-  while ( true )
-  {
-    if ( date('D', $result) == 'Sun' )
-    {
-      $tick++;
-      if ( ( $tick == 1 && $sunday == FIRST_SUNDAY ) ||
-           ( $tick == 2 && $sunday == SECOND_SUNDAY ) ||
-           ( $tick == 3 && $sunday == THIRD_SUNDAY ) ||
-           ( $sunday == LAST_SUNDAY && $days_remaining < 7 ) )
-        break;
-    }
-    $days_remaining--;
-    $result += 86400;
-  }
-  
-  return $result;
+	$days_in_month = array(
+		1 => 31,
+		2 => $year % 4 == 0 && ( $year % 100 != 0 || ( $year % 100 == 0 && $year % 400 == 0 ) ) ? 29 : 28,
+		3 => 31,
+		4 => 30,
+		5 => 31,
+		6 => 30,
+		7 => 31,
+		8 => 31,
+		9 => 30,
+		10 => 31,
+		11 => 30,
+		12 => 31
+	);
+	
+	$result = mktime(0, 0, 0, $month, 1, $year);
+	
+	// hack. allows a specific day of the month to be set instead of a sunday. not a good place to do this.
+	if ( is_string($sunday) && substr($sunday, -1) === 'd' )
+	{
+		$result += 86400 * ( intval($sunday) - 1);
+		return $result;
+	}
+	
+	$tick = 0;
+	$days_remaining = $days_in_month[$month];
+	while ( true )
+	{
+		if ( date('D', $result) == 'Sun' )
+		{
+			$tick++;
+			if ( ( $tick == 1 && $sunday == FIRST_SUNDAY ) ||
+ 					( $tick == 2 && $sunday == SECOND_SUNDAY ) ||
+ 					( $tick == 3 && $sunday == THIRD_SUNDAY ) ||
+ 					( $sunday == LAST_SUNDAY && $days_remaining < 7 ) )
+				break;
+		}
+		$days_remaining--;
+		$result += 86400;
+	}
+	
+	return $result;
 }
 
 /**
@@ -548,10 +548,10 @@
 
 function get_page_title($page_id, $show_ns = true)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-
-  $idata = RenderMan::strToPageID($page_id);
-  return get_page_title_ns($idata[0], $idata[1]);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+
+	$idata = RenderMan::strToPageID($page_id);
+	return get_page_title_ns($idata[0], $idata[1]);
 }
 
 /**
@@ -563,10 +563,10 @@
 
 function get_page_title_ns($page_id, $namespace)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  $ns = namespace_factory($page_id, $namespace);
-  return $ns->title;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	$ns = namespace_factory($page_id, $namespace);
+	return $ns->title;
 }
 
 /**
@@ -579,59 +579,59 @@
 
 function redirect($url, $title = 'etc_redirect_title', $message = 'etc_redirect_body', $timeout = 3)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-
-  // POST check added in 1.1.x because Firefox 3.0 asks us if we want to "resend the form
-  // data to the new location", which can be confusing for some users.
-  $is_firefox_3 = ( strstr(@$_SERVER['HTTP_USER_AGENT'], 'Firefox/3.') ) ? true : false;
-  if ( $timeout == 0 && ( empty($_POST) || !$is_firefox_3 ) )
-  {
-    header('Location: ' . $url);
-    header('Content-length: 0');
-    header('HTTP/1.1 307 Temporary Redirect');
-    
-    // with 3xx codes HTTP clients expect a response of 0 bytes, so just die here
-    exit();
-  }
-  
-  if ( !is_object($template) )
-  {
-    $template = new template_nodb();
-    $template->load_theme('oxygen', 'bleu', false);
-    $template->assign_vars(array(
-        'SITE_NAME' => 'Enano',
-        'SITE_DESC' => 'This site is experiencing a critical error and cannot load.',
-        'COPYRIGHT' => 'Powered by Enano CMS - &copy; 2006-2008 Dan Fuhry. This program is Free Software; see the <a href="' . scriptPath . '/install.php?mode=license">GPL file</a> included with this package for details.',
-        'PAGE_NAME' => htmlspecialchars($title)
-      ));
-  }
-
-  $template->add_header('<meta http-equiv="refresh" content="' . $timeout . '; url=' . str_replace('"', '\\"', $url) . '" />');
-  $template->add_header('<script type="text/javascript">
-      function __r() {
-        // FUNCTION AUTOMATICALLY GENERATED
-        window.location="' . str_replace('"', '\\"', $url) . '";
-      }
-      setTimeout(\'__r();\', ' . $timeout . '000);
-    </script>
-    ');
-  
-  if ( get_class($template) == 'template_nodb' )
-    $template->init_vars();
-
-  $template->assign_vars(array('PAGE_NAME' => $title));
-  $template->header(true);
-  echo '<p>' . $message . '</p>';
-  $subst = array(
-      'timeout' => $timeout,
-      'redirect_url' => str_replace('"', '\\"', $url)
-    );
-  echo '<p>' . $lang->get('etc_redirect_timeout', $subst) . '</p>';
-  $template->footer(true);
-
-  $db->close();
-  exit(0);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+
+	// POST check added in 1.1.x because Firefox 3.0 asks us if we want to "resend the form
+	// data to the new location", which can be confusing for some users.
+	$is_firefox_3 = ( strstr(@$_SERVER['HTTP_USER_AGENT'], 'Firefox/3.') ) ? true : false;
+	if ( $timeout == 0 && ( empty($_POST) || !$is_firefox_3 ) )
+	{
+		header('Location: ' . $url);
+		header('Content-length: 0');
+		header('HTTP/1.1 307 Temporary Redirect');
+		
+		// with 3xx codes HTTP clients expect a response of 0 bytes, so just die here
+		exit();
+	}
+	
+	if ( !is_object($template) )
+	{
+		$template = new template_nodb();
+		$template->load_theme('oxygen', 'bleu', false);
+		$template->assign_vars(array(
+				'SITE_NAME' => 'Enano',
+				'SITE_DESC' => 'This site is experiencing a critical error and cannot load.',
+				'COPYRIGHT' => 'Powered by Enano CMS - &copy; 2006-2008 Dan Fuhry. This program is Free Software; see the <a href="' . scriptPath . '/install.php?mode=license">GPL file</a> included with this package for details.',
+				'PAGE_NAME' => htmlspecialchars($title)
+			));
+	}
+
+	$template->add_header('<meta http-equiv="refresh" content="' . $timeout . '; url=' . str_replace('"', '\\"', $url) . '" />');
+	$template->add_header('<script type="text/javascript">
+			function __r() {
+				// FUNCTION AUTOMATICALLY GENERATED
+				window.location="' . str_replace('"', '\\"', $url) . '";
+			}
+			setTimeout(\'__r();\', ' . $timeout . '000);
+		</script>
+		');
+	
+	if ( get_class($template) == 'template_nodb' )
+		$template->init_vars();
+
+	$template->assign_vars(array('PAGE_NAME' => $title));
+	$template->header(true);
+	echo '<p>' . $message . '</p>';
+	$subst = array(
+			'timeout' => $timeout,
+			'redirect_url' => str_replace('"', '\\"', $url)
+		);
+	echo '<p>' . $lang->get('etc_redirect_timeout', $subst) . '</p>';
+	$template->footer(true);
+
+	$db->close();
+	exit(0);
 
 }
 
@@ -641,85 +641,85 @@
 
 function csrf_request_confirm()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang, $output;
-  
-  // If the token was overridden with the correct one, the user confirmed the action using this form. Continue exec.
-  if ( isset($_POST['cstok']) || isset($_GET['cstok']) )
-  {
-    // using the if() check makes sure that the token isn't in a cookie, since $_REQUEST includes $_COOKIE.
-    $token_check =& $_REQUEST['cstok'];
-    if ( $token_check === $session->csrf_token )
-    {
-      // overridden token matches, continue exec
-      return true;
-    }
-  }
-  
-  @ob_end_clean();
-  
-  $output->set_title($lang->get('user_csrf_confirm_title'));
-  $output->header();
-  
-  // initial info
-  echo '<p>' . $lang->get('user_csrf_confirm_body') . '</p>';
-  
-  // start form
-  $form_method = ( empty($_POST) ) ? 'get' : 'post';
-  echo '<form action="' . htmlspecialchars($_SERVER['REQUEST_URI']) . '" method="' . $form_method . '" enctype="multipart/form-data">';
-  
-  echo '<fieldset enano:expand="closed">';
-  echo '<legend>' . $lang->get('user_csrf_confirm_btn_viewrequest') . '</legend><div>';
-  
-  if ( empty($_POST) )
-  {
-    // GET request
-    echo csrf_confirm_get_recursive();
-  }
-  else
-  {
-    // POST request
-    echo csrf_confirm_post_recursive();
-  }
-  echo '</div></fieldset>';
-  // insert the right CSRF token
-  echo '<input type="hidden" name="cstok" value="' . $session->csrf_token . '" />';
-  echo '<p><input type="submit" value="' . $lang->get('user_csrf_confirm_btn_continue') . '" /></p>';
-  echo '</form><script type="text/javascript">addOnloadHook(function(){load_component(\'expander\');});</script>';
-  
-  $output->footer();
-  
-  exit;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang, $output;
+	
+	// If the token was overridden with the correct one, the user confirmed the action using this form. Continue exec.
+	if ( isset($_POST['cstok']) || isset($_GET['cstok']) )
+	{
+		// using the if() check makes sure that the token isn't in a cookie, since $_REQUEST includes $_COOKIE.
+		$token_check =& $_REQUEST['cstok'];
+		if ( $token_check === $session->csrf_token )
+		{
+			// overridden token matches, continue exec
+			return true;
+		}
+	}
+	
+	@ob_end_clean();
+	
+	$output->set_title($lang->get('user_csrf_confirm_title'));
+	$output->header();
+	
+	// initial info
+	echo '<p>' . $lang->get('user_csrf_confirm_body') . '</p>';
+	
+	// start form
+	$form_method = ( empty($_POST) ) ? 'get' : 'post';
+	echo '<form action="' . htmlspecialchars($_SERVER['REQUEST_URI']) . '" method="' . $form_method . '" enctype="multipart/form-data">';
+	
+	echo '<fieldset enano:expand="closed">';
+	echo '<legend>' . $lang->get('user_csrf_confirm_btn_viewrequest') . '</legend><div>';
+	
+	if ( empty($_POST) )
+	{
+		// GET request
+		echo csrf_confirm_get_recursive();
+	}
+	else
+	{
+		// POST request
+		echo csrf_confirm_post_recursive();
+	}
+	echo '</div></fieldset>';
+	// insert the right CSRF token
+	echo '<input type="hidden" name="cstok" value="' . $session->csrf_token . '" />';
+	echo '<p><input type="submit" value="' . $lang->get('user_csrf_confirm_btn_continue') . '" /></p>';
+	echo '</form><script type="text/javascript">addOnloadHook(function(){load_component(\'expander\');});</script>';
+	
+	$output->footer();
+	
+	exit;
 }
 
 function csrf_confirm_get_recursive($_inner = false, $pfx = false, $data = false)
 {
-  // make posted arrays work right
-  if ( !$data )
-    ( $_inner == 'post' ) ? $data =& $_POST : $data =& $_GET;
-  foreach ( $data as $key => $value )
-  {
-    $pfx_this = ( empty($pfx) ) ? $key : "{$pfx}[{$key}]";
-    if ( is_array($value) )
-    {
-      csrf_confirm_get_recursive(true, $pfx_this, $value);
-    }
-    else if ( empty($value) )
-    {
-      echo htmlspecialchars($pfx_this . " = <nil>") . "<br />\n";
-      echo '<input type="hidden" name="' . htmlspecialchars($pfx_this) . '" value="" />';
-    }
-    else
-    {
-      echo htmlspecialchars($pfx_this . " = " . $value) . "<br />\n";
-      echo '<input type="hidden" name="' . htmlspecialchars($pfx_this) . '" value="' . htmlspecialchars($value) . '" />';
-    }
-  }
+	// make posted arrays work right
+	if ( !$data )
+		( $_inner == 'post' ) ? $data =& $_POST : $data =& $_GET;
+	foreach ( $data as $key => $value )
+	{
+		$pfx_this = ( empty($pfx) ) ? $key : "{$pfx}[{$key}]";
+		if ( is_array($value) )
+		{
+			csrf_confirm_get_recursive(true, $pfx_this, $value);
+		}
+		else if ( empty($value) )
+		{
+			echo htmlspecialchars($pfx_this . " = <nil>") . "<br />\n";
+			echo '<input type="hidden" name="' . htmlspecialchars($pfx_this) . '" value="" />';
+		}
+		else
+		{
+			echo htmlspecialchars($pfx_this . " = " . $value) . "<br />\n";
+			echo '<input type="hidden" name="' . htmlspecialchars($pfx_this) . '" value="' . htmlspecialchars($value) . '" />';
+		}
+	}
 }
 
 function csrf_confirm_post_recursive()
 {
-  csrf_confirm_get_recursive('post');
+	csrf_confirm_get_recursive('post');
 }
 
 // Removed wikiFormat() from here, replaced with RenderMan::render
@@ -732,22 +732,22 @@
 
 function isPage($p)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  static $ispage_cache = array();
-  if ( isset($ispage_cache[$p]) )
-    return $ispage_cache[$p];
-  
-  list($page_id, $namespace) = RenderMan::strToPageID($p);
-  $cdata = $paths->get_cdata($page_id, $namespace);
-  if ( !isset($cdata['page_exists']) )
-  {
-    $class = ( class_exists($_ = "Namespace_$namespace") ) ? $_ : "Namespace_Default";
-    $page = new $class($page_id, $namespace);
-    return $page->exists();
-  }
-  
-  $ispage_cache[$p] = $cdata['page_exists'];
-  return $cdata['page_exists'];
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	static $ispage_cache = array();
+	if ( isset($ispage_cache[$p]) )
+		return $ispage_cache[$p];
+	
+	list($page_id, $namespace) = RenderMan::strToPageID($p);
+	$cdata = $paths->get_cdata($page_id, $namespace);
+	if ( !isset($cdata['page_exists']) )
+	{
+		$class = ( class_exists($_ = "Namespace_$namespace") ) ? $_ : "Namespace_Default";
+		$page = new $class($page_id, $namespace);
+		return $page->exists();
+	}
+	
+	$ispage_cache[$p] = $cdata['page_exists'];
+	return $cdata['page_exists'];
 }
 
 /**
@@ -759,33 +759,33 @@
 
 function namespace_factory($page_id, $namespace, $revision_id = 0)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  static $objcache = array();
-  $pathskey = $paths->get_pathskey($page_id, $namespace) . ":$revision_id";
-  if ( isset($objcache[$pathskey]) )
-    return $objcache[$pathskey];
-  
-  if ( !class_exists("Namespace_$namespace") )
-  {
-    if ( file_exists(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php") )
-    {
-      require(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php");
-    }
-  }
-  if ( class_exists("Namespace_$namespace") )
-  {
-    $class = "Namespace_$namespace";
-    $ns = new $class($page_id, $namespace, $revision_id);
-    $objcache[$pathskey] = $ns;
-    return $ns;
-  }
-  else
-  {
-    $ns = new Namespace_Default($page_id, $namespace, $revision_id);
-    $objcache[$pathskey] = $ns;
-    return $ns;
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	static $objcache = array();
+	$pathskey = $paths->get_pathskey($page_id, $namespace) . ":$revision_id";
+	if ( isset($objcache[$pathskey]) )
+		return $objcache[$pathskey];
+	
+	if ( !class_exists("Namespace_$namespace") )
+	{
+		if ( file_exists(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php") )
+		{
+			require(ENANO_ROOT . "/includes/namespaces/" . strtolower($namespace) . ".php");
+		}
+	}
+	if ( class_exists("Namespace_$namespace") )
+	{
+		$class = "Namespace_$namespace";
+		$ns = new $class($page_id, $namespace, $revision_id);
+		$objcache[$pathskey] = $ns;
+		return $ns;
+	}
+	else
+	{
+		$ns = new Namespace_Default($page_id, $namespace, $revision_id);
+		$objcache[$pathskey] = $ns;
+		return $ns;
+	}
 }
 
 /**
@@ -793,52 +793,52 @@
  */
 
 function arrayItemUp($arr, $keyname) {
-  $keylist = array_keys($arr);
-  $keyflop = array_flip($keylist);
-  $idx = $keyflop[$keyname];
-  $idxm = $idx - 1;
-  $temp = $arr[$keylist[$idxm]];
-  if($arr[$keylist[0]] == $arr[$keyname]) return $arr;
-  $arr[$keylist[$idxm]] = $arr[$keylist[$idx]];
-  $arr[$keylist[$idx]] = $temp;
-  return $arr;
+	$keylist = array_keys($arr);
+	$keyflop = array_flip($keylist);
+	$idx = $keyflop[$keyname];
+	$idxm = $idx - 1;
+	$temp = $arr[$keylist[$idxm]];
+	if($arr[$keylist[0]] == $arr[$keyname]) return $arr;
+	$arr[$keylist[$idxm]] = $arr[$keylist[$idx]];
+	$arr[$keylist[$idx]] = $temp;
+	return $arr;
 }
 
 function arrayItemDown($arr, $keyname) {
-  $keylist = array_keys($arr);
-  $keyflop = array_flip($keylist);
-  $idx = $keyflop[$keyname];
-  $idxm = $idx + 1;
-  $temp = $arr[$keylist[$idxm]];
-  $sz = sizeof($arr); $sz--;
-  if($arr[$keylist[$sz]] == $arr[$keyname]) return $arr;
-  $arr[$keylist[$idxm]]  =  $arr[$keylist[$idx]];
-  $arr[$keylist[$idx]]   =  $temp;
-  return $arr;
+	$keylist = array_keys($arr);
+	$keyflop = array_flip($keylist);
+	$idx = $keyflop[$keyname];
+	$idxm = $idx + 1;
+	$temp = $arr[$keylist[$idxm]];
+	$sz = sizeof($arr); $sz--;
+	if($arr[$keylist[$sz]] == $arr[$keyname]) return $arr;
+	$arr[$keylist[$idxm]]  =  $arr[$keylist[$idx]];
+	$arr[$keylist[$idx]]   =  $temp;
+	return $arr;
 }
 
 function arrayItemTop($arr, $keyname) {
-  $keylist = array_keys($arr);
-  $keyflop = array_flip($keylist);
-  $idx = $keyflop[$keyname];
-  while( $orig != $arr[$keylist[0]] ) {
-    // echo 'Keyname: '.$keylist[$idx] . '<br />'; flush(); ob_flush(); // Debugger
-    if($idx < 0) return $arr;
-    if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) {
-      return $arr;
-    }
-    $arr = arrayItemUp($arr, $keylist[$idx]);
-    $idx--;
-  }
-  return $arr;
+	$keylist = array_keys($arr);
+	$keyflop = array_flip($keylist);
+	$idx = $keyflop[$keyname];
+	while( $orig != $arr[$keylist[0]] ) {
+		// echo 'Keyname: '.$keylist[$idx] . '<br />'; flush(); ob_flush(); // Debugger
+		if($idx < 0) return $arr;
+		if($keylist[$idx] == '' || $keylist[$idx] < 0 || !$keylist[$idx]) {
+			return $arr;
+		}
+		$arr = arrayItemUp($arr, $keylist[$idx]);
+		$idx--;
+	}
+	return $arr;
 }
 
 function arrayItemBottom($arr, $keyname) {
-  $b = $arr[$keyname];
-  unset($arr[$keyname]);
-  $arr[$keyname] = $b;
-  unset($b);
-  return $arr;
+	$b = $arr[$keyname];
+	unset($arr[$keyname]);
+	$arr[$keyname] = $b;
+	unset($b);
+	return $arr;
 }
 
 /**
@@ -850,12 +850,12 @@
 
 function enano_safe_array_merge($arr1, $arr2)
 {
-  $arr3 = $arr1;
-  foreach($arr2 as $k => $v)
-  {
-    $arr3[$k] = $v;
-  }
-  return $arr3;
+	$arr3 = $arr1;
+	foreach($arr2 as $k => $v)
+	{
+		$arr3[$k] = $v;
+	}
+	return $arr3;
 }
 
 /**
@@ -866,21 +866,21 @@
 
 function integerize_array($arr)
 {
-  if ( !is_array($arr) )
-    return $arr;
-  
-  foreach ( $arr as &$val )
-  {
-    if ( is_string($val) && ctype_digit($val) && strlen($val) < 10 )
-    {
-      $val = intval($val);
-    }
-    else if ( is_array($val) )
-    {
-      $val = integerize_array($val);
-    }
-  }
-  return $arr;
+	if ( !is_array($arr) )
+		return $arr;
+	
+	foreach ( $arr as &$val )
+	{
+		if ( is_string($val) && ctype_digit($val) && strlen($val) < 10 )
+		{
+			$val = intval($val);
+		}
+		else if ( is_array($val) )
+		{
+			$val = integerize_array($val);
+		}
+	}
+	return $arr;
 }
 
 // Convert IP address to hex string
@@ -888,21 +888,21 @@
 // Output: 0x7f000001 (string)
 // Updated 12/8/06 to work with PHP4 and not use eval() (blech)
 function ip2hex($ip) {
-  if ( preg_match('/^([0-9a-f:]+)$/', $ip) )
-  {
-    // this is an ipv6 address
-    return str_replace(':', '', $ip);
-  }
-  $nums = explode('.', $ip);
-  if(sizeof($nums) != 4) return false;
-  $str = '0x';
-  foreach($nums as $n)
-  {
-    $byte = (string)dechex($n);
-    if ( strlen($byte) < 2 )
-      $byte = '0' . $byte;
-  }
-  return $str;
+	if ( preg_match('/^([0-9a-f:]+)$/', $ip) )
+	{
+		// this is an ipv6 address
+		return str_replace(':', '', $ip);
+	}
+	$nums = explode('.', $ip);
+	if(sizeof($nums) != 4) return false;
+	$str = '0x';
+	foreach($nums as $n)
+	{
+		$byte = (string)dechex($n);
+		if ( strlen($byte) < 2 )
+			$byte = '0' . $byte;
+	}
+	return $str;
 }
 
 // Convert DWord to IP address
@@ -910,17 +910,17 @@
 // Output: 127.0.0.1
 // Updated 12/8/06 to work with PHP4 and not use eval() (blech)
 function hex2ip($in) {
-  if(substr($in, 0, 2) == '0x') $ip = substr($in, 2, 8);
-  else $ip = substr($in, 0, 8);
-  $octets = enano_str_split($ip, 2);
-  $str = '';
-  $newoct = Array();
-  foreach($octets as $o)
-  {
-    $o = (int)hexdec($o);
-    $newoct[] = $o;
-  }
-  return implode('.', $newoct);
+	if(substr($in, 0, 2) == '0x') $ip = substr($in, 2, 8);
+	else $ip = substr($in, 0, 8);
+	$octets = enano_str_split($ip, 2);
+	$str = '';
+	$newoct = Array();
+	foreach($octets as $o)
+	{
+		$o = (int)hexdec($o);
+		$newoct[] = $o;
+	}
+	return implode('.', $newoct);
 }
 
 // Function strip_php moved to RenderMan class
@@ -936,38 +936,38 @@
 
 function die_semicritical($t, $p, $no_wrapper = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  $db->close();
-  
-  if ( @ob_get_status() )
-    ob_end_clean();
-
-  // If the config hasn't been fetched yet, call grinding_halt.
-  if ( !defined('ENANO_CONFIG_FETCHED') )
-  {
-    grinding_halt($t, $p);
-  }
-  
-  // also do grinding_halt() if we're in CLI mode
-  if ( defined('ENANO_CLI') )
-  {
-    grinding_halt($t, $p);
-  }
-
-  if ( $no_wrapper )
-  {
-    echo '<h2>' . htmlspecialchars($t) . '</h2>';
-    echo "<p>$p</p>";
-    exit;
-  }
-  
-  $output = new Output_Safe();
-  $output->set_title($t);
-  $output->header();
-  echo $p;
-  $output->footer();
-
-  exit;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	$db->close();
+	
+	if ( @ob_get_status() )
+		ob_end_clean();
+
+	// If the config hasn't been fetched yet, call grinding_halt.
+	if ( !defined('ENANO_CONFIG_FETCHED') )
+	{
+		grinding_halt($t, $p);
+	}
+	
+	// also do grinding_halt() if we're in CLI mode
+	if ( defined('ENANO_CLI') )
+	{
+		grinding_halt($t, $p);
+	}
+
+	if ( $no_wrapper )
+	{
+		echo '<h2>' . htmlspecialchars($t) . '</h2>';
+		echo "<p>$p</p>";
+		exit;
+	}
+	
+	$output = new Output_Safe();
+	$output->set_title($t);
+	$output->header();
+	echo $p;
+	$output->footer();
+
+	exit;
 }
 
 /**
@@ -978,20 +978,20 @@
 
 function die_friendly($t, $p)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-
-  if ( @ob_get_status() )
-    ob_end_clean();
-  
-  global $output;
-
-  $output->set_title($t);
-  $template->header();
-  echo $p;
-  $template->footer();
-  $db->close();
-
-  exit;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+
+	if ( @ob_get_status() )
+		ob_end_clean();
+	
+	global $output;
+
+	$output->set_title($t);
+	$template->header();
+	echo $p;
+	$template->footer();
+	$db->close();
+
+	exit;
 }
 
 /**
@@ -1002,47 +1002,47 @@
 
 function grinding_halt($t, $p)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  if ( !defined('scriptPath') )
-    require( ENANO_ROOT . '/config.php' );
-
-  if ( is_object($db) )
-    $db->close();
-  
-  if ( @ob_get_status() )
-    ob_end_clean();
-  
-  if ( defined('ENANO_CLI') )
-  {
-    // set console color
-    echo "\x1B[31;1m";
-    // error title
-    echo "Critical error in Enano runtime: ";
-    // unbold
-    echo "$t\n";
-    // bold
-    echo "\x1B[37;1m";
-    echo "Error: ";
-    // unbold
-    echo "\x1B[0m";
-    echo "$p\n";
-    exit(1);
-  }
-  $theme = ( defined('ENANO_CONFIG_FETCHED') ) ? getConfig('theme_default') : 'oxygen';
-  $style = ( defined('ENANO_CONFIG_FETCHED') ) ? '__foo__' : 'bleu';
-  
-  $tpl = new template_nodb();
-  $tpl->load_theme($theme, $style);
-  $tpl->tpl_strings['SITE_NAME'] = 'Critical error';
-  $tpl->tpl_strings['SITE_DESC'] = 'This website is experiencing a serious error and cannot load.';
-  $tpl->tpl_strings['COPYRIGHT'] = 'Unable to retrieve copyright information';
-  $tpl->tpl_strings['PAGE_NAME'] = $t;
-  $tpl->header();
-  echo $p;
-  $tpl->footer();
-  
-  exit;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	if ( !defined('scriptPath') )
+		require( ENANO_ROOT . '/config.php' );
+
+	if ( is_object($db) )
+		$db->close();
+	
+	if ( @ob_get_status() )
+		ob_end_clean();
+	
+	if ( defined('ENANO_CLI') )
+	{
+		// set console color
+		echo "\x1B[31;1m";
+		// error title
+		echo "Critical error in Enano runtime: ";
+		// unbold
+		echo "$t\n";
+		// bold
+		echo "\x1B[37;1m";
+		echo "Error: ";
+		// unbold
+		echo "\x1B[0m";
+		echo "$p\n";
+		exit(1);
+	}
+	$theme = ( defined('ENANO_CONFIG_FETCHED') ) ? getConfig('theme_default') : 'oxygen';
+	$style = ( defined('ENANO_CONFIG_FETCHED') ) ? '__foo__' : 'bleu';
+	
+	$tpl = new template_nodb();
+	$tpl->load_theme($theme, $style);
+	$tpl->tpl_strings['SITE_NAME'] = 'Critical error';
+	$tpl->tpl_strings['SITE_DESC'] = 'This website is experiencing a serious error and cannot load.';
+	$tpl->tpl_strings['COPYRIGHT'] = 'Unable to retrieve copyright information';
+	$tpl->tpl_strings['PAGE_NAME'] = $t;
+	$tpl->header();
+	echo $p;
+	$tpl->footer();
+	
+	exit;
 }
 
 /**
@@ -1051,7 +1051,7 @@
 
 function show_category_info()
 {
-  throw new Exception('show_category_info() is deprecated. Use Namespace_*::display_categories().');
+	throw new Exception('show_category_info() is deprecated. Use Namespace_*::display_categories().');
 }
 
 /**
@@ -1060,7 +1060,7 @@
 
 function show_file_info($page = false)
 {
-  throw new Exception('show_file_info() is deprecated. Use Namespace_File::show_info().');
+	throw new Exception('show_file_info() is deprecated. Use Namespace_File::show_info().');
 }
 
 /**
@@ -1069,7 +1069,7 @@
 
 function display_page_headers()
 {
-  // Deprecated.
+	// Deprecated.
 }
 
 /**
@@ -1078,18 +1078,18 @@
 
 function display_page_footers()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  if ( isset($_GET['nofooters']) )
-  {
-    return;
-  }
-  
-  $code = $plugins->setHook('send_page_footers');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	if ( isset($_GET['nofooters']) )
+	{
+		return;
+	}
+	
+	$code = $plugins->setHook('send_page_footers');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
 }
 
 /**
@@ -1100,34 +1100,34 @@
 
 function display_redirect_notice($page_id, $namespace)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang, $output;
-  
-  $url = makeUrlNS($namespace, $page_id, false, true);
-  $ns = namespace_factory($page_id, $namespace);
-  $page_data = $ns->get_cdata();
-  
-  $title = $page_data['name'];
-  
-  $cls = $ns->exists() ? '' : 'class="wikilink-nonexistent" ';
-    
-  $a = '<a ' . $cls . 'href="' . $url . '">' . $title . '</a>';
-  $redir_html = '<br /><div class="mdg-infobox">
-          <table border="0" width="100%" cellspacing="0" cellpadding="0">
-            <tr>
-              <td valign="top">
-                <img alt="Cute wet-floor icon" src="' . cdnPath . '/images/redirector.png" />
-              </td>
-              <td valign="top" style="padding-left: 10px;">
-                ' . $lang->get('page_msg_this_is_a_redirector', array( 'redirect_target' => $a )) . '
-              </td>
-            </tr>
-          </table>
-        </div>
-        <br />
-        <hr style="margin-left: 1em; width: 200px;" />';
-  
-  $output->add_after_header($redir_html);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang, $output;
+	
+	$url = makeUrlNS($namespace, $page_id, false, true);
+	$ns = namespace_factory($page_id, $namespace);
+	$page_data = $ns->get_cdata();
+	
+	$title = $page_data['name'];
+	
+	$cls = $ns->exists() ? '' : 'class="wikilink-nonexistent" ';
+		
+	$a = '<a ' . $cls . 'href="' . $url . '">' . $title . '</a>';
+	$redir_html = '<br /><div class="mdg-infobox">
+					<table border="0" width="100%" cellspacing="0" cellpadding="0">
+						<tr>
+							<td valign="top">
+								<img alt="Cute wet-floor icon" src="' . cdnPath . '/images/redirector.png" />
+							</td>
+							<td valign="top" style="padding-left: 10px;">
+								' . $lang->get('page_msg_this_is_a_redirector', array( 'redirect_target' => $a )) . '
+							</td>
+						</tr>
+					</table>
+				</div>
+				<br />
+				<hr style="margin-left: 1em; width: 200px;" />';
+	
+	$output->add_after_header($redir_html);
 }
 
 /**
@@ -1138,19 +1138,19 @@
 
 function smtp_get_response($socket, $response, $line = __LINE__)
 {
-  $server_response = '';
-  while (substr($server_response, 3, 1) != ' ')
-  {
-    if (!($server_response = fgets($socket, 256)))
-    {
-      die_friendly('SMTP Error', "<p>Couldn't get mail server response codes</p>");
-    }
-  }
-
-  if (!(substr($server_response, 0, 3) == $response))
-  {
-    die_friendly('SMTP Error', "<p>Ran into problems sending mail. Response: $server_response</p>");
-  }
+	$server_response = '';
+	while (substr($server_response, 3, 1) != ' ')
+	{
+		if (!($server_response = fgets($socket, 256)))
+		{
+			die_friendly('SMTP Error', "<p>Couldn't get mail server response codes</p>");
+		}
+	}
+
+	if (!(substr($server_response, 0, 3) == $response))
+	{
+		die_friendly('SMTP Error', "<p>Ran into problems sending mail. Response: $server_response</p>");
+	}
 }
 
 /**
@@ -1163,7 +1163,7 @@
 
 function smtp_send_email($to, $subject, $message, $from)
 {
-  return smtp_send_email_core($to, $subject, $message, "From: <$from>\n");
+	return smtp_send_email_core($to, $subject, $message, "From: <$from>\n");
 }
 
 /**
@@ -1178,184 +1178,184 @@
 
 function smtp_send_email_core($mail_to, $subject, $message, $headers = '')
 {
-  // Fix any bare linefeeds in the message to make it RFC821 Compliant.
-  $message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
-
-  if ($headers != '')
-  {
-    if (is_array($headers))
-    {
-      if (sizeof($headers) > 1)
-      {
-        $headers = join("\n", $headers);
-      }
-      else
-      {
-        $headers = $headers[0];
-      }
-    }
-    $headers = chop($headers);
-
-    // Make sure there are no bare linefeeds in the headers
-    $headers = preg_replace('#(?<!\r)\n#si', "\r\n", $headers);
-
-    // Ok this is rather confusing all things considered,
-    // but we have to grab bcc and cc headers and treat them differently
-    // Something we really didn't take into consideration originally
-    $header_array = explode("\r\n", $headers);
-    @reset($header_array);
-
-    $headers = '';
-    $cc = '';
-    $bcc = '';
-    while(list(, $header) = each($header_array))
-    {
-      if (preg_match('#^cc:#si', $header))
-      {
-        $cc = preg_replace('#^cc:(.*)#si', '\1', $header);
-      }
-      else if (preg_match('#^bcc:#si', $header))
-      {
-        $bcc = preg_replace('#^bcc:(.*)#si', '\1', $header);
-        $header = '';
-      }
-      $headers .= ($header != '') ? $header . "\r\n" : '';
-    }
-
-    $headers = chop($headers);
-    $cc = explode(', ', $cc);
-    $bcc = explode(', ', $bcc);
-  }
-
-  if (trim($subject) == '')
-  {
-    die_friendly(GENERAL_ERROR, "No email Subject specified");
-  }
-
-  if (trim($message) == '')
-  {
-    die_friendly(GENERAL_ERROR, "Email message was blank");
-  }
-
-  // setup SMTP
-  $host = getConfig('smtp_server');
-  if ( empty($host) )
-    return 'No smtp_host in config';
-  if ( strstr($host, ':' ) )
-  {
-    $n = explode(':', $host);
-    $smtp_host = $n[0];
-    $port = intval($n[1]);
-  }
-  else
-  {
-    $smtp_host = $host;
-    $port = 25;
-  }
-
-  $smtp_user = getConfig('smtp_user');
-  $smtp_pass = getConfig('smtp_password');
-
-  // Ok we have error checked as much as we can to this point let's get on
-  // it already.
-  if( !$socket = @fsockopen($smtp_host, $port, $errno, $errstr, 20) )
-  {
-    die_friendly(GENERAL_ERROR, "Could not connect to smtp host : $errno : $errstr");
-  }
-
-  // Wait for reply
-  smtp_get_response($socket, "220", __LINE__);
-
-  // Do we want to use AUTH?, send RFC2554 EHLO, else send RFC821 HELO
-  // This improved as provided by SirSir to accomodate
-  if( !empty($smtp_user) && !empty($smtp_pass) )
-  {
-    enano_fputs($socket, "EHLO " . $smtp_host . "\r\n");
-    smtp_get_response($socket, "250", __LINE__);
-
-    enano_fputs($socket, "AUTH LOGIN\r\n");
-    smtp_get_response($socket, "334", __LINE__);
-
-    enano_fputs($socket, base64_encode($smtp_user) . "\r\n");
-    smtp_get_response($socket, "334", __LINE__);
-
-    enano_fputs($socket, base64_encode($smtp_pass) . "\r\n");
-    smtp_get_response($socket, "235", __LINE__);
-  }
-  else
-  {
-    enano_fputs($socket, "HELO " . $smtp_host . "\r\n");
-    smtp_get_response($socket, "250", __LINE__);
-  }
-
-  // From this point onward most server response codes should be 250
-  // Specify who the mail is from....
-  enano_fputs($socket, "MAIL FROM: <" . getConfig('contact_email') . ">\r\n");
-  smtp_get_response($socket, "250", __LINE__);
-
-  // Specify each user to send to and build to header.
-  $to_header = '';
-
-  // Add an additional bit of error checking to the To field.
-  $mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to);
-  if (preg_match('#[^ ]+\@[^ ]+#', $mail_to))
-  {
-    enano_fputs($socket, "RCPT TO: <$mail_to>\r\n");
-    smtp_get_response($socket, "250", __LINE__);
-  }
-
-  // Ok now do the CC and BCC fields...
-  @reset($bcc);
-  while(list(, $bcc_address) = each($bcc))
-  {
-    // Add an additional bit of error checking to bcc header...
-    $bcc_address = trim($bcc_address);
-    if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address))
-    {
-      enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n");
-      smtp_get_response($socket, "250", __LINE__);
-    }
-  }
-
-  @reset($cc);
-  while(list(, $cc_address) = each($cc))
-  {
-    // Add an additional bit of error checking to cc header
-    $cc_address = trim($cc_address);
-    if (preg_match('#[^ ]+\@[^ ]+#', $cc_address))
-    {
-      enano_fputs($socket, "RCPT TO: <$cc_address>\r\n");
-      smtp_get_response($socket, "250", __LINE__);
-    }
-  }
-
-  // Ok now we tell the server we are ready to start sending data
-  enano_fputs($socket, "DATA\r\n");
-
-  // This is the last response code we look for until the end of the message.
-  smtp_get_response($socket, "354", __LINE__);
-
-  // Send the Subject Line...
-  enano_fputs($socket, "Subject: $subject\r\n");
-
-  // Now the To Header.
-  enano_fputs($socket, "To: $mail_to\r\n");
-
-  // Now any custom headers....
-  enano_fputs($socket, "$headers\r\n\r\n");
-
-  // Ok now we are ready for the message...
-  enano_fputs($socket, "$message\r\n");
-
-  // Ok the all the ingredients are mixed in let's cook this puppy...
-  enano_fputs($socket, ".\r\n");
-  smtp_get_response($socket, "250", __LINE__);
-
-  // Now tell the server we are done and close the socket...
-  enano_fputs($socket, "QUIT\r\n");
-  fclose($socket);
-
-  return TRUE;
+	// Fix any bare linefeeds in the message to make it RFC821 Compliant.
+	$message = preg_replace("#(?<!\r)\n#si", "\r\n", $message);
+
+	if ($headers != '')
+	{
+		if (is_array($headers))
+		{
+			if (sizeof($headers) > 1)
+			{
+				$headers = join("\n", $headers);
+			}
+			else
+			{
+				$headers = $headers[0];
+			}
+		}
+		$headers = chop($headers);
+
+		// Make sure there are no bare linefeeds in the headers
+		$headers = preg_replace('#(?<!\r)\n#si', "\r\n", $headers);
+
+		// Ok this is rather confusing all things considered,
+		// but we have to grab bcc and cc headers and treat them differently
+		// Something we really didn't take into consideration originally
+		$header_array = explode("\r\n", $headers);
+		@reset($header_array);
+
+		$headers = '';
+		$cc = '';
+		$bcc = '';
+		while(list(, $header) = each($header_array))
+		{
+			if (preg_match('#^cc:#si', $header))
+			{
+				$cc = preg_replace('#^cc:(.*)#si', '\1', $header);
+			}
+			else if (preg_match('#^bcc:#si', $header))
+			{
+				$bcc = preg_replace('#^bcc:(.*)#si', '\1', $header);
+				$header = '';
+			}
+			$headers .= ($header != '') ? $header . "\r\n" : '';
+		}
+
+		$headers = chop($headers);
+		$cc = explode(', ', $cc);
+		$bcc = explode(', ', $bcc);
+	}
+
+	if (trim($subject) == '')
+	{
+		die_friendly(GENERAL_ERROR, "No email Subject specified");
+	}
+
+	if (trim($message) == '')
+	{
+		die_friendly(GENERAL_ERROR, "Email message was blank");
+	}
+
+	// setup SMTP
+	$host = getConfig('smtp_server');
+	if ( empty($host) )
+		return 'No smtp_host in config';
+	if ( strstr($host, ':' ) )
+	{
+		$n = explode(':', $host);
+		$smtp_host = $n[0];
+		$port = intval($n[1]);
+	}
+	else
+	{
+		$smtp_host = $host;
+		$port = 25;
+	}
+
+	$smtp_user = getConfig('smtp_user');
+	$smtp_pass = getConfig('smtp_password');
+
+	// Ok we have error checked as much as we can to this point let's get on
+	// it already.
+	if( !$socket = @fsockopen($smtp_host, $port, $errno, $errstr, 20) )
+	{
+		die_friendly(GENERAL_ERROR, "Could not connect to smtp host : $errno : $errstr");
+	}
+
+	// Wait for reply
+	smtp_get_response($socket, "220", __LINE__);
+
+	// Do we want to use AUTH?, send RFC2554 EHLO, else send RFC821 HELO
+	// This improved as provided by SirSir to accomodate
+	if( !empty($smtp_user) && !empty($smtp_pass) )
+	{
+		enano_fputs($socket, "EHLO " . $smtp_host . "\r\n");
+		smtp_get_response($socket, "250", __LINE__);
+
+		enano_fputs($socket, "AUTH LOGIN\r\n");
+		smtp_get_response($socket, "334", __LINE__);
+
+		enano_fputs($socket, base64_encode($smtp_user) . "\r\n");
+		smtp_get_response($socket, "334", __LINE__);
+
+		enano_fputs($socket, base64_encode($smtp_pass) . "\r\n");
+		smtp_get_response($socket, "235", __LINE__);
+	}
+	else
+	{
+		enano_fputs($socket, "HELO " . $smtp_host . "\r\n");
+		smtp_get_response($socket, "250", __LINE__);
+	}
+
+	// From this point onward most server response codes should be 250
+	// Specify who the mail is from....
+	enano_fputs($socket, "MAIL FROM: <" . getConfig('contact_email') . ">\r\n");
+	smtp_get_response($socket, "250", __LINE__);
+
+	// Specify each user to send to and build to header.
+	$to_header = '';
+
+	// Add an additional bit of error checking to the To field.
+	$mail_to = (trim($mail_to) == '') ? 'Undisclosed-recipients:;' : trim($mail_to);
+	if (preg_match('#[^ ]+\@[^ ]+#', $mail_to))
+	{
+		enano_fputs($socket, "RCPT TO: <$mail_to>\r\n");
+		smtp_get_response($socket, "250", __LINE__);
+	}
+
+	// Ok now do the CC and BCC fields...
+	@reset($bcc);
+	while(list(, $bcc_address) = each($bcc))
+	{
+		// Add an additional bit of error checking to bcc header...
+		$bcc_address = trim($bcc_address);
+		if (preg_match('#[^ ]+\@[^ ]+#', $bcc_address))
+		{
+			enano_fputs($socket, "RCPT TO: <$bcc_address>\r\n");
+			smtp_get_response($socket, "250", __LINE__);
+		}
+	}
+
+	@reset($cc);
+	while(list(, $cc_address) = each($cc))
+	{
+		// Add an additional bit of error checking to cc header
+		$cc_address = trim($cc_address);
+		if (preg_match('#[^ ]+\@[^ ]+#', $cc_address))
+		{
+			enano_fputs($socket, "RCPT TO: <$cc_address>\r\n");
+			smtp_get_response($socket, "250", __LINE__);
+		}
+	}
+
+	// Ok now we tell the server we are ready to start sending data
+	enano_fputs($socket, "DATA\r\n");
+
+	// This is the last response code we look for until the end of the message.
+	smtp_get_response($socket, "354", __LINE__);
+
+	// Send the Subject Line...
+	enano_fputs($socket, "Subject: $subject\r\n");
+
+	// Now the To Header.
+	enano_fputs($socket, "To: $mail_to\r\n");
+
+	// Now any custom headers....
+	enano_fputs($socket, "$headers\r\n\r\n");
+
+	// Ok now we are ready for the message...
+	enano_fputs($socket, "$message\r\n");
+
+	// Ok the all the ingredients are mixed in let's cook this puppy...
+	enano_fputs($socket, ".\r\n");
+	smtp_get_response($socket, "250", __LINE__);
+
+	// Now tell the server we are done and close the socket...
+	enano_fputs($socket, "QUIT\r\n");
+	fclose($socket);
+
+	return TRUE;
 }
 
 /**
@@ -1367,24 +1367,24 @@
 
 function enano_version($long = false, $no_nightly = false)
 {
-  if ( !defined('ENANO_CONFIG_FETCHED') )
-  {
-    return function_exists('installer_enano_version') ? installer_enano_version() : $GLOBALS['enano_version'];
-  }
-  $r = getConfig('enano_version');
-  $rc = ( $long ) ? ' release candidate ' : 'RC';
-  $b = ( $long ) ? ' beta ' : 'b';
-  $a = ( $long ) ? ' alpha ' : 'a';
-  if($v = getConfig('enano_rc_version')) $r .= $rc.$v;
-  if($v = getConfig('enano_beta_version')) $r .= $b.$v;
-  if($v = getConfig('enano_alpha_version')) $r .= $a.$v;
-  if ( defined('ENANO_NIGHTLY') && !$no_nightly )
-  {
-    $nightlytag  = ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
-    $nightlylong = ' nightly; build date: ' . ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
-    $r = ( $long ) ? $r . $nightlylong : $r . '-nightly-' . $nightlytag;
-  }
-  return $r;
+	if ( !defined('ENANO_CONFIG_FETCHED') )
+	{
+		return function_exists('installer_enano_version') ? installer_enano_version() : $GLOBALS['enano_version'];
+	}
+	$r = getConfig('enano_version');
+	$rc = ( $long ) ? ' release candidate ' : 'RC';
+	$b = ( $long ) ? ' beta ' : 'b';
+	$a = ( $long ) ? ' alpha ' : 'a';
+	if($v = getConfig('enano_rc_version')) $r .= $rc.$v;
+	if($v = getConfig('enano_beta_version')) $r .= $b.$v;
+	if($v = getConfig('enano_alpha_version')) $r .= $a.$v;
+	if ( defined('ENANO_NIGHTLY') && !$no_nightly )
+	{
+		$nightlytag  = ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
+		$nightlylong = ' nightly; build date: ' . ENANO_NIGHTLY_MONTH . '-' . ENANO_NIGHTLY_DAY . '-' . ENANO_NIGHTLY_YEAR;
+		$r = ( $long ) ? $r . $nightlylong : $r . '-nightly-' . $nightlytag;
+	}
+	return $r;
 }
 
 /**
@@ -1394,31 +1394,31 @@
 
 function enano_codename()
 {
-  $names = array(
-      '1.0RC1' => 'Leprechaun',
-      '1.0RC2' => 'Clurichaun',
-      '1.0RC3' => 'Druid',
-      '1.0'    => 'Banshee',
-      '1.0.1'  => 'Loch Ness',
-      '1.0.1.1'=> 'Loch Ness internal bugfix build',
-      '1.0.2b1'=> 'Coblynau unstable',
-      '1.0.2'  => 'Coblynau',
-      '1.0.3'  => 'Dyrad',
-      '1.1.1'  => 'Caoineag alpha 1',
-      '1.1.2'  => 'Caoineag alpha 2',
-      '1.1.3'  => 'Caoineag alpha 3',
-      '1.1.4'  => 'Caoineag alpha 4',
-      '1.1.5'  => 'Caoineag alpha 5',
-      '1.1.6'  => 'Caoineag beta 1',
-      '1.1.7'  => 'Caoineag beta 2',
-      '1.1.8'  => 'Caoineag beta 3',
-    );
-  $version = enano_version();
-  if ( isset($names[$version]) )
-  {
-    return $names[$version];
-  }
-  return 'Anonymous build';
+	$names = array(
+			'1.0RC1' => 'Leprechaun',
+			'1.0RC2' => 'Clurichaun',
+			'1.0RC3' => 'Druid',
+			'1.0'    => 'Banshee',
+			'1.0.1'  => 'Loch Ness',
+			'1.0.1.1'=> 'Loch Ness internal bugfix build',
+			'1.0.2b1'=> 'Coblynau unstable',
+			'1.0.2'  => 'Coblynau',
+			'1.0.3'  => 'Dyrad',
+			'1.1.1'  => 'Caoineag alpha 1',
+			'1.1.2'  => 'Caoineag alpha 2',
+			'1.1.3'  => 'Caoineag alpha 3',
+			'1.1.4'  => 'Caoineag alpha 4',
+			'1.1.5'  => 'Caoineag alpha 5',
+			'1.1.6'  => 'Caoineag beta 1',
+			'1.1.7'  => 'Caoineag beta 2',
+			'1.1.8'  => 'Caoineag beta 3',
+		);
+	$version = enano_version();
+	if ( isset($names[$version]) )
+	{
+		return $names[$version];
+	}
+	return 'Anonymous build';
 }
 
 /**
@@ -1427,8 +1427,8 @@
  */
 
 function _die($t) {
-  $_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')';
-  die($_ob);
+	$_ob = 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\'' . rawurlencode('' . $t . '') . '\')';
+	die($_ob);
 }
 
 /**
@@ -1437,9 +1437,9 @@
  */
 
 function jsdie($text) {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  $text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace());
-  echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	$text = rawurlencode($text . "\n\nSQL Backtrace:\n" . $db->sql_backtrace());
+	echo 'document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.$text.'\');';
 }
 
 /**
@@ -1450,7 +1450,7 @@
 
 function capitalize_first_letter($text)
 {
-  return strtoupper(substr($text, 0, 1)) . substr($text, 1);
+	return strtoupper(substr($text, 0, 1)) . substr($text, 1);
 }
 
 /**
@@ -1462,7 +1462,7 @@
 
 function is_bit($bitfield, $value)
 {
-  return ( $bitfield & $value ) ? true : false;
+	return ( $bitfield & $value ) ? true : false;
 }
 
 /**
@@ -1473,16 +1473,16 @@
 
 function trim_spaces($text)
 {
-  $d = true;
-  while($d)
-  {
-    $c = substr($text, 0, 1);
-    $a = substr($text, strlen($text)-1, strlen($text));
-    if($c == "\n" || $c == "\r" || $c == "\t" || $c == ' ') $text = substr($text, 1, strlen($text));
-    elseif($a == "\n" || $a == "\r" || $a == "\t" || $a == ' ') $text = substr($text, 0, strlen($text)-1);
-    else $d = false;
-  }
-  return $text;
+	$d = true;
+	while($d)
+	{
+		$c = substr($text, 0, 1);
+		$a = substr($text, strlen($text)-1, strlen($text));
+		if($c == "\n" || $c == "\r" || $c == "\t" || $c == ' ') $text = substr($text, 1, strlen($text));
+		elseif($a == "\n" || $a == "\r" || $a == "\t" || $a == ' ') $text = substr($text, 0, strlen($text)-1);
+		else $d = false;
+	}
+	return $text;
 }
 
 /**
@@ -1494,21 +1494,21 @@
 
 function enano_str_split($text, $inc = 1)
 {
-  if($inc < 1)
-  {
-    return false;
-  }
-  if($inc >= strlen($text))
-  {
-    return Array($text);
-  }
-  $len = ceil(strlen($text) / $inc);
-  $ret = Array();
-  for ( $i = 0; $i < strlen($text); $i = $i + $inc )
-  {
-    $ret[] = substr($text, $i, $inc);
-  }
-  return $ret;
+	if($inc < 1)
+	{
+		return false;
+	}
+	if($inc >= strlen($text))
+	{
+		return Array($text);
+	}
+	$len = ceil(strlen($text) / $inc);
+	$ret = Array();
+	for ( $i = 0; $i < strlen($text); $i = $i + $inc )
+	{
+		$ret[] = substr($text, $i, $inc);
+	}
+	return $ret;
 }
 
 /**
@@ -1518,13 +1518,13 @@
  */
 function hex2bin($text)
 {
-  $arr = enano_str_split($text, 2);
-  $ret = '';
-  for ($i=0; $i<sizeof($arr); $i++)
-  {
-    $ret .= chr(hexdec($arr[$i]));
-  }
-  return $ret;
+	$arr = enano_str_split($text, 2);
+	$ret = '';
+	for ($i=0; $i<sizeof($arr); $i++)
+	{
+		$ret .= chr(hexdec($arr[$i]));
+	}
+	return $ret;
 }
 
 /**
@@ -1535,24 +1535,24 @@
 
 function enano_debug_print_backtrace($return = false)
 {
-  ob_start();
-  if ( !$return )
-    echo '<pre>';
-  if ( function_exists('debug_print_backtrace') )
-  {
-    debug_print_backtrace();
-  }
-  else
-  {
-    echo '<b>Warning:</b> No debug_print_backtrace() support!';
-  }
-  if ( !$return )
-    echo '</pre>';
-  $c = ob_get_contents();
-  ob_end_clean();
-  if($return) return $c;
-  else echo $c;
-  return null;
+	ob_start();
+	if ( !$return )
+		echo '<pre>';
+	if ( function_exists('debug_print_backtrace') )
+	{
+		debug_print_backtrace();
+	}
+	else
+	{
+		echo '<b>Warning:</b> No debug_print_backtrace() support!';
+	}
+	if ( !$return )
+		echo '</pre>';
+	$c = ob_get_contents();
+	ob_end_clean();
+	if($return) return $c;
+	else echo $c;
+	return null;
 }
 
 /**
@@ -1565,15 +1565,15 @@
 
 function hexencode($text, $prefix = '%', $suffix = '')
 {
-  $arr = enano_str_split($text);
-  $r = '';
-  foreach($arr as $a)
-  {
-    $nibble = (string)dechex(ord($a));
-    if(strlen($nibble) == 1) $nibble = '0' . $nibble;
-    $r .= $prefix . $nibble . $suffix;
-  }
-  return $r;
+	$arr = enano_str_split($text);
+	$r = '';
+	foreach($arr as $a)
+	{
+		$nibble = (string)dechex(ord($a));
+		if(strlen($nibble) == 1) $nibble = '0' . $nibble;
+		$r .= $prefix . $nibble . $suffix;
+	}
+	return $r;
 }
 
 /**
@@ -1583,14 +1583,14 @@
 
 function enano_get_magic_quotes_gpc()
 {
-  if(function_exists('get_magic_quotes_gpc'))
-  {
-    return ( get_magic_quotes_gpc() == 1 );
-  }
-  else
-  {
-    return ( strtolower(@ini_get('magic_quotes_gpc')) == '1' );
-  }
+	if(function_exists('get_magic_quotes_gpc'))
+	{
+		return ( get_magic_quotes_gpc() == 1 );
+	}
+	else
+	{
+		return ( strtolower(@ini_get('magic_quotes_gpc')) == '1' );
+	}
 }
 
 /**
@@ -1601,15 +1601,15 @@
 
 function stripslashes_recurse($arr)
 {
-  foreach($arr as $k => $xxxx)
-  {
-    $val =& $arr[$k];
-    if(is_string($val))
-      $val = stripslashes($val);
-    elseif(is_array($val))
-      $val = stripslashes_recurse($val);
-  }
-  return $arr;
+	foreach($arr as $k => $xxxx)
+	{
+		$val =& $arr[$k];
+		if(is_string($val))
+			$val = stripslashes($val);
+		elseif(is_array($val))
+			$val = stripslashes_recurse($val);
+	}
+	return $arr;
 }
 
 /**
@@ -1620,15 +1620,15 @@
 
 function strip_nul_chars($arr)
 {
-  foreach($arr as $k => $xxxx_unused)
-  {
-    $val =& $arr[$k];
-    if(is_string($val))
-      $val = str_replace("\000", '', $val);
-    elseif(is_array($val))
-      $val = strip_nul_chars($val);
-  }
-  return $arr;
+	foreach($arr as $k => $xxxx_unused)
+	{
+		$val =& $arr[$k];
+		if(is_string($val))
+			$val = str_replace("\000", '', $val);
+		elseif(is_array($val))
+			$val = strip_nul_chars($val);
+	}
+	return $arr;
 }
 
 /**
@@ -1639,21 +1639,21 @@
  */
 function strip_magic_quotes_gpc()
 {
-  if(enano_get_magic_quotes_gpc())
-  {
-    $_POST    = stripslashes_recurse($_POST);
-    $_GET     = stripslashes_recurse($_GET);
-    $_COOKIE  = stripslashes_recurse($_COOKIE);
-    $_REQUEST = stripslashes_recurse($_REQUEST);
-  }
-  $_POST    = strip_nul_chars($_POST);
-  $_GET     = strip_nul_chars($_GET);
-  $_COOKIE  = strip_nul_chars($_COOKIE);
-  $_REQUEST = strip_nul_chars($_REQUEST);
-  $_POST    = decode_unicode_array($_POST);
-  $_GET     = decode_unicode_array($_GET);
-  $_COOKIE  = decode_unicode_array($_COOKIE);
-  $_REQUEST = decode_unicode_array($_REQUEST);
+	if(enano_get_magic_quotes_gpc())
+	{
+		$_POST    = stripslashes_recurse($_POST);
+		$_GET     = stripslashes_recurse($_GET);
+		$_COOKIE  = stripslashes_recurse($_COOKIE);
+		$_REQUEST = stripslashes_recurse($_REQUEST);
+	}
+	$_POST    = strip_nul_chars($_POST);
+	$_GET     = strip_nul_chars($_GET);
+	$_COOKIE  = strip_nul_chars($_COOKIE);
+	$_REQUEST = strip_nul_chars($_REQUEST);
+	$_POST    = decode_unicode_array($_POST);
+	$_GET     = decode_unicode_array($_GET);
+	$_COOKIE  = decode_unicode_array($_COOKIE);
+	$_REQUEST = decode_unicode_array($_REQUEST);
 }
 
 /**
@@ -1664,42 +1664,42 @@
  
 function compress_bitfield($bits)
 {
-  if ( !preg_match('/^[01]+$/', $bits) )
-    return false;
-  
-  $current = intval($bits{0});
-  $clen = 0;
-  $out = '';
-  for ( $i = 0; $i < strlen($bits); $i++ )
-  {
-    $cbit = intval($bits{$i});
-    if ( $cbit !== $current || $clen == 127 || $i == strlen($bits) - 1 )
-    {
-      if ( $i == strlen($bits) - 1 && $cbit === $current )
-      {
-        $clen++;
-      }
-      // write chunk
-      $byte = $clen;
-      if ( $current === 1 )
-        $byte |= 0x80;
-      $out .= chr($byte);
-      
-      if ( $i == strlen($bits) - 1 && $cbit !== $current )
-      {
-        $out .= ( $cbit === 1 ) ? chr(0x81) : chr(0x1);
-      }
-      
-      // reset
-      $current = intval($cbit);
-      $clen = 0;
-    }
-    $clen++;
-  }
-  $crc = dechex(crc32($out));
-  while ( strlen($crc) < 8 )
-    $crc = "0$crc";
-  return "cbf2:{$crc}" . hexencode($out, '', '');
+	if ( !preg_match('/^[01]+$/', $bits) )
+		return false;
+	
+	$current = intval($bits{0});
+	$clen = 0;
+	$out = '';
+	for ( $i = 0; $i < strlen($bits); $i++ )
+	{
+		$cbit = intval($bits{$i});
+		if ( $cbit !== $current || $clen == 127 || $i == strlen($bits) - 1 )
+		{
+			if ( $i == strlen($bits) - 1 && $cbit === $current )
+			{
+				$clen++;
+			}
+			// write chunk
+			$byte = $clen;
+			if ( $current === 1 )
+				$byte |= 0x80;
+			$out .= chr($byte);
+			
+			if ( $i == strlen($bits) - 1 && $cbit !== $current )
+			{
+				$out .= ( $cbit === 1 ) ? chr(0x81) : chr(0x1);
+			}
+			
+			// reset
+			$current = intval($cbit);
+			$clen = 0;
+		}
+		$clen++;
+	}
+	$crc = dechex(crc32($out));
+	while ( strlen($crc) < 8 )
+		$crc = "0$crc";
+	return "cbf2:{$crc}" . hexencode($out, '', '');
 }
 
 // test case
@@ -1714,36 +1714,36 @@
 
 function uncompress_bitfield($bits)
 {
-  if ( substr($bits, 0, 4) == 'cbf:' )
-  {
-    return uncompress_bitfield_old($bits);
-  }
-  if ( substr($bits, 0, 5) != 'cbf2:' )
-  {
-    echo __FUNCTION__.'(): ERROR: Invalid stream';
-    return false;
-  }
-  $bits = substr($bits, 5);
-  $crc = substr($bits, 0, 8);
-  $bits = substr($bits, 8);
-  $bits = hexdecode($bits);
-  if ( dechex(crc32($bits)) !== $crc )
-  {
-    echo __FUNCTION__."(): ERROR: CRC failed";
-    return false;
-  }
-  $out = '';
-  for ( $i = 0; $i < strlen($bits); $i++ )
-  {
-    $byte = ord($bits{$i});
-    $char = $byte & 0x80 ? '1' : '0';
-    $byte &= ~0x80;
-    for ( $j = 0; $j < $byte; $j++ )
-    {
-      $out .= $char;
-    }
-  }
-  return $out;
+	if ( substr($bits, 0, 4) == 'cbf:' )
+	{
+		return uncompress_bitfield_old($bits);
+	}
+	if ( substr($bits, 0, 5) != 'cbf2:' )
+	{
+		echo __FUNCTION__.'(): ERROR: Invalid stream';
+		return false;
+	}
+	$bits = substr($bits, 5);
+	$crc = substr($bits, 0, 8);
+	$bits = substr($bits, 8);
+	$bits = hexdecode($bits);
+	if ( dechex(crc32($bits)) !== $crc )
+	{
+		echo __FUNCTION__."(): ERROR: CRC failed";
+		return false;
+	}
+	$out = '';
+	for ( $i = 0; $i < strlen($bits); $i++ )
+	{
+		$byte = ord($bits{$i});
+		$char = $byte & 0x80 ? '1' : '0';
+		$byte &= ~0x80;
+		for ( $j = 0; $j < $byte; $j++ )
+		{
+			$out .= $char;
+		}
+	}
+	return $out;
 }
 
 /**
@@ -1755,34 +1755,34 @@
 
 function uncompress_bitfield_old($bits)
 {
-  if(substr($bits, 0, 4) != 'cbf:')
-  {
-    echo __FUNCTION__.'(): ERROR: Invalid stream';
-    return false;
-  }
-  $len = intval(substr($bits, strpos($bits, 'len=')+4, strpos($bits, ';')-strpos($bits, 'len=')-4));
-  $crc = substr($bits, strpos($bits, 'crc=')+4, 8);
-  $data = substr($bits, strpos($bits, 'data=')+5, strpos($bits, '|end')-strpos($bits, 'data=')-5);
-  $data = explode(',', $data);
-  foreach($data as $a => $b)
-  {
-    $d =& $data[$a];
-    $char = substr($d, 0, 1);
-    $dlen = intval(substr($d, 2, strlen($d)-1));
-    $s = '';
-    for($i=0;$i<$dlen;$i++,$s.=$char);
-    $d = $s;
-    unset($s, $dlen, $char);
-  }
-  $decompressed = implode('', $data);
-  $decompressed = substr($decompressed, 0, -1);
-  $dcrc = (string)dechex(crc32($decompressed));
-  if($dcrc != $crc)
-  {
-    echo __FUNCTION__.'(): ERROR: CRC check failed<br />debug info:<br />original crc: '.$crc.'<br />decomp\'ed crc: '.$dcrc.'<br />';
-    return false;
-  }
-  return $decompressed;
+	if(substr($bits, 0, 4) != 'cbf:')
+	{
+		echo __FUNCTION__.'(): ERROR: Invalid stream';
+		return false;
+	}
+	$len = intval(substr($bits, strpos($bits, 'len=')+4, strpos($bits, ';')-strpos($bits, 'len=')-4));
+	$crc = substr($bits, strpos($bits, 'crc=')+4, 8);
+	$data = substr($bits, strpos($bits, 'data=')+5, strpos($bits, '|end')-strpos($bits, 'data=')-5);
+	$data = explode(',', $data);
+	foreach($data as $a => $b)
+	{
+		$d =& $data[$a];
+		$char = substr($d, 0, 1);
+		$dlen = intval(substr($d, 2, strlen($d)-1));
+		$s = '';
+		for($i=0;$i<$dlen;$i++,$s.=$char);
+		$d = $s;
+		unset($s, $dlen, $char);
+	}
+	$decompressed = implode('', $data);
+	$decompressed = substr($decompressed, 0, -1);
+	$dcrc = (string)dechex(crc32($decompressed));
+	if($dcrc != $crc)
+	{
+		echo __FUNCTION__.'(): ERROR: CRC check failed<br />debug info:<br />original crc: '.$crc.'<br />decomp\'ed crc: '.$dcrc.'<br />';
+		return false;
+	}
+	return $decompressed;
 }
 
 /**
@@ -1796,125 +1796,125 @@
 
 function export_table($table, $structure = true, $data = true, $compact = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  $struct_keys = '';
-  $divider   = (!$compact) ? "\n" : "\n";
-  $spacer1   = (!$compact) ? "\n" : " ";
-  $spacer2   = (!$compact) ? "  " : " ";
-  $rowspacer = (!$compact) ? "\n  " : " ";
-  $index_list = Array();
-  $cols = $db->sql_query('SHOW COLUMNS IN '.$table.';');
-  if(!$cols)
-  {
-    echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
-    return false;
-  }
-  $col = Array();
-  $sqlcol = Array();
-  $collist = Array();
-  $pri_keys = Array();
-  // Using fetchrow_num() here to compensate for MySQL l10n
-  while( $row = $db->fetchrow_num() )
-  {
-    $field =& $row[0];
-    $type  =& $row[1];
-    $null  =& $row[2];
-    $key   =& $row[3];
-    $def   =& $row[4];
-    $extra =& $row[5];
-    $col[] = Array(
-      'name'=>$field,
-      'type'=>$type,
-      'null'=>$null,
-      'key'=>$key,
-      'default'=>$def,
-      'extra'=>$extra,
-      );
-    $collist[] = $field;
-  }
-
-  if ( $structure )
-  {
-    $db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;');
-    $struct = $db->sql_query('SHOW CREATE TABLE '.$table.';');
-    if ( !$struct )
-      $db->_die();
-    $row = $db->fetchrow_num();
-    $db->free_result();
-    $struct = $row[1];
-    $struct = preg_replace("/\n\) ENGINE=(.+)$/", "\n);", $struct);
-    unset($row);
-    if ( $compact )
-    {
-      $struct_arr = explode("\n", $struct);
-      foreach ( $struct_arr as $i => $leg )
-      {
-        if ( $i == 0 )
-          continue;
-        $test = trim($leg);
-        if ( empty($test) )
-        {
-          unset($struct_arr[$i]);
-          continue;
-        }
-        $struct_arr[$i] = preg_replace('/^([\s]*)/', ' ', $leg);
-      }
-      $struct = implode("", $struct_arr);
-    }
-  }
-
-  // Structuring complete
-  if($data)
-  {
-    $datq = $db->sql_query('SELECT * FROM '.$table.';');
-    if(!$datq)
-    {
-      echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
-      return false;
-    }
-    if($db->numrows() < 1)
-    {
-      if($structure) return $struct;
-      else return '';
-    }
-    $rowdata = Array();
-    $dataqs = Array();
-    $insert_strings = Array();
-    $z = false;
-    while($row = $db->fetchrow_num())
-    {
-      $z = false;
-      foreach($row as $i => $cell)
-      {
-        $str = mysql_encode_column($cell, $col[$i]['type']);
-        $rowdata[] = $str;
-      }
-      $dataqs2 = implode(",$rowspacer", $dataqs) . ",$rowspacer" . '( ' . implode(', ', $rowdata) . ' )';
-      $ins = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . $dataqs2 . ";";
-      if ( strlen( $ins ) > MYSQL_MAX_PACKET_SIZE )
-      {
-        // We've exceeded the maximum allowed packet size for MySQL - separate this into a different query
-        $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
-        $dataqs = Array('( ' . implode(', ', $rowdata) . ' )');
-        $z = true;
-      }
-      else
-      {
-        $dataqs[] = '( ' . implode(', ', $rowdata) . ' )';
-      }
-      $rowdata = Array();
-    }
-    if ( !$z )
-    {
-      $insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
-      $dataqs = Array();
-    }
-    $datstring = implode($divider, $insert_strings);
-  }
-  if($structure && !$data) return $struct;
-  elseif(!$structure && $data) return $datstring;
-  elseif($structure && $data) return $struct . $divider . $datstring;
-  elseif(!$structure && !$data) return '';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	$struct_keys = '';
+	$divider   = (!$compact) ? "\n" : "\n";
+	$spacer1   = (!$compact) ? "\n" : " ";
+	$spacer2   = (!$compact) ? "  " : " ";
+	$rowspacer = (!$compact) ? "\n  " : " ";
+	$index_list = Array();
+	$cols = $db->sql_query('SHOW COLUMNS IN '.$table.';');
+	if(!$cols)
+	{
+		echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
+		return false;
+	}
+	$col = Array();
+	$sqlcol = Array();
+	$collist = Array();
+	$pri_keys = Array();
+	// Using fetchrow_num() here to compensate for MySQL l10n
+	while( $row = $db->fetchrow_num() )
+	{
+		$field =& $row[0];
+		$type  =& $row[1];
+		$null  =& $row[2];
+		$key   =& $row[3];
+		$def   =& $row[4];
+		$extra =& $row[5];
+		$col[] = Array(
+			'name'=>$field,
+			'type'=>$type,
+			'null'=>$null,
+			'key'=>$key,
+			'default'=>$def,
+			'extra'=>$extra,
+			);
+		$collist[] = $field;
+	}
+
+	if ( $structure )
+	{
+		$db->sql_query('SET SQL_QUOTE_SHOW_CREATE = 0;');
+		$struct = $db->sql_query('SHOW CREATE TABLE '.$table.';');
+		if ( !$struct )
+			$db->_die();
+		$row = $db->fetchrow_num();
+		$db->free_result();
+		$struct = $row[1];
+		$struct = preg_replace("/\n\) ENGINE=(.+)$/", "\n);", $struct);
+		unset($row);
+		if ( $compact )
+		{
+			$struct_arr = explode("\n", $struct);
+			foreach ( $struct_arr as $i => $leg )
+			{
+				if ( $i == 0 )
+					continue;
+				$test = trim($leg);
+				if ( empty($test) )
+				{
+					unset($struct_arr[$i]);
+					continue;
+				}
+				$struct_arr[$i] = preg_replace('/^([\s]*)/', ' ', $leg);
+			}
+			$struct = implode("", $struct_arr);
+		}
+	}
+
+	// Structuring complete
+	if($data)
+	{
+		$datq = $db->sql_query('SELECT * FROM '.$table.';');
+		if(!$datq)
+		{
+			echo 'export_table(): Error getting column list: '.$db->get_error_text().'<br />';
+			return false;
+		}
+		if($db->numrows() < 1)
+		{
+			if($structure) return $struct;
+			else return '';
+		}
+		$rowdata = Array();
+		$dataqs = Array();
+		$insert_strings = Array();
+		$z = false;
+		while($row = $db->fetchrow_num())
+		{
+			$z = false;
+			foreach($row as $i => $cell)
+			{
+				$str = mysql_encode_column($cell, $col[$i]['type']);
+				$rowdata[] = $str;
+			}
+			$dataqs2 = implode(",$rowspacer", $dataqs) . ",$rowspacer" . '( ' . implode(', ', $rowdata) . ' )';
+			$ins = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . $dataqs2 . ";";
+			if ( strlen( $ins ) > MYSQL_MAX_PACKET_SIZE )
+			{
+				// We've exceeded the maximum allowed packet size for MySQL - separate this into a different query
+				$insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
+				$dataqs = Array('( ' . implode(', ', $rowdata) . ' )');
+				$z = true;
+			}
+			else
+			{
+				$dataqs[] = '( ' . implode(', ', $rowdata) . ' )';
+			}
+			$rowdata = Array();
+		}
+		if ( !$z )
+		{
+			$insert_strings[] = 'INSERT INTO '.$table.'( '.implode(',', $collist).' ) VALUES' . implode(",$rowspacer", $dataqs) . ";";;
+			$dataqs = Array();
+		}
+		$datstring = implode($divider, $insert_strings);
+	}
+	if($structure && !$data) return $struct;
+	elseif(!$structure && $data) return $datstring;
+	elseif($structure && $data) return $struct . $divider . $datstring;
+	elseif(!$structure && !$data) return '';
 }
 
 /**
@@ -1924,25 +1924,25 @@
 
 function mysql_encode_column($input, $type)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  // Decide whether to quote the string or not
-  if(substr($type, 0, 7) == 'varchar' || $type == 'datetime' || $type == 'text' || $type == 'tinytext' || $type == 'smalltext' || $type == 'longtext' || substr($type, 0, 4) == 'char')
-  {
-    $str = "'" . $db->escape($input) . "'";
-  }
-  elseif(in_array($type, Array('blob', 'longblob', 'mediumblob', 'smallblob')) || substr($type, 0, 6) == 'binary' || substr($type, 0, 9) == 'varbinary')
-  {
-    $str = '0x' . hexencode($input, '', '');
-  }
-  elseif(is_null($input))
-  {
-    $str = 'NULL';
-  }
-  else
-  {
-    $str = (string)$input;
-  }
-  return $str;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	// Decide whether to quote the string or not
+	if(substr($type, 0, 7) == 'varchar' || $type == 'datetime' || $type == 'text' || $type == 'tinytext' || $type == 'smalltext' || $type == 'longtext' || substr($type, 0, 4) == 'char')
+	{
+		$str = "'" . $db->escape($input) . "'";
+	}
+	elseif(in_array($type, Array('blob', 'longblob', 'mediumblob', 'smallblob')) || substr($type, 0, 6) == 'binary' || substr($type, 0, 9) == 'varbinary')
+	{
+		$str = '0x' . hexencode($input, '', '');
+	}
+	elseif(is_null($input))
+	{
+		$str = 'NULL';
+	}
+	else
+	{
+		$str = (string)$input;
+	}
+	return $str;
 }
 
 /**
@@ -1952,19 +1952,19 @@
 
 function fetch_allowed_extensions()
 {
-  global $mime_types;
-  $bits = getConfig('allowed_mime_types');
-  if(!$bits) return Array(false);
-  $bits = uncompress_bitfield($bits);
-  if(!$bits) return Array(false);
-  $bits = enano_str_split($bits, 1);
-  $ret = Array();
-  $mt = array_keys($mime_types);
-  foreach($bits as $i => $b)
-  {
-    $ret[$mt[$i]] = ( $b == '1' ) ? true : false;
-  }
-  return $ret;
+	global $mime_types;
+	$bits = getConfig('allowed_mime_types');
+	if(!$bits) return Array(false);
+	$bits = uncompress_bitfield($bits);
+	if(!$bits) return Array(false);
+	$bits = enano_str_split($bits, 1);
+	$ret = Array();
+	$mt = array_keys($mime_types);
+	foreach($bits as $i => $b)
+	{
+		$ret[$mt[$i]] = ( $b == '1' ) ? true : false;
+	}
+	return $ret;
 }
 
 /**
@@ -1975,12 +1975,12 @@
 
 function randkey($len = 32)
 {
-  $key = '';
-  for($i=0;$i<$len;$i++)
-  {
-    $key .= chr(mt_rand(0, 255));
-  }
-  return $key;
+	$key = '';
+	for($i=0;$i<$len;$i++)
+	{
+		$key .= chr(mt_rand(0, 255));
+	}
+	return $key;
 }
 
 /**
@@ -1991,14 +1991,14 @@
 
 function hexdecode($hex)
 {
-  $hex = enano_str_split($hex, 2);
-  $bin_key = '';
-  foreach($hex as $nibble)
-  {
-    $byte = chr(hexdec($nibble));
-    $bin_key .= $byte;
-  }
-  return $bin_key;
+	$hex = enano_str_split($hex, 2);
+	$bin_key = '';
+	foreach($hex as $nibble)
+	{
+		$byte = chr(hexdec($nibble));
+		$bin_key .= $byte;
+	}
+	return $bin_key;
 }
 
 /**
@@ -2009,166 +2009,166 @@
 
 function sanitize_html($html, $filter_php = true)
 {
-  // Random seed for substitution
-  $rand_seed = md5( sha1(microtime()) . mt_rand() );
-  
-  // We need MediaWiki
-  require_once(ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php');
-  
-  // Strip out comments that are already escaped
-  preg_match_all('/&lt;!--(.*?)--&gt;/', $html, $comment_match);
-  $i = 0;
-  foreach ( $comment_match[0] as $comment )
-  {
-    $html = str_replace_once($comment, "{HTMLCOMMENT:$i:$rand_seed}", $html);
-    $i++;
-  }
-  
-  // Strip out code sections that will be postprocessed by Text_Wiki
-  preg_match_all(';^<code(\s[^>]*)?>((?:(?R)|.)*?)</code>(\s|$);msi', $html, $code_match);
-  $i = 0;
-  foreach ( $code_match[0] as $code )
-  {
-    $html = str_replace_once($code, "{TW_CODE:$i:$rand_seed}", $html);
-    $i++;
-  }
-
-  $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)</\\1>#is', '&lt;\\1\\2\\3javascript:\\59&gt;\\60&lt;/\\1&gt;', $html);
-  $html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '&lt;\\1\\2\\3javascript:\\59&gt;', $html);
-
-  if($filter_php)
-    $html = str_replace(
-      Array('<?php',    '<?',    '<%',    '?>',    '%>'),
-      Array('&lt;?php', '&lt;?', '&lt;%', '?&gt;', '%&gt;'),
-      $html);
-
-  $tag_whitelist = array_keys ( setupAttributeWhitelist() );
-  if ( !$filter_php )
-    $tag_whitelist[] = '?php';
-  // allow HTML comments
-  $tag_whitelist[] = '!--';
-  $len = strlen($html);
-  $in_quote = false;
-  $quote_char = '';
-  $tag_start = 0;
-  $tag_name = '';
-  $in_tag = false;
-  $trk_name = false;
-  for ( $i = 0; $i < $len; $i++ )
-  {
-    $chr = $html{$i};
-    $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
-    $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
-    if ( $in_quote && $in_tag )
-    {
-      if ( $quote_char == $chr && $prev != '\\' )
-        $in_quote = false;
-    }
-    elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
-    {
-      $in_quote = true;
-      $quote_char = $chr;
-    }
-    if ( $chr == '<' && !$in_tag && $next != '/' )
-    {
-      // start of a tag
-      $tag_start = $i;
-      $in_tag = true;
-      $trk_name = true;
-    }
-    elseif ( !$in_quote && $in_tag && $chr == '>' )
-    {
-      $full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 );
-      $l = strlen($tag_name) + 2;
-      $attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) );
-
-      // Debugging message
-      // echo htmlspecialchars($full_tag) . '<br />';
-
-      if ( !in_array($tag_name, $tag_whitelist) && substr($tag_name, 0, 3) != '!--' )
-      {
-        // Illegal tag
-        //echo $tag_name . ' ';
-
-        $s = ( empty($attribs_only) ) ? '' : ' ';
-
-        $sanitized = '&lt;' . $tag_name . $s . $attribs_only . '&gt;';
-
-        $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
-        $html = str_replace('</' . $tag_name . '>', '&lt;/' . $tag_name . '&gt;', $html);
-        $new_i = $tag_start + strlen($sanitized);
-
-        $len = strlen($html);
-        $i = $new_i;
-
-        $in_tag = false;
-        $tag_name = '';
-        continue;
-      }
-      else
-      {
-        // If not filtering PHP, don't bother to strip
-        if ( $tag_name == '?php' && !$filter_php )
-          continue;
-        // If this is a comment, likewise skip this "tag"
-        if ( $tag_name == '!--' )
-          continue;
-        $f = fixTagAttributes( $attribs_only, $tag_name );
-        $s = ( empty($f) ) ? '' : ' ';
-
-        $sanitized = '<' . $tag_name . $f . '>';
-        $new_i = $tag_start + strlen($sanitized);
-
-        $html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
-        $len = strlen($html);
-        $i = $new_i;
-
-        $in_tag = false;
-        $tag_name = '';
-        continue;
-      }
-    }
-    elseif ( $in_tag && $trk_name )
-    {
-      $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
-      if ( $is_alphabetical )
-        $tag_name .= $chr;
-      else
-      {
-        $trk_name = false;
-      }
-    }
-
-  }
-  
-  // Vulnerability from ha.ckers.org/xss.html:
-  // <script src="http://foo.com/xss.js"
-  // <
-  // The rule is so specific because everything else will have been filtered by now
-  $html = preg_replace('/<(script|iframe)(.+?)src=([^>]*)</i', '&lt;\\1\\2src=\\3&lt;', $html);
-  
-  // Vulnerability reported by fuzion from nukeit.org:
-  // XSS in closing HTML tag style attribute
-  // Fix: escape all closing tags with non-whitelisted characters
-  $html = preg_replace('!</((?:[^>]*)([^a-z0-9_:>-]+)(?:[^>]*))>!i', '&lt;/\\1&gt;', $html);
-
-  // Restore stripped comments
-  $i = 0;
-  foreach ( $comment_match[0] as $comment )
-  {
-    $html = str_replace_once("{HTMLCOMMENT:$i:$rand_seed}", $comment, $html);
-    $i++;
-  }
-  
-  // Restore stripped code
-  $i = 0;
-  foreach ( $code_match[0] as $code )
-  {
-    $html = str_replace_once("{TW_CODE:$i:$rand_seed}", $code, $html);
-    $i++;
-  }
-
-  return $html;
+	// Random seed for substitution
+	$rand_seed = md5( sha1(microtime()) . mt_rand() );
+	
+	// We need MediaWiki
+	require_once(ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php');
+	
+	// Strip out comments that are already escaped
+	preg_match_all('/&lt;!--(.*?)--&gt;/', $html, $comment_match);
+	$i = 0;
+	foreach ( $comment_match[0] as $comment )
+	{
+		$html = str_replace_once($comment, "{HTMLCOMMENT:$i:$rand_seed}", $html);
+		$i++;
+	}
+	
+	// Strip out code sections that will be postprocessed by Text_Wiki
+	preg_match_all(';^<code(\s[^>]*)?>((?:(?R)|.)*?)</code>(\s|$);msi', $html, $code_match);
+	$i = 0;
+	foreach ( $code_match[0] as $code )
+	{
+		$html = str_replace_once($code, "{TW_CODE:$i:$rand_seed}", $html);
+		$i++;
+	}
+
+	$html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>(.*?)</\\1>#is', '&lt;\\1\\2\\3javascript:\\59&gt;\\60&lt;/\\1&gt;', $html);
+	$html = preg_replace('#<([a-z]+)([\s]+)([^>]+?)'.htmlalternatives('javascript:').'(.+?)>#is', '&lt;\\1\\2\\3javascript:\\59&gt;', $html);
+
+	if($filter_php)
+		$html = str_replace(
+			Array('<?php',    '<?',    '<%',    '?>',    '%>'),
+			Array('&lt;?php', '&lt;?', '&lt;%', '?&gt;', '%&gt;'),
+			$html);
+
+	$tag_whitelist = array_keys ( setupAttributeWhitelist() );
+	if ( !$filter_php )
+		$tag_whitelist[] = '?php';
+	// allow HTML comments
+	$tag_whitelist[] = '!--';
+	$len = strlen($html);
+	$in_quote = false;
+	$quote_char = '';
+	$tag_start = 0;
+	$tag_name = '';
+	$in_tag = false;
+	$trk_name = false;
+	for ( $i = 0; $i < $len; $i++ )
+	{
+		$chr = $html{$i};
+		$prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
+		$next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
+		if ( $in_quote && $in_tag )
+		{
+			if ( $quote_char == $chr && $prev != '\\' )
+				$in_quote = false;
+		}
+		elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
+		{
+			$in_quote = true;
+			$quote_char = $chr;
+		}
+		if ( $chr == '<' && !$in_tag && $next != '/' )
+		{
+			// start of a tag
+			$tag_start = $i;
+			$in_tag = true;
+			$trk_name = true;
+		}
+		elseif ( !$in_quote && $in_tag && $chr == '>' )
+		{
+			$full_tag = substr($html, $tag_start, ( $i - $tag_start ) + 1 );
+			$l = strlen($tag_name) + 2;
+			$attribs_only = trim( substr($full_tag, $l, ( strlen($full_tag) - $l - 1 ) ) );
+
+			// Debugging message
+			// echo htmlspecialchars($full_tag) . '<br />';
+
+			if ( !in_array($tag_name, $tag_whitelist) && substr($tag_name, 0, 3) != '!--' )
+			{
+				// Illegal tag
+				//echo $tag_name . ' ';
+
+				$s = ( empty($attribs_only) ) ? '' : ' ';
+
+				$sanitized = '&lt;' . $tag_name . $s . $attribs_only . '&gt;';
+
+				$html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
+				$html = str_replace('</' . $tag_name . '>', '&lt;/' . $tag_name . '&gt;', $html);
+				$new_i = $tag_start + strlen($sanitized);
+
+				$len = strlen($html);
+				$i = $new_i;
+
+				$in_tag = false;
+				$tag_name = '';
+				continue;
+			}
+			else
+			{
+				// If not filtering PHP, don't bother to strip
+				if ( $tag_name == '?php' && !$filter_php )
+					continue;
+				// If this is a comment, likewise skip this "tag"
+				if ( $tag_name == '!--' )
+					continue;
+				$f = fixTagAttributes( $attribs_only, $tag_name );
+				$s = ( empty($f) ) ? '' : ' ';
+
+				$sanitized = '<' . $tag_name . $f . '>';
+				$new_i = $tag_start + strlen($sanitized);
+
+				$html = substr($html, 0, $tag_start) . $sanitized . substr($html, $i + 1);
+				$len = strlen($html);
+				$i = $new_i;
+
+				$in_tag = false;
+				$tag_name = '';
+				continue;
+			}
+		}
+		elseif ( $in_tag && $trk_name )
+		{
+			$is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
+			if ( $is_alphabetical )
+				$tag_name .= $chr;
+			else
+			{
+				$trk_name = false;
+			}
+		}
+
+	}
+	
+	// Vulnerability from ha.ckers.org/xss.html:
+	// <script src="http://foo.com/xss.js"
+	// <
+	// The rule is so specific because everything else will have been filtered by now
+	$html = preg_replace('/<(script|iframe)(.+?)src=([^>]*)</i', '&lt;\\1\\2src=\\3&lt;', $html);
+	
+	// Vulnerability reported by fuzion from nukeit.org:
+	// XSS in closing HTML tag style attribute
+	// Fix: escape all closing tags with non-whitelisted characters
+	$html = preg_replace('!</((?:[^>]*)([^a-z0-9_:>-]+)(?:[^>]*))>!i', '&lt;/\\1&gt;', $html);
+
+	// Restore stripped comments
+	$i = 0;
+	foreach ( $comment_match[0] as $comment )
+	{
+		$html = str_replace_once("{HTMLCOMMENT:$i:$rand_seed}", $comment, $html);
+		$i++;
+	}
+	
+	// Restore stripped code
+	$i = 0;
+	foreach ( $code_match[0] as $code )
+	{
+		$html = str_replace_once("{TW_CODE:$i:$rand_seed}", $code, $html);
+		$i++;
+	}
+
+	return $html;
 }
 
 /**
@@ -2180,162 +2180,162 @@
 function wikiformat_process_block($html)
 {
 
-  $tok1 = "<litewiki>";
-  $tok2 = "</litewiki>";
-
-  $block_tags = array('div', 'p', 'table', 'blockquote', 'pre');
-
-  $len = strlen($html);
-  $in_quote = false;
-  $quote_char = '';
-  $tag_start = 0;
-  $tag_name = '';
-  $in_tag = false;
-  $trk_name = false;
-
-  $diag = 0;
-
-  $block_tagname = '';
-  $in_blocksec = 0;
-  $block_start = 0;
-
-  for ( $i = 0; $i < $len; $i++ )
-  {
-    $chr = $html{$i};
-    $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
-    $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
-
-    // Are we inside of a quoted section?
-    if ( $in_quote && $in_tag )
-    {
-      if ( $quote_char == $chr && $prev != '\\' )
-        $in_quote = false;
-    }
-    elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
-    {
-      $in_quote = true;
-      $quote_char = $chr;
-    }
-
-    if ( $chr == '<' && !$in_tag && $next == '/' )
-    {
-      // Iterate through until we've got a tag name
-      $tag_name = '';
-      $i++;
-      while(true)
-      {
-        $i++;
-        // echo $i . ' ';
-        $chr = $html{$i};
-        $prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
-        $next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
-        $tag_name .= $chr;
-        if ( $next == '>' )
-          break;
-      }
-      // echo '<br />';
-      if ( in_array($tag_name, $block_tags) )
-      {
-        if ( $block_tagname == $tag_name )
-        {
-          $in_blocksec -= 1;
-          if ( $in_blocksec == 0 )
-          {
-            $block_tagname = '';
-            $i += 2;
-            // echo 'Finished wiki litewiki wraparound calc at pos: ' . $i;
-            $full_litewiki = substr($html, $block_start, ( $i - $block_start ));
-            $new_text = "{$tok1}{$full_litewiki}{$tok2}";
-            $html = substr($html, 0, $block_start) . $new_text . substr($html, $i);
-
-            $i += ( strlen($tok1) + strlen($tok2) ) - 1;
-            $len = strlen($html);
-
-            //die('<pre>' . htmlspecialchars($html) . '</pre>');
-          }
-        }
-      }
-
-      $in_tag = false;
-      $in_quote = false;
-      $tag_name = '';
-
-      continue;
-    }
-    else if ( $chr == '<' && !$in_tag && $next != '/' )
-    {
-      // start of a tag
-      $tag_start = $i;
-      $in_tag = true;
-      $trk_name = true;
-    }
-    else if ( !$in_quote && $in_tag && $chr == '>' )
-    {
-      if ( !in_array($tag_name, $block_tags) )
-      {
-        // Inline tag - reset and go to the next one
-        // echo '&lt;inline ' . $tag_name . '&gt; ';
-
-        $in_tag = false;
-        $tag_name = '';
-        continue;
-      }
-      else
-      {
-        // echo '&lt;block: ' . $tag_name . ' @ ' . $i . '&gt;<br/>';
-        if ( $in_blocksec == 0 )
-        {
-          //die('Found a starting tag for a block element: ' . $tag_name . ' at pos ' . $tag_start);
-          $block_tagname = $tag_name;
-          $block_start = $tag_start;
-          $in_blocksec++;
-        }
-        else if ( $block_tagname == $tag_name )
-        {
-          $in_blocksec++;
-        }
-
-        $in_tag = false;
-        $tag_name = '';
-        continue;
-      }
-    }
-    elseif ( $in_tag && $trk_name )
-    {
-      $is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
-      if ( $is_alphabetical )
-        $tag_name .= $chr;
-      else
-      {
-        $trk_name = false;
-      }
-    }
-
-    // Tokenization complete
-
-  }
-
-  $regex = '/' . str_replace('/', '\\/', preg_quote($tok2)) . '([\s]*)' . preg_quote($tok1) . '/is';
-  // die(htmlspecialchars($regex));
-  $html = preg_replace($regex, '\\1', $html);
-
-  return $html;
+	$tok1 = "<litewiki>";
+	$tok2 = "</litewiki>";
+
+	$block_tags = array('div', 'p', 'table', 'blockquote', 'pre');
+
+	$len = strlen($html);
+	$in_quote = false;
+	$quote_char = '';
+	$tag_start = 0;
+	$tag_name = '';
+	$in_tag = false;
+	$trk_name = false;
+
+	$diag = 0;
+
+	$block_tagname = '';
+	$in_blocksec = 0;
+	$block_start = 0;
+
+	for ( $i = 0; $i < $len; $i++ )
+	{
+		$chr = $html{$i};
+		$prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
+		$next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
+
+		// Are we inside of a quoted section?
+		if ( $in_quote && $in_tag )
+		{
+			if ( $quote_char == $chr && $prev != '\\' )
+				$in_quote = false;
+		}
+		elseif ( ( $chr == '"' || $chr == "'" ) && $prev != '\\' && $in_tag )
+		{
+			$in_quote = true;
+			$quote_char = $chr;
+		}
+
+		if ( $chr == '<' && !$in_tag && $next == '/' )
+		{
+			// Iterate through until we've got a tag name
+			$tag_name = '';
+			$i++;
+			while(true)
+			{
+				$i++;
+				// echo $i . ' ';
+				$chr = $html{$i};
+				$prev = ( $i == 0 ) ? '' : $html{ $i - 1 };
+				$next = ( ( $i + 1 ) == $len ) ? '' : $html { $i + 1 };
+				$tag_name .= $chr;
+				if ( $next == '>' )
+					break;
+			}
+			// echo '<br />';
+			if ( in_array($tag_name, $block_tags) )
+			{
+				if ( $block_tagname == $tag_name )
+				{
+					$in_blocksec -= 1;
+					if ( $in_blocksec == 0 )
+					{
+						$block_tagname = '';
+						$i += 2;
+						// echo 'Finished wiki litewiki wraparound calc at pos: ' . $i;
+						$full_litewiki = substr($html, $block_start, ( $i - $block_start ));
+						$new_text = "{$tok1}{$full_litewiki}{$tok2}";
+						$html = substr($html, 0, $block_start) . $new_text . substr($html, $i);
+
+						$i += ( strlen($tok1) + strlen($tok2) ) - 1;
+						$len = strlen($html);
+
+						//die('<pre>' . htmlspecialchars($html) . '</pre>');
+					}
+				}
+			}
+
+			$in_tag = false;
+			$in_quote = false;
+			$tag_name = '';
+
+			continue;
+		}
+		else if ( $chr == '<' && !$in_tag && $next != '/' )
+		{
+			// start of a tag
+			$tag_start = $i;
+			$in_tag = true;
+			$trk_name = true;
+		}
+		else if ( !$in_quote && $in_tag && $chr == '>' )
+		{
+			if ( !in_array($tag_name, $block_tags) )
+			{
+				// Inline tag - reset and go to the next one
+				// echo '&lt;inline ' . $tag_name . '&gt; ';
+
+				$in_tag = false;
+				$tag_name = '';
+				continue;
+			}
+			else
+			{
+				// echo '&lt;block: ' . $tag_name . ' @ ' . $i . '&gt;<br/>';
+				if ( $in_blocksec == 0 )
+				{
+					//die('Found a starting tag for a block element: ' . $tag_name . ' at pos ' . $tag_start);
+					$block_tagname = $tag_name;
+					$block_start = $tag_start;
+					$in_blocksec++;
+				}
+				else if ( $block_tagname == $tag_name )
+				{
+					$in_blocksec++;
+				}
+
+				$in_tag = false;
+				$tag_name = '';
+				continue;
+			}
+		}
+		elseif ( $in_tag && $trk_name )
+		{
+			$is_alphabetical = ( strtolower($chr) != strtoupper($chr) || in_array($chr, array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) || $chr == '?' || $chr == '!' || $chr == '-' );
+			if ( $is_alphabetical )
+				$tag_name .= $chr;
+			else
+			{
+				$trk_name = false;
+			}
+		}
+
+		// Tokenization complete
+
+	}
+
+	$regex = '/' . str_replace('/', '\\/', preg_quote($tok2)) . '([\s]*)' . preg_quote($tok1) . '/is';
+	// die(htmlspecialchars($regex));
+	$html = preg_replace($regex, '\\1', $html);
+
+	return $html;
 
 }
 
 function htmlalternatives($string)
 {
-  $ret = '';
-  for ( $i = 0; $i < strlen($string); $i++ )
-  {
-    $chr = $string{$i};
-    $ch1 = ord($chr);
-    $ch2 = dechex($ch1);
-    $byte = '(&\\#([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch2 . ';|&\\#x([0]*){0,7}' . $ch2 . ';|%([0]*){0,7}' . $ch2 . '|' . preg_quote($chr) . ')';
-    $ret .= $byte;
-    $ret .= '([\s]){0,2}';
-  }
-  return $ret;
+	$ret = '';
+	for ( $i = 0; $i < strlen($string); $i++ )
+	{
+		$chr = $string{$i};
+		$ch1 = ord($chr);
+		$ch2 = dechex($ch1);
+		$byte = '(&\\#([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch1 . ';|\\\\([0]*){0,7}' . $ch2 . ';|&\\#x([0]*){0,7}' . $ch2 . ';|%([0]*){0,7}' . $ch2 . '|' . preg_quote($chr) . ')';
+		$ret .= $byte;
+		$ret .= '([\s]){0,2}';
+	}
+	return $ret;
 }
 
 /**
@@ -2350,13 +2350,13 @@
 
 function gen_sprite($path, $width, $height, $xpos, $ypos)
 {
-  $html = '<img src="' . scriptPath . '/images/spacer.gif" width="' . $width . '" height="' . $height . '" ';
-  $xpos = ( $xpos == 0 ) ? '0' : '-' . strval($xpos);
-  $ypos = ( $ypos == 0 ) ? '0' : '-' . strval($ypos);
-  $html .= 'style="background-image: url(' . $path . '); background-repeat: no-repeat; background-position: ' . $ypos . 'px ' . $xpos . 'px;"';
-  $html .= ' />';
-  
-  return $html;
+	$html = '<img src="' . scriptPath . '/images/spacer.gif" width="' . $width . '" height="' . $height . '" ';
+	$xpos = ( $xpos == 0 ) ? '0' : '-' . strval($xpos);
+	$ypos = ( $ypos == 0 ) ? '0' : '-' . strval($ypos);
+	$html .= 'style="background-image: url(' . $path . '); background-repeat: no-repeat; background-position: ' . $ypos . 'px ' . $xpos . 'px;"';
+	$html .= ' />';
+	
+	return $html;
 }
 
 /**
@@ -2369,10 +2369,10 @@
  $plugins->attachHook('spam_check', 'return my_spam_check($string);');
  function my_spam_check($string)
  {
-   if ( stristr($string, 'viagra') )
-     return false;
-   
-   return true;
+ 	if ( stristr($string, 'viagra') )
+ 		return false;
+ 	
+ 	return true;
  }
  </code>
  * @param string String to check for spam
@@ -2385,18 +2385,18 @@
 
 function spamalyze($string, $name = false, $email = false, $url = false, $ip = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if ( !$ip )
-    $ip =& $_SERVER['REMOTE_ADDR'];
-  
-  $code = $plugins->setHook('spam_check');
-  foreach ( $code as $cmd )
-  {
-    $result = eval($cmd);
-    if ( !$result )
-      return false;
-  }
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	if ( !$ip )
+		$ip =& $_SERVER['REMOTE_ADDR'];
+	
+	$code = $plugins->setHook('spam_check');
+	foreach ( $code as $cmd )
+	{
+		$result = eval($cmd);
+		if ( !$result )
+			return false;
+	}
+	return true;
 }
 
 /**
@@ -2411,113 +2411,113 @@
 
 function generate_paginator($current_page, $num_pages, $result_url, $start_mult = 1, $start_add = 1)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $out = '';
-  $i = 0;
-
-  // Build paginator
-  $pg_css = ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ) ?
-            // IE-specific hack
-            'display: block; width: 1px;':
-            // Other browsers
-            'display: table; margin: 10px 0 0 auto;';
-  
-  $begin = '<div class="tblholder" style="'. $pg_css . '">
-    <table border="0" cellspacing="1" cellpadding="4">
-      <tr><th>' . $lang->get('paginate_lbl_page') . '</th>';
-  $block = '<td class="row1" style="text-align: center;">{LINK}</td>';
-  $end = '</tr></table></div>';
-  $blk = $template->makeParserText($block);
-  $inner = '';
-  $cls = 'row2';
-  if ( $num_pages < 5 )
-  {
-    for ( $i = 0; $i < $num_pages; $i++ )
-    {
-      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-      $offset = strval(($i * $start_mult) + $start_add);
-      $url = htmlspecialchars(sprintf($result_url, $offset));
-      $j = $i + 1;
-      $link = ( $i == $current_page ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
-      $blk->assign_vars(array(
-        'CLASS'=>$cls,
-        'LINK'=>$link
-        ));
-      $inner .= $blk->run();
-    }
-  }
-  else
-  {
-    if ( $current_page + 5 > $num_pages )
-    {
-      $list = Array();
-      $tp = $current_page;
-      if ( $current_page + 0 == $num_pages ) $tp = $tp - 3;
-      if ( $current_page + 1 == $num_pages ) $tp = $tp - 2;
-      if ( $current_page + 2 == $num_pages ) $tp = $tp - 1;
-      for ( $i = $tp - 1; $i <= $tp + 1; $i++ )
-      {
-        $list[] = $i;
-      }
-    }
-    else
-    {
-      $list = Array();
-      $current = $current_page;
-      $lower = ( $current < 3 ) ? 1 : $current - 1;
-      for ( $i = 0; $i < 3; $i++ )
-      {
-        $list[] = $lower + $i;
-      }
-    }
-    $url = sprintf($result_url, $start_add);
-    $link = ( 0 == $current_page ) ? "<b>" . $lang->get('paginate_btn_first') . "</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>&laquo; " . $lang->get('paginate_btn_first') . "</a>";
-    $blk->assign_vars(array(
-      'CLASS'=>$cls,
-      'LINK'=>$link
-      ));
-    $inner .= $blk->run();
-
-    foreach ( $list as $i )
-    {
-      if ( $i == $num_pages )
-        break;
-      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-      $offset = strval(($i * $start_mult) + $start_add);
-      $url = sprintf($result_url, $offset);
-      $j = $i + 1;
-      $link = ( $i == $current_page ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
-      $blk->assign_vars(array(
-        'CLASS'=>$cls,
-        'LINK'=>$link
-        ));
-      $inner .= $blk->run();
-    }
-
-    // "Last" button
-    $total = (($num_pages - 1) * $start_mult) + $start_add;
-
-    if ( $current_page < $num_pages )
-    {
-      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-      $offset = strval($total);
-      $url = sprintf($result_url, $offset);
-      $link = ( $num_pages - 1 == $current_page ) ? "<b>" . $lang->get('paginate_btn_last') . "</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>" . $lang->get('paginate_btn_last') . " &raquo;</a>";
-      $blk->assign_vars(array(
-        'CLASS'=>$cls,
-        'LINK'=>$link
-        ));
-      $inner .= $blk->run();
-    }
-
-  }
-
-  $inner .= '<td class="row2" style="cursor: pointer;" onclick="paginator_goto(this, '.$current_page.', '.$num_pages.', '.$start_mult.', '.$start_add.', unescape(\'' . rawurlencode($result_url) . '\'));">&darr;</td>';
-
-  $paginator = "\n$begin$inner$end\n";
-  return $paginator;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$out = '';
+	$i = 0;
+
+	// Build paginator
+	$pg_css = ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ) ?
+						// IE-specific hack
+						'display: block; width: 1px;':
+						// Other browsers
+						'display: table; margin: 10px 0 0 auto;';
+	
+	$begin = '<div class="tblholder" style="'. $pg_css . '">
+		<table border="0" cellspacing="1" cellpadding="4">
+			<tr><th>' . $lang->get('paginate_lbl_page') . '</th>';
+	$block = '<td class="row1" style="text-align: center;">{LINK}</td>';
+	$end = '</tr></table></div>';
+	$blk = $template->makeParserText($block);
+	$inner = '';
+	$cls = 'row2';
+	if ( $num_pages < 5 )
+	{
+		for ( $i = 0; $i < $num_pages; $i++ )
+		{
+			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+			$offset = strval(($i * $start_mult) + $start_add);
+			$url = htmlspecialchars(sprintf($result_url, $offset));
+			$j = $i + 1;
+			$link = ( $i == $current_page ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
+			$blk->assign_vars(array(
+				'CLASS'=>$cls,
+				'LINK'=>$link
+				));
+			$inner .= $blk->run();
+		}
+	}
+	else
+	{
+		if ( $current_page + 5 > $num_pages )
+		{
+			$list = Array();
+			$tp = $current_page;
+			if ( $current_page + 0 == $num_pages ) $tp = $tp - 3;
+			if ( $current_page + 1 == $num_pages ) $tp = $tp - 2;
+			if ( $current_page + 2 == $num_pages ) $tp = $tp - 1;
+			for ( $i = $tp - 1; $i <= $tp + 1; $i++ )
+			{
+				$list[] = $i;
+			}
+		}
+		else
+		{
+			$list = Array();
+			$current = $current_page;
+			$lower = ( $current < 3 ) ? 1 : $current - 1;
+			for ( $i = 0; $i < 3; $i++ )
+			{
+				$list[] = $lower + $i;
+			}
+		}
+		$url = sprintf($result_url, $start_add);
+		$link = ( 0 == $current_page ) ? "<b>" . $lang->get('paginate_btn_first') . "</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>&laquo; " . $lang->get('paginate_btn_first') . "</a>";
+		$blk->assign_vars(array(
+			'CLASS'=>$cls,
+			'LINK'=>$link
+			));
+		$inner .= $blk->run();
+
+		foreach ( $list as $i )
+		{
+			if ( $i == $num_pages )
+				break;
+			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+			$offset = strval(($i * $start_mult) + $start_add);
+			$url = sprintf($result_url, $offset);
+			$j = $i + 1;
+			$link = ( $i == $current_page ) ? "<b>$j</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>$j</a>";
+			$blk->assign_vars(array(
+				'CLASS'=>$cls,
+				'LINK'=>$link
+				));
+			$inner .= $blk->run();
+		}
+
+		// "Last" button
+		$total = (($num_pages - 1) * $start_mult) + $start_add;
+
+		if ( $current_page < $num_pages )
+		{
+			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+			$offset = strval($total);
+			$url = sprintf($result_url, $offset);
+			$link = ( $num_pages - 1 == $current_page ) ? "<b>" . $lang->get('paginate_btn_last') . "</b>" : "<a href=".'"'."$url".'"'." style='text-decoration: none;'>" . $lang->get('paginate_btn_last') . " &raquo;</a>";
+			$blk->assign_vars(array(
+				'CLASS'=>$cls,
+				'LINK'=>$link
+				));
+			$inner .= $blk->run();
+		}
+
+	}
+
+	$inner .= '<td class="row2" style="cursor: pointer;" onclick="paginator_goto(this, '.$current_page.', '.$num_pages.', '.$start_mult.', '.$start_add.', unescape(\'' . rawurlencode($result_url) . '\'));">&darr;</td>';
+
+	$paginator = "\n$begin$inner$end\n";
+	return $paginator;
 }
 
 /**
@@ -2536,61 +2536,61 @@
 
 function paginate($q, $tpl_text, $num_results, $result_url, $start = 0, $perpage = 10, $callers = Array(), $header = '', $footer = '')
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  $parser = $template->makeParserText($tpl_text);
-  
-  $num_pages = ceil ( $num_results / $perpage );
-  $out = '';
-  $this_page = ceil ( $start / $perpage );
-  $i = 0;
-  
-  if ( $num_results > 0 )
-  {
-    $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0);
-    $out .= $paginator;
-  }
-
-  $cls = 'row2';
-
-  if ( $row = $db->fetchrow($q) )
-  {
-    $i = 0;
-    $out .= $header;
-    do {
-      $i++;
-      if ( $i <= $start )
-      {
-        continue;
-      }
-      if ( ( $i - $start ) > $perpage )
-      {
-        break;
-      }
-      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-      foreach ( $row as $j => $val )
-      {
-        if ( isset($callers[$j]) )
-        {
-          $tmp = ( is_callable($callers[$j]) ) ? call_user_func($callers[$j], $val, $row) : $val;
-
-          if ( is_string($tmp) )
-          {
-            $row[$j] = $tmp;
-          }
-        }
-      }
-      $parser->assign_vars($row);
-      $parser->assign_vars(array('_css_class' => $cls));
-      $out .= $parser->run();
-    } while ( $row = @$db->fetchrow($q) );
-    $out .= $footer;
-  }
-
-  if ( $num_results > 0 )
-    $out .= $paginator;
-
-  return $out;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	$parser = $template->makeParserText($tpl_text);
+	
+	$num_pages = ceil ( $num_results / $perpage );
+	$out = '';
+	$this_page = ceil ( $start / $perpage );
+	$i = 0;
+	
+	if ( $num_results > 0 )
+	{
+		$paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0);
+		$out .= $paginator;
+	}
+
+	$cls = 'row2';
+
+	if ( $row = $db->fetchrow($q) )
+	{
+		$i = 0;
+		$out .= $header;
+		do {
+			$i++;
+			if ( $i <= $start )
+			{
+				continue;
+			}
+			if ( ( $i - $start ) > $perpage )
+			{
+				break;
+			}
+			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+			foreach ( $row as $j => $val )
+			{
+				if ( isset($callers[$j]) )
+				{
+					$tmp = ( is_callable($callers[$j]) ) ? call_user_func($callers[$j], $val, $row) : $val;
+
+					if ( is_string($tmp) )
+					{
+						$row[$j] = $tmp;
+					}
+				}
+			}
+			$parser->assign_vars($row);
+			$parser->assign_vars(array('_css_class' => $cls));
+			$out .= $parser->run();
+		} while ( $row = @$db->fetchrow($q) );
+		$out .= $footer;
+	}
+
+	if ( $num_results > 0 )
+		$out .= $paginator;
+
+	return $out;
 }
 
 /**
@@ -2607,46 +2607,46 @@
 
 function paginate_array($q, $num_results, $result_url, $start = 0, $perpage = 10, $header = '', $footer = '')
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $num_pages = ceil ( $num_results / $perpage );
-  $out = '';
-  $i = 0;
-  $this_page = ceil ( $start / $perpage );
-
-  $paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0);
-  
-  if ( $num_results > 1 )
-  {
-    $out .= $paginator;
-  }
-
-  $cls = 'row2';
-
-  if ( sizeof($q) > 0 )
-  {
-    $i = 0;
-    $out .= $header;
-    foreach ( $q as $val ) {
-      $i++;
-      if ( $i <= $start )
-      {
-        continue;
-      }
-      if ( ( $i - $start ) > $perpage )
-      {
-        break;
-      }
-      $out .= $val;
-    }
-    $out .= $footer;
-  }
-
-  if ( $num_results > 1 )
-    $out .= $paginator;
-
-  return $out;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$num_pages = ceil ( $num_results / $perpage );
+	$out = '';
+	$i = 0;
+	$this_page = ceil ( $start / $perpage );
+
+	$paginator = generate_paginator($this_page, $num_pages, $result_url, $perpage, 0);
+	
+	if ( $num_results > 1 )
+	{
+		$out .= $paginator;
+	}
+
+	$cls = 'row2';
+
+	if ( sizeof($q) > 0 )
+	{
+		$i = 0;
+		$out .= $header;
+		foreach ( $q as $val ) {
+			$i++;
+			if ( $i <= $start )
+			{
+				continue;
+			}
+			if ( ( $i - $start ) > $perpage )
+			{
+				break;
+			}
+			$out .= $val;
+		}
+		$out .= $footer;
+	}
+
+	if ( $num_results > 1 )
+		$out .= $paginator;
+
+	return $out;
 }
 
 /**
@@ -2655,11 +2655,11 @@
 
 function enano_fputs($socket, $data)
 {
-  // echo '<pre>' . htmlspecialchars($data) . '</pre>';
-  // flush();
-  // ob_flush();
-  // ob_end_flush();
-  return fputs($socket, $data);
+	// echo '<pre>' . htmlspecialchars($data) . '</pre>';
+	// flush();
+	// ob_flush();
+	// ob_end_flush();
+	return fputs($socket, $data);
 }
 
 /**
@@ -2670,62 +2670,62 @@
 
 function sanitize_page_id($page_id)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  if ( isset($paths->nslist['User']) )
-  {
-    if ( preg_match('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', $page_id) )
-    {
-      $ip = preg_replace('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', '', $page_id);
-      if ( is_valid_ip($ip) )
-      {
-        return $page_id;
-      }
-    }
-  }
-  
-  if ( empty($page_id) )
-    return '';
-  
-  // Remove character escapes
-  $page_id = dirtify_page_id($page_id);
-
-  $pid_clean = preg_replace('/[\w\.\/:;\(\)@\[\]=_-]/', 'X', $page_id);
-  $pid_dirty = enano_str_split($pid_clean, 1);
-  
-  foreach ( $pid_dirty as $id => $char )
-  {
-    if ( $char == 'X' )
-      continue;
-    $cid = ord($char);
-    $cid = dechex($cid);
-    $cid = strval($cid);
-    if ( strlen($cid) < 2 )
-    {
-      $cid = strtoupper("0$cid");
-    }
-    $pid_dirty[$id] = ".$cid";
-  }
-
-  $pid_chars = enano_str_split($page_id, 1);
-  $page_id_cleaned = '';
-
-  foreach ( $pid_chars as $id => $char )
-  {
-    if ( $pid_dirty[$id] == 'X' )
-      $page_id_cleaned .= $char;
-    else
-      $page_id_cleaned .= $pid_dirty[$id];
-  }
-  
-  // global $mime_types;
-
-  // $exts = array_keys($mime_types);
-  // $exts = '(' . implode('|', $exts) . ')';
-
-  // $page_id_cleaned = preg_replace('/\.2e' . $exts . '$/', '.\\1', $page_id_cleaned);
-
-  return $page_id_cleaned;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	if ( isset($paths->nslist['User']) )
+	{
+		if ( preg_match('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', $page_id) )
+		{
+			$ip = preg_replace('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', '', $page_id);
+			if ( is_valid_ip($ip) )
+			{
+				return $page_id;
+			}
+		}
+	}
+	
+	if ( empty($page_id) )
+		return '';
+	
+	// Remove character escapes
+	$page_id = dirtify_page_id($page_id);
+
+	$pid_clean = preg_replace('/[\w\.\/:;\(\)@\[\]=_-]/', 'X', $page_id);
+	$pid_dirty = enano_str_split($pid_clean, 1);
+	
+	foreach ( $pid_dirty as $id => $char )
+	{
+		if ( $char == 'X' )
+			continue;
+		$cid = ord($char);
+		$cid = dechex($cid);
+		$cid = strval($cid);
+		if ( strlen($cid) < 2 )
+		{
+			$cid = strtoupper("0$cid");
+		}
+		$pid_dirty[$id] = ".$cid";
+	}
+
+	$pid_chars = enano_str_split($page_id, 1);
+	$page_id_cleaned = '';
+
+	foreach ( $pid_chars as $id => $char )
+	{
+		if ( $pid_dirty[$id] == 'X' )
+			$page_id_cleaned .= $char;
+		else
+			$page_id_cleaned .= $pid_dirty[$id];
+	}
+	
+	// global $mime_types;
+
+	// $exts = array_keys($mime_types);
+	// $exts = '(' . implode('|', $exts) . ')';
+
+	// $page_id_cleaned = preg_replace('/\.2e' . $exts . '$/', '.\\1', $page_id_cleaned);
+
+	return $page_id_cleaned;
 }
 
 /**
@@ -2736,31 +2736,31 @@
 
 function dirtify_page_id($page_id)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  // First, replace spaces with underscores
-  $page_id = str_replace(' ', '_', $page_id);
-
-  // Exception for userpages for IP addresses
-  $pid_ip_check = ( is_object($paths) ) ? preg_replace('+^' . preg_quote($paths->nslist['User']) . '+', '', $page_id) : $page_id;
-  if ( is_valid_ip($pid_ip_check) )
-  {
-    return $page_id;
-  }
-
-  preg_match_all('/\.[a-f0-9][a-f0-9]/', $page_id, $matches);
-
-  foreach ( $matches[0] as $id => $char )
-  {
-    $char = substr($char, 1);
-    $char = strtolower($char);
-    $char = intval(hexdec($char));
-    $char = chr($char);
-    if ( preg_match('/^[\w\.\/:;\(\)@\[\]=_-]$/', $char) )
-      continue;
-    $page_id = str_replace($matches[0][$id], $char, $page_id);
-  }
-  
-  return $page_id;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	// First, replace spaces with underscores
+	$page_id = str_replace(' ', '_', $page_id);
+
+	// Exception for userpages for IP addresses
+	$pid_ip_check = ( is_object($paths) ) ? preg_replace('+^' . preg_quote($paths->nslist['User']) . '+', '', $page_id) : $page_id;
+	if ( is_valid_ip($pid_ip_check) )
+	{
+		return $page_id;
+	}
+
+	preg_match_all('/\.[a-f0-9][a-f0-9]/', $page_id, $matches);
+
+	foreach ( $matches[0] as $id => $char )
+	{
+		$char = substr($char, 1);
+		$char = strtolower($char);
+		$char = intval(hexdec($char));
+		$char = chr($char);
+		if ( preg_match('/^[\w\.\/:;\(\)@\[\]=_-]$/', $char) )
+			continue;
+		$page_id = str_replace($matches[0][$id], $char, $page_id);
+	}
+	
+	return $page_id;
 }
 
 /**
@@ -2771,36 +2771,36 @@
 
 function commatize($num)
 {
-  $num = (string)$num;
-  if ( strpos($num, '.') )
-  {
-    $whole = explode('.', $num);
-    $num = $whole[0];
-    $dec = $whole[1];
-  }
-  else
-  {
-    $whole = $num;
-  }
-  $offset = ( strlen($num) ) % 3;
-  $len = strlen($num);
-  $offset = ( $offset == 0 )
-    ? 3
-    : $offset;
-  for ( $i = $offset; $i < $len; $i=$i+3 )
-  {
-    $num = substr($num, 0, $i) . ',' . substr($num, $i, $len);
-    $len = strlen($num);
-    $i++;
-  }
-  if ( isset($dec) )
-  {
-    return $num . '.' . $dec;
-  }
-  else
-  {
-    return $num;
-  }
+	$num = (string)$num;
+	if ( strpos($num, '.') )
+	{
+		$whole = explode('.', $num);
+		$num = $whole[0];
+		$dec = $whole[1];
+	}
+	else
+	{
+		$whole = $num;
+	}
+	$offset = ( strlen($num) ) % 3;
+	$len = strlen($num);
+	$offset = ( $offset == 0 )
+		? 3
+		: $offset;
+	for ( $i = $offset; $i < $len; $i=$i+3 )
+	{
+		$num = substr($num, 0, $i) . ',' . substr($num, $i, $len);
+		$len = strlen($num);
+		$i++;
+	}
+	if ( isset($dec) )
+	{
+		return $num . '.' . $dec;
+	}
+	else
+	{
+		return $num;
+	}
 }
 
 /**
@@ -2811,25 +2811,25 @@
 
 function humanize_filesize($size)
 {
-  global $lang;
-  
-  if ( $size > ( 1099511627776 * 0.9 ) )
-  {
-    return number_format($size / 1099511627776, 1) . $lang->get('etc_unit_terabytes_short');
-  }
-  if ( $size > ( 1073741824 * 0.9 ) )
-  {
-    return number_format($size / 1073741824, 1) . $lang->get('etc_unit_gigabytes_short');
-  }
-  if ( $size > ( 1048576 * 0.9 ) )
-  {
-    return number_format($size / 1048576, 1) . $lang->get('etc_unit_megabytes_short');
-  }
-  if ( $size > ( 1024 * 0.9 ) )
-  {
-    return number_format($size / 1024, 1) . $lang->get('etc_unit_kilobytes_short');
-  }
-  return "$size " . $lang->get('etc_unit_bytes');
+	global $lang;
+	
+	if ( $size > ( 1099511627776 * 0.9 ) )
+	{
+		return number_format($size / 1099511627776, 1) . $lang->get('etc_unit_terabytes_short');
+	}
+	if ( $size > ( 1073741824 * 0.9 ) )
+	{
+		return number_format($size / 1073741824, 1) . $lang->get('etc_unit_gigabytes_short');
+	}
+	if ( $size > ( 1048576 * 0.9 ) )
+	{
+		return number_format($size / 1048576, 1) . $lang->get('etc_unit_megabytes_short');
+	}
+	if ( $size > ( 1024 * 0.9 ) )
+	{
+		return number_format($size / 1024, 1) . $lang->get('etc_unit_kilobytes_short');
+	}
+	return "$size " . $lang->get('etc_unit_bytes');
 }
 
 /**
@@ -2841,10 +2841,10 @@
 
 function inject_substr($haystack, $needle, $pos)
 {
-  $str1 = substr($haystack, 0, $pos);
-  $pos++;
-  $str2 = substr($haystack, $pos);
-  return "{$str1}{$needle}{$str2}";
+	$str1 = substr($haystack, 0, $pos);
+	$pos++;
+	$str2 = substr($haystack, $pos);
+	return "{$str1}{$needle}{$str2}";
 }
 
 /**
@@ -2855,14 +2855,14 @@
 
 function is_valid_ip($ip)
 {
-  // This next one came from phpBB3.
-  $ipv4 = '(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])';
-  $ipv6 = '(?:[a-f0-9]{0,4}):(?:[a-f0-9]{0,4}):(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{1,4})';
-
-  if ( preg_match("/^{$ipv4}$/", $ip) || preg_match("/^{$ipv6}$/", $ip) )
-    return true;
-  else
-    return false;
+	// This next one came from phpBB3.
+	$ipv4 = '(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])';
+	$ipv6 = '(?:[a-f0-9]{0,4}):(?:[a-f0-9]{0,4}):(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{0,4}:|:)?(?:[a-f0-9]{1,4})';
+
+	if ( preg_match("/^{$ipv4}$/", $ip) || preg_match("/^{$ipv6}$/", $ip) )
+		return true;
+	else
+		return false;
 }
 
 /**
@@ -2874,14 +2874,14 @@
 
 function str_replace_once($needle, $thread, $haystack)
 {
-  $needle_len = strlen($needle);
-  if ( $pos = strstr($haystack, $needle) )
-  {
-    $upto = substr($haystack, 0, ( strlen($haystack) - strlen($pos) ));
-    $from = substr($pos, $needle_len);
-    return "{$upto}{$thread}{$from}";
-  }
-  return $haystack;
+	$needle_len = strlen($needle);
+	if ( $pos = strstr($haystack, $needle) )
+	{
+		$upto = substr($haystack, 0, ( strlen($haystack) - strlen($pos) ));
+		$from = substr($pos, $needle_len);
+		return "{$upto}{$thread}{$from}";
+	}
+	return $haystack;
 }
 
 /**
@@ -2894,22 +2894,22 @@
 
 function str_replace_i($needle, $thread, $haystack)
 {
-  $needle_len = strlen($needle);
-  $haystack_len = strlen($haystack);
-  for ( $i = 0; $i < $haystack_len; $i++ )
-  {
-    $test = substr($haystack, $i, $needle_len);
-    if ( strtolower($test) == strtolower($needle) )
-    {
-      // Got it!
-      $upto = substr($haystack, 0, $i);
-      $from = substr($haystack, ( $i + $needle_len ));
-      $haystack = "{$upto}{$thread}{$from}";
-      $haystack_len = strlen($haystack);
-      $i = $i + strlen($thread);
-    }
-  }
-  return $haystack;
+	$needle_len = strlen($needle);
+	$haystack_len = strlen($haystack);
+	for ( $i = 0; $i < $haystack_len; $i++ )
+	{
+		$test = substr($haystack, $i, $needle_len);
+		if ( strtolower($test) == strtolower($needle) )
+		{
+			// Got it!
+			$upto = substr($haystack, 0, $i);
+			$from = substr($haystack, ( $i + $needle_len ));
+			$haystack = "{$upto}{$thread}{$from}";
+			$haystack_len = strlen($haystack);
+			$i = $i + strlen($thread);
+		}
+	}
+	return $haystack;
 }
 
 /**
@@ -2923,22 +2923,22 @@
 
 function highlight_term($needle, $haystack, $start_tag = '<b>', $end_tag = '</b>')
 {
-  $needle_len = strlen($needle);
-  $haystack_len = strlen($haystack);
-  for ( $i = 0; $i < $haystack_len; $i++ )
-  {
-    $test = substr($haystack, $i, $needle_len);
-    if ( strtolower($test) == strtolower($needle) )
-    {
-      // Got it!
-      $upto = substr($haystack, 0, $i);
-      $from = substr($haystack, ( $i + $needle_len ));
-      $haystack = "{$upto}{$start_tag}{$test}{$end_tag}{$from}";
-      $haystack_len = strlen($haystack);
-      $i = $i + strlen($needle) + strlen($start_tag) + strlen($end_tag);
-    }
-  }
-  return $haystack;
+	$needle_len = strlen($needle);
+	$haystack_len = strlen($haystack);
+	for ( $i = 0; $i < $haystack_len; $i++ )
+	{
+		$test = substr($haystack, $i, $needle_len);
+		if ( strtolower($test) == strtolower($needle) )
+		{
+			// Got it!
+			$upto = substr($haystack, 0, $i);
+			$from = substr($haystack, ( $i + $needle_len ));
+			$haystack = "{$upto}{$start_tag}{$test}{$end_tag}{$from}";
+			$haystack_len = strlen($haystack);
+			$i = $i + strlen($needle) + strlen($start_tag) + strlen($end_tag);
+		}
+	}
+	return $haystack;
 }
 
 /**
@@ -2961,33 +2961,33 @@
  *  - additionalwhere: additional SQL to inject into WHERE clause, in the format of "AND foo = bar"
  * @example Working example of adding users to search results:
  <code>
-  register_search_handler(array(
-      'table' => 'users',
-      'titlecolumn' => 'username',
-      'uniqueid' => 'ns=User;cid={username}',
-      'additionalcolumns' => array('user_id'),
-      'resultnote' => '[Member]',
-      'linkformat' => array(
-          'page_id' => '{username}',
-          'namespace' => 'User'
-        ),
-      'formatcallback' => 'format_user_search_result',
-    ));
-  
-  function format_user_search_result($row)
-  {
-    global $session, $lang;
-    $rankdata = $session->get_user_rank(intval($row['user_id']));
-    $rankspan = '<span style="' . $rankdata['rank_style'] . '">' . $lang->get($rankdata['rank_title']) . '</span>';
-    if ( empty($rankdata['user_title']) )
-    {
-      return $rankspan;
-    }
-    else
-    {
-      return '"' . htmlspecialchars($rankdata['user_title']) . "\" (<b>$rankspan</b>)";
-    }
-  }
+	register_search_handler(array(
+			'table' => 'users',
+			'titlecolumn' => 'username',
+			'uniqueid' => 'ns=User;cid={username}',
+			'additionalcolumns' => array('user_id'),
+			'resultnote' => '[Member]',
+			'linkformat' => array(
+					'page_id' => '{username}',
+					'namespace' => 'User'
+				),
+			'formatcallback' => 'format_user_search_result',
+		));
+	
+	function format_user_search_result($row)
+	{
+		global $session, $lang;
+		$rankdata = $session->get_user_rank(intval($row['user_id']));
+		$rankspan = '<span style="' . $rankdata['rank_style'] . '">' . $lang->get($rankdata['rank_title']) . '</span>';
+		if ( empty($rankdata['user_title']) )
+		{
+			return $rankspan;
+		}
+		else
+		{
+			return '"' . htmlspecialchars($rankdata['user_title']) . "\" (<b>$rankspan</b>)";
+		}
+	}
  </code>
  * @param array Options array - see function documentation
  * @return null
@@ -2998,18 +2998,18 @@
 
 function register_search_handler($options)
 {
-  global $search_handlers;
-  
-  $required = array('table', 'titlecolumn', 'uniqueid', 'linkformat');
-  foreach ( $required as $key )
-  {
-    if ( !isset($options[$key]) )
-    {
-      throw new Exception("Required search handler option '$key' is missing");
-    }
-  }
-  $search_handlers[] = $options;
-  return null;
+	global $search_handlers;
+	
+	$required = array('table', 'titlecolumn', 'uniqueid', 'linkformat');
+	foreach ( $required as $key )
+	{
+		if ( !isset($options[$key]) )
+		{
+			throw new Exception("Required search handler option '$key' is missing");
+		}
+	}
+	$search_handlers[] = $options;
+	return null;
 }
 
 /**
@@ -3020,55 +3020,55 @@
 
 function decode_unicode_url($str)
 {
-  $res = '';
-
-  $i = 0;
-  $max = strlen($str) - 6;
-  while ($i <= $max)
-  {
-    $character = $str[$i];
-    if ($character == '%' && $str[$i + 1] == 'u')
-    {
-      if ( !preg_match('/^([a-f0-9]{2})+$/', substr($str, $i + 2, 4)) )
-      {
-        $res .= substr($str, $i, 6);
-        $i += 6;
-        continue;
-      }
-      
-      $value = hexdec(substr($str, $i + 2, 4));
-      $i += 6;
-
-      if ($value < 0x0080)
-      {
-        // 1 byte: 0xxxxxxx
-        $character = chr($value);
-      }
-      else if ($value < 0x0800)
-      {
-        // 2 bytes: 110xxxxx 10xxxxxx
-        $character =
-            chr((($value & 0x07c0) >> 6) | 0xc0)
-          . chr(($value & 0x3f) | 0x80);
-      }
-      else
-      {
-        // 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx
-        $character =
-            chr((($value & 0xf000) >> 12) | 0xe0)
-          . chr((($value & 0x0fc0) >> 6) | 0x80)
-          . chr(($value & 0x3f) | 0x80);
-      }
-    }
-    else
-    {
-      $i++;
-    }
-
-    $res .= $character;
-  }
-
-  return $res . substr($str, $i);
+	$res = '';
+
+	$i = 0;
+	$max = strlen($str) - 6;
+	while ($i <= $max)
+	{
+		$character = $str[$i];
+		if ($character == '%' && $str[$i + 1] == 'u')
+		{
+			if ( !preg_match('/^([a-f0-9]{2})+$/', substr($str, $i + 2, 4)) )
+			{
+				$res .= substr($str, $i, 6);
+				$i += 6;
+				continue;
+			}
+			
+			$value = hexdec(substr($str, $i + 2, 4));
+			$i += 6;
+
+			if ($value < 0x0080)
+			{
+				// 1 byte: 0xxxxxxx
+				$character = chr($value);
+			}
+			else if ($value < 0x0800)
+			{
+				// 2 bytes: 110xxxxx 10xxxxxx
+				$character =
+						chr((($value & 0x07c0) >> 6) | 0xc0)
+					. chr(($value & 0x3f) | 0x80);
+			}
+			else
+			{
+				// 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx
+				$character =
+						chr((($value & 0xf000) >> 12) | 0xe0)
+					. chr((($value & 0x0fc0) >> 6) | 0x80)
+					. chr(($value & 0x3f) | 0x80);
+			}
+		}
+		else
+		{
+			$i++;
+		}
+
+		$res .= $character;
+	}
+
+	return $res . substr($str, $i);
 }
 
 /**
@@ -3079,18 +3079,18 @@
 
 function decode_unicode_array($array)
 {
-  foreach ( $array as $i => $val )
-  {
-    if ( is_string($val) )
-    {
-      $array[$i] = decode_unicode_url($val);
-    }
-    else if ( is_array($val) )
-    {
-      $array[$i] = decode_unicode_array($val);
-    }
-  }
-  return $array;
+	foreach ( $array as $i => $val )
+	{
+		if ( is_string($val) )
+		{
+			$array[$i] = decode_unicode_url($val);
+		}
+		else if ( is_array($val) )
+		{
+			$array[$i] = decode_unicode_array($val);
+		}
+	}
+	return $array;
 }
 
 /**
@@ -3101,11 +3101,11 @@
 
 function sanitize_tag($tag)
 {
-  $tag = strtolower($tag);
-  $tag = preg_replace('/[^\w @\$%\^&-]+/', '', $tag);
-  $tag = str_replace('_', ' ', $tag);
-  $tag = trim($tag);
-  return $tag;
+	$tag = strtolower($tag);
+	$tag = preg_replace('/[^\w @\$%\^&-]+/', '', $tag);
+	$tag = str_replace('_', ' ', $tag);
+	$tag = trim($tag);
+	return $tag;
 }
 
 /**
@@ -3119,17 +3119,17 @@
 
 function enano_gzencode($data = "", $level = 6, $filename = "", $comments = "")
 {
-  $flags = (empty($comment)? 0 : 16) + (empty($filename)? 0 : 8);
-  $mtime = time();
-  
-  if ( !function_exists('gzdeflate') )
-    return false;
+	$flags = (empty($comment)? 0 : 16) + (empty($filename)? 0 : 8);
+	$mtime = time();
+	
+	if ( !function_exists('gzdeflate') )
+		return false;
  
-  return (pack("C1C1C1C1VC1C1", 0x1f, 0x8b, 8, $flags, $mtime, 2, 0xFF) .
-          (empty($filename) ? "" : $filename . "\0") .
-          (empty($comment) ? "" : $comment . "\0") .
-          gzdeflate($data, $level) .
-          pack("VV", crc32($data), strlen($data)));
+	return (pack("C1C1C1C1VC1C1", 0x1f, 0x8b, 8, $flags, $mtime, 2, 0xFF) .
+					(empty($filename) ? "" : $filename . "\0") .
+					(empty($comment) ? "" : $comment . "\0") .
+					gzdeflate($data, $level) .
+					pack("VV", crc32($data), strlen($data)));
 }
 
 $php_errors = array();
@@ -3142,56 +3142,56 @@
 
 function enano_handle_error($errno, $errstr, $errfile, $errline)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  $er = error_reporting();
-  if ( ! $er & $errno || $er == 0 )
-  {
-    return true;
-  }
-  global $do_gzip, $php_errors;
-  
-  if ( defined('ENANO_DEBUG') )
-  {
-    // turn off gzip and echo out error immediately for debug installs
-    $do_gzip = false;
-  }
-  
-  $error_type = 'error';
-  if ( in_array($errno, array(E_WARNING, E_USER_WARNING)) )
-    $error_type = 'warning';
-  else if ( in_array($errno, array(E_NOTICE, E_USER_NOTICE)) )
-    $error_type = 'notice';
-  
-  if ( @is_object(@$plugins) )
-  {
-    $code = $plugins->setHook('php_error');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-  }
-  
-  // bypass errors in date() and mktime() (Enano has its own code for this anyway)
-  if ( strstr($errstr, "It is not safe to rely on the system's timezone settings. Please use the date.timezone setting, the TZ environment variable or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier.") )
-  {
-    return true;
-  }
-  
-  if ( $do_gzip )
-  {
-    $php_errors[] = array(
-        'num' => $errno,
-        'type' => $error_type,
-        'error' => $errstr,
-        'file' => $errfile,
-        'line' => $errline
-      );
-  }
-  else
-  {
-    echo "[ <b>PHP $error_type:</b> $errstr in <b>$errfile</b>:<b>$errline</b> ]<br />";
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	$er = error_reporting();
+	if ( ! $er & $errno || $er == 0 )
+	{
+		return true;
+	}
+	global $do_gzip, $php_errors;
+	
+	if ( defined('ENANO_DEBUG') )
+	{
+		// turn off gzip and echo out error immediately for debug installs
+		$do_gzip = false;
+	}
+	
+	$error_type = 'error';
+	if ( in_array($errno, array(E_WARNING, E_USER_WARNING)) )
+		$error_type = 'warning';
+	else if ( in_array($errno, array(E_NOTICE, E_USER_NOTICE)) )
+		$error_type = 'notice';
+	
+	if ( @is_object(@$plugins) )
+	{
+		$code = $plugins->setHook('php_error');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+	}
+	
+	// bypass errors in date() and mktime() (Enano has its own code for this anyway)
+	if ( strstr($errstr, "It is not safe to rely on the system's timezone settings. Please use the date.timezone setting, the TZ environment variable or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier.") )
+	{
+		return true;
+	}
+	
+	if ( $do_gzip )
+	{
+		$php_errors[] = array(
+				'num' => $errno,
+				'type' => $error_type,
+				'error' => $errstr,
+				'file' => $errfile,
+				'line' => $errline
+			);
+	}
+	else
+	{
+		echo "[ <b>PHP $error_type:</b> $errstr in <b>$errfile</b>:<b>$errline</b> ]<br />";
+	}
 }
 
 set_error_handler('enano_handle_error');
@@ -3202,46 +3202,46 @@
 
 function gzip_output()
 {
-  global $do_gzip;
-  
-  $gzip_supported = false;
-  if ( isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
-  {
-    $encodings = explode(',', $_SERVER['HTTP_ACCEPT_ENCODING']);
-    $gzip_supported = in_array('gzip', $encodings) || in_array('deflate', $encodings);
-  }
-  
-  //
-  // Compress buffered output if required and send to browser
-  // Sorry, doesn't work in IE. What else is new?
-  //
-  if ( $do_gzip && getConfig('gzip_output', false) == 1 && function_exists('gzdeflate') && !strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && !headers_sent() && $gzip_supported )
-  {
-    $gzip_contents = ob_get_contents();
-    ob_end_clean();
-    
-    global $php_errors;
-    if ( !empty($php_errors) )
-    {
-      $errors = '';
-      foreach ( $php_errors as $error )
-      {
-        $errors .= "[ <b>PHP {$error['type']}:</b> {$error['error']} in <b>{$error['file']}</b>:<b>{$error['line']}</b> ]<br />";
-      }
-      $gzip_contents = str_replace("</body>", "$errors</body>", $gzip_contents);
-    }
-    
-    $return = @enano_gzencode($gzip_contents);
-    if ( $return )
-    {
-      header('Content-encoding: gzip');
-      echo $return;
-    }
-    else
-    {
-      echo $gzip_contents;
-    }
-  }
+	global $do_gzip;
+	
+	$gzip_supported = false;
+	if ( isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
+	{
+		$encodings = explode(',', $_SERVER['HTTP_ACCEPT_ENCODING']);
+		$gzip_supported = in_array('gzip', $encodings) || in_array('deflate', $encodings);
+	}
+	
+	//
+	// Compress buffered output if required and send to browser
+	// Sorry, doesn't work in IE. What else is new?
+	//
+	if ( $do_gzip && getConfig('gzip_output', false) == 1 && function_exists('gzdeflate') && !strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && !headers_sent() && $gzip_supported )
+	{
+		$gzip_contents = ob_get_contents();
+		ob_end_clean();
+		
+		global $php_errors;
+		if ( !empty($php_errors) )
+		{
+			$errors = '';
+			foreach ( $php_errors as $error )
+			{
+				$errors .= "[ <b>PHP {$error['type']}:</b> {$error['error']} in <b>{$error['file']}</b>:<b>{$error['line']}</b> ]<br />";
+			}
+			$gzip_contents = str_replace("</body>", "$errors</body>", $gzip_contents);
+		}
+		
+		$return = @enano_gzencode($gzip_contents);
+		if ( $return )
+		{
+			header('Content-encoding: gzip');
+			echo $return;
+		}
+		else
+		{
+			echo $gzip_contents;
+		}
+	}
 }
 
 /**
@@ -3252,93 +3252,93 @@
 
 function aggressive_optimize_html($html)
 {
-  $size_before = strlen($html);
-  
-  // kill carriage returns
-  $html = str_replace("\r", "", $html);
-  
-  // Which tags to strip for JAVASCRIPT PROCESSING ONLY - you can change this if needed
-  $strip_tags = Array('enano:no-opt');
-  $strip_tags = implode('|', $strip_tags);
-  
-  // Strip out the tags and replace with placeholders
-  preg_match_all("#<($strip_tags)([ ]+.*?)?>(.*?)</($strip_tags)>#is", $html, $matches);
-  $seed = md5(microtime() . mt_rand()); // Random value used for placeholders
-  for ($i = 0;$i < sizeof($matches[1]); $i++)
-  {
-    $html = str_replace($matches[0][$i], "{DONT_STRIP_ME_NAKED:$seed:$i}", $html);
-  }
-  
-  // Optimize (but don't obfuscate) Javascript
-  preg_match_all('/<script([ ]+.*?)?>(.*?)(\]\]>)?<\/script>/is', $html, $jscript);
-  require_once(ENANO_ROOT . '/includes/js-compressor.php');
-  $jsc = new JavascriptCompressor();
-  
-  // list of Javascript reserved words - from about.com
-  $reserved_words = array('abstract', 'as', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'continue', 'const', 'debugger', 'default', 'delete', 'do',
-                          'double', 'else', 'enum', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import',
-                          'in', 'instanceof', 'int', 'interface', 'is', 'long', 'namespace', 'native', 'new', 'null', 'package', 'private', 'protected', 'public',
-                          'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'use', 'var',
-                          'void', 'volatile', 'while', 'with');
-  
-  $reserved_words = '(' . implode('|', $reserved_words) . ')';
-  
-  for ( $i = 0; $i < count($jscript[0]); $i++ )
-  {
-    $js =& $jscript[2][$i];
-    if ( empty($js) )
-      continue;
-    
-    $js = $jsc->getClean($js);
-    
-    $replacement = "<script{$jscript[1][$i]}>/* <![CDATA[ */ $js /* ]]> */</script>";
-    // apply changes
-    $html = str_replace($jscript[0][$i], $replacement, $html);
-     
-  }
-  
-  // Re-insert untouchable tags
-  for ($i = 0;$i < sizeof($matches[1]); $i++)
-  {
-    $html = str_replace("{DONT_STRIP_ME_NAKED:$seed:$i}", "<{$matches[1][$i]}{$matches[2][$i]}>{$matches[3][$i]}</{$matches[4][$i]}>", $html);
-  }
-  
-  // Which tags to strip - you can change this if needed
-  $strip_tags = Array('pre', 'script', 'style', 'enano:no-opt', 'textarea');
-  $strip_tags = implode('|', $strip_tags);
-  
-  // Strip out the tags and replace with placeholders
-  preg_match_all("#<($strip_tags)(.*?)>(.*?)</($strip_tags)>#is", $html, $matches);
-  $seed = md5(microtime() . mt_rand()); // Random value used for placeholders
-  for ($i = 0;$i < sizeof($matches[1]); $i++)
-  {
-    $html = str_replace($matches[0][$i], "{DONT_STRIP_ME_NAKED:$seed:$i}", $html);
-  }
-  
-  // Finally, process the HTML
-  $html = preg_replace("#\n([ ]*)#", " ", $html);
-  
-  // Remove annoying spaces between tags
-  $html = preg_replace("#>([ ][ ]+)<#", "> <", $html);
-  
-  // Re-insert untouchable tags
-  for ($i = 0;$i < sizeof($matches[1]); $i++)
-  {
-    $html = str_replace("{DONT_STRIP_ME_NAKED:$seed:$i}", "<{$matches[1][$i]}{$matches[2][$i]}>{$matches[3][$i]}</{$matches[4][$i]}>", $html);
-  }
-  
-  // Remove <enano:no-opt> blocks (can be used by themes that don't want their HTML optimized)
-  $html = preg_replace('#<(\/|)enano:no-opt(.*?)>#', '', $html);
-  
-  $size_after = strlen($html);
-  
-  // Tell snoopish users what's going on
-  $html = str_replace('<html', "\n".'<!-- NOTE: Enano has performed an HTML optimization routine on the HTML you see here. This is to enhance page loading speeds.
-     To view the uncompressed source of this page, add the "nocompress" parameter to the URI of this page: index.php?title=Main_Page&nocompress or Main_Page?nocompress'."
-     Size before compression: $size_before bytes
-     Size after compression:  $size_after bytes
-     -->\n<html", $html);
-  return $html;
+	$size_before = strlen($html);
+	
+	// kill carriage returns
+	$html = str_replace("\r", "", $html);
+	
+	// Which tags to strip for JAVASCRIPT PROCESSING ONLY - you can change this if needed
+	$strip_tags = Array('enano:no-opt');
+	$strip_tags = implode('|', $strip_tags);
+	
+	// Strip out the tags and replace with placeholders
+	preg_match_all("#<($strip_tags)([ ]+.*?)?>(.*?)</($strip_tags)>#is", $html, $matches);
+	$seed = md5(microtime() . mt_rand()); // Random value used for placeholders
+	for ($i = 0;$i < sizeof($matches[1]); $i++)
+	{
+		$html = str_replace($matches[0][$i], "{DONT_STRIP_ME_NAKED:$seed:$i}", $html);
+	}
+	
+	// Optimize (but don't obfuscate) Javascript
+	preg_match_all('/<script([ ]+.*?)?>(.*?)(\]\]>)?<\/script>/is', $html, $jscript);
+	require_once(ENANO_ROOT . '/includes/js-compressor.php');
+	$jsc = new JavascriptCompressor();
+	
+	// list of Javascript reserved words - from about.com
+	$reserved_words = array('abstract', 'as', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'continue', 'const', 'debugger', 'default', 'delete', 'do',
+													'double', 'else', 'enum', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import',
+													'in', 'instanceof', 'int', 'interface', 'is', 'long', 'namespace', 'native', 'new', 'null', 'package', 'private', 'protected', 'public',
+													'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'use', 'var',
+													'void', 'volatile', 'while', 'with');
+	
+	$reserved_words = '(' . implode('|', $reserved_words) . ')';
+	
+	for ( $i = 0; $i < count($jscript[0]); $i++ )
+	{
+		$js =& $jscript[2][$i];
+		if ( empty($js) )
+			continue;
+		
+		$js = $jsc->getClean($js);
+		
+		$replacement = "<script{$jscript[1][$i]}>/* <![CDATA[ */ $js /* ]]> */</script>";
+		// apply changes
+		$html = str_replace($jscript[0][$i], $replacement, $html);
+ 		
+	}
+	
+	// Re-insert untouchable tags
+	for ($i = 0;$i < sizeof($matches[1]); $i++)
+	{
+		$html = str_replace("{DONT_STRIP_ME_NAKED:$seed:$i}", "<{$matches[1][$i]}{$matches[2][$i]}>{$matches[3][$i]}</{$matches[4][$i]}>", $html);
+	}
+	
+	// Which tags to strip - you can change this if needed
+	$strip_tags = Array('pre', 'script', 'style', 'enano:no-opt', 'textarea');
+	$strip_tags = implode('|', $strip_tags);
+	
+	// Strip out the tags and replace with placeholders
+	preg_match_all("#<($strip_tags)(.*?)>(.*?)</($strip_tags)>#is", $html, $matches);
+	$seed = md5(microtime() . mt_rand()); // Random value used for placeholders
+	for ($i = 0;$i < sizeof($matches[1]); $i++)
+	{
+		$html = str_replace($matches[0][$i], "{DONT_STRIP_ME_NAKED:$seed:$i}", $html);
+	}
+	
+	// Finally, process the HTML
+	$html = preg_replace("#\n([ ]*)#", " ", $html);
+	
+	// Remove annoying spaces between tags
+	$html = preg_replace("#>([ ][ ]+)<#", "> <", $html);
+	
+	// Re-insert untouchable tags
+	for ($i = 0;$i < sizeof($matches[1]); $i++)
+	{
+		$html = str_replace("{DONT_STRIP_ME_NAKED:$seed:$i}", "<{$matches[1][$i]}{$matches[2][$i]}>{$matches[3][$i]}</{$matches[4][$i]}>", $html);
+	}
+	
+	// Remove <enano:no-opt> blocks (can be used by themes that don't want their HTML optimized)
+	$html = preg_replace('#<(\/|)enano:no-opt(.*?)>#', '', $html);
+	
+	$size_after = strlen($html);
+	
+	// Tell snoopish users what's going on
+	$html = str_replace('<html', "\n".'<!-- NOTE: Enano has performed an HTML optimization routine on the HTML you see here. This is to enhance page loading speeds.
+ 		To view the uncompressed source of this page, add the "nocompress" parameter to the URI of this page: index.php?title=Main_Page&nocompress or Main_Page?nocompress'."
+ 		Size before compression: $size_before bytes
+ 		Size after compression:  $size_after bytes
+ 		-->\n<html", $html);
+	return $html;
 }
 
 /**
@@ -3349,23 +3349,23 @@
 
 function int_range($range)
 {
-  if ( strval(intval($range)) == $range )
-    return $range;
-  if ( !preg_match('/^[0-9]+(-[0-9]+)?$/', $range) )
-    return false;
-  $ends = explode('-', $range);
-  if ( count($ends) != 2 )
-    return $range;
-  $ret = array();
-  if ( $ends[1] < $ends[0] )
-    $ends = array($ends[1], $ends[0]);
-  else if ( $ends[0] == $ends[1] )
-    return array($ends[0]);
-  for ( $i = $ends[0]; $i <= $ends[1]; $i++ )
-  {
-    $ret[] = $i;
-  }
-  return $ret;
+	if ( strval(intval($range)) == $range )
+		return $range;
+	if ( !preg_match('/^[0-9]+(-[0-9]+)?$/', $range) )
+		return false;
+	$ends = explode('-', $range);
+	if ( count($ends) != 2 )
+		return $range;
+	$ret = array();
+	if ( $ends[1] < $ends[0] )
+		$ends = array($ends[1], $ends[0]);
+	else if ( $ends[0] == $ends[1] )
+		return array($ends[0]);
+	for ( $i = $ends[0]; $i <= $ends[1]; $i++ )
+	{
+		$ret[] = $i;
+	}
+	return $ret;
 }
 
 /**
@@ -3377,47 +3377,47 @@
 
 function parse_ip_range($range)
 {
-  $octets = explode('.', $range);
-  if ( count($octets) != 4 )
-    // invalid range
-    return $range;
-  $i = 0;
-  $possibilities = array( 0 => array(), 1 => array(), 2 => array(), 3 => array() );
-  foreach ( $octets as $octet )
-  {
-    $existing =& $possibilities[$i];
-    $inner = explode('|', $octet);
-    foreach ( $inner as $bit )
-    {
-      if ( $i >= 2 )
-      {
-        $bits = int_range($bit);
-        if ( $bits === false )
-          return false;
-        else if ( !is_array($bits) )
-          $existing[] = intval($bits);
-        else
-          $existing = array_merge($existing, $bits);
-      }
-      else
-      {
-        $bit = intval($bit);
-        $existing[] = $bit;
-      }
-    }
-    $existing = array_unique($existing);
-    $i++;
-  }
-  $ips = array();
-  
-  // The only way to combine all those possibilities. ;-)
-  foreach ( $possibilities[0] as $oc1 )
-    foreach ( $possibilities[1] as $oc2 )
-      foreach ( $possibilities[2] as $oc3 )
-        foreach ( $possibilities[3] as $oc4 )
-          $ips[] = "$oc1.$oc2.$oc3.$oc4";
-        
-  return $ips;
+	$octets = explode('.', $range);
+	if ( count($octets) != 4 )
+		// invalid range
+		return $range;
+	$i = 0;
+	$possibilities = array( 0 => array(), 1 => array(), 2 => array(), 3 => array() );
+	foreach ( $octets as $octet )
+	{
+		$existing =& $possibilities[$i];
+		$inner = explode('|', $octet);
+		foreach ( $inner as $bit )
+		{
+			if ( $i >= 2 )
+			{
+				$bits = int_range($bit);
+				if ( $bits === false )
+					return false;
+				else if ( !is_array($bits) )
+					$existing[] = intval($bits);
+				else
+					$existing = array_merge($existing, $bits);
+			}
+			else
+			{
+				$bit = intval($bit);
+				$existing[] = $bit;
+			}
+		}
+		$existing = array_unique($existing);
+		$i++;
+	}
+	$ips = array();
+	
+	// The only way to combine all those possibilities. ;-)
+	foreach ( $possibilities[0] as $oc1 )
+		foreach ( $possibilities[1] as $oc2 )
+			foreach ( $possibilities[2] as $oc3 )
+				foreach ( $possibilities[3] as $oc4 )
+					$ips[] = "$oc1.$oc2.$oc3.$oc4";
+				
+	return $ips;
 }
 
 /**
@@ -3428,14 +3428,14 @@
 
 function parse_ip_range_regex($range)
 {
-  if ( strstr($range, ':') )
-  {
-    return parse_ipv6_range_regex($range);
-  }
-  else
-  {
-    return parse_ipv4_range_regex($range);
-  }
+	if ( strstr($range, ':') )
+	{
+		return parse_ipv6_range_regex($range);
+	}
+	else
+	{
+		return parse_ipv4_range_regex($range);
+	}
 }
 
 /**
@@ -3446,62 +3446,62 @@
 
 function parse_ipv4_range_regex($range)
 {
-  // Regular expression to test the range string for validity
-  $regex = '/^(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
-           . '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
-           . '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
-           . '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)$/';
-  if ( !preg_match($regex, $range) )
-  {
-    return false;
-  }
-  $octets = array(0 => array(), 1 => array(), 2 => array(), 3 => array());
-  list($octets[0], $octets[1], $octets[2], $octets[3]) = explode('.', $range);
-  $return = '^';
-  foreach ( $octets as $octet )
-  {
-    // alternatives array
-    $alts = array();
-    if ( strpos($octet, '|') )
-    {
-      $particles = explode('|', $octet);
-    }
-    else
-    {
-      $particles = array($octet);
-    }
-    foreach ( $particles as $atom )
-    {
-      // each $atom will be either
-      if ( strval(intval($atom)) == $atom )
-      {
-        $alts[] = $atom;
-        continue;
-      }
-      else
-      {
-        // it's a range - parse it out
-        $alt2 = int_range($atom);
-        if ( !$alt2 )
-          return false;
-        foreach ( $alt2 as $neutrino )
-          $alts[] = $neutrino;
-      }
-    }
-    $alts = array_unique($alts);
-    $alts = '|' . implode('|', $alts) . '|';
-    // we can further optimize/compress this by weaseling our way into using some character ranges
-    for ( $i = 1; $i <= 25; $i++ )
-    {
-      $alts = str_replace("|{$i}0|{$i}1|{$i}2|{$i}3|{$i}4|{$i}5|{$i}6|{$i}7|{$i}8|{$i}9|", "|{$i}[0-9]|", $alts);
-    }
-    $alts = str_replace("|1|2|3|4|5|6|7|8|9|", "|[1-9]|", $alts);
-    $alts = '(' . substr($alts, 1, -1) . ')';
-    $return .= $alts . '\.';
-  }
-  $return = substr($return, 0, -2);
-  $return .= '$';
-  return $return;
+	// Regular expression to test the range string for validity
+	$regex = '/^(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
+ 					. '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
+ 					. '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)\.'
+ 					. '(([0-9]+(-[0-9]+)?)(\|([0-9]+(-[0-9]+)?))*)$/';
+	if ( !preg_match($regex, $range) )
+	{
+		return false;
+	}
+	$octets = array(0 => array(), 1 => array(), 2 => array(), 3 => array());
+	list($octets[0], $octets[1], $octets[2], $octets[3]) = explode('.', $range);
+	$return = '^';
+	foreach ( $octets as $octet )
+	{
+		// alternatives array
+		$alts = array();
+		if ( strpos($octet, '|') )
+		{
+			$particles = explode('|', $octet);
+		}
+		else
+		{
+			$particles = array($octet);
+		}
+		foreach ( $particles as $atom )
+		{
+			// each $atom will be either
+			if ( strval(intval($atom)) == $atom )
+			{
+				$alts[] = $atom;
+				continue;
+			}
+			else
+			{
+				// it's a range - parse it out
+				$alt2 = int_range($atom);
+				if ( !$alt2 )
+					return false;
+				foreach ( $alt2 as $neutrino )
+					$alts[] = $neutrino;
+			}
+		}
+		$alts = array_unique($alts);
+		$alts = '|' . implode('|', $alts) . '|';
+		// we can further optimize/compress this by weaseling our way into using some character ranges
+		for ( $i = 1; $i <= 25; $i++ )
+		{
+			$alts = str_replace("|{$i}0|{$i}1|{$i}2|{$i}3|{$i}4|{$i}5|{$i}6|{$i}7|{$i}8|{$i}9|", "|{$i}[0-9]|", $alts);
+		}
+		$alts = str_replace("|1|2|3|4|5|6|7|8|9|", "|[1-9]|", $alts);
+		$alts = '(' . substr($alts, 1, -1) . ')';
+		$return .= $alts . '\.';
+	}
+	$return = substr($return, 0, -2);
+	$return .= '$';
+	return $return;
 }
 
 /**
@@ -3512,89 +3512,89 @@
 
 function parse_ipv6_range_regex($range)
 {
-  $range = strtolower(trim($range));
-  $valid = '/^';
-  $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}):';
-  $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}):';
-  $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
-  $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
-  $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
-  $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
-  $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
-  $valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4})$/';
-  if ( !preg_match($valid, $range) )
-    return false;
-  
-  // expand address range.
-  // this takes short ranges like:
-  //   2001:470-471:054-b02b::5-bb
-  // up to:
-  //   2001:0470-0471:0054-b02b:0000:0000:0000:0005-00bb
-  $range = preg_replace('/^:/', '0000:', $range);
-  $range = explode(':', $range);
-  $expanded = '';
-  $size = count($range);
-  foreach ( $range as $byteset )
-  {
-    if ( empty($byteset) )
-    {
-      // ::
-      while ( $size < 9 )
-      {
-        $expanded .= '0000:';
-        $size++;
-      }
-    }
-    else
-    {
-      if ( strstr($byteset, '-') ) 
-      {
-        // this is a range
-        $sides = explode('-', $byteset);
-        foreach ( $sides as &$bytepair )
-        {
-          while ( strlen($bytepair) < 4 )
-          {
-            $bytepair = "0$bytepair";
-          }
-        }
-        $byteset = implode('-', $sides);
-      }
-      else
-      {
-        while ( strlen($byteset) < 4 )
-        {
-          $byteset = "0$byteset";
-        }
-      }
-      $expanded .= "$byteset:";
-    }
-  }
-  $expanded = explode(':', rtrim($expanded, ':'));
-  
-  // ready to dive in and start generating range regexes.
-  // this has to be pretty optimized... we want to end up with regexes like:
-  // range: 54-b12b
-  /*
-  /005[4-9a-f]|
-  00[6-9a-f][0-9a-f]|
-  0[1-9a-f][0-9a-f][0-9a-f]|
-  [1-9a][0-9a-f][0-9a-f][0-9a-f]|
-  b[0-0][0-1][0-9a-f]|
-  b0[0-1][0-9a-f]|
-  b02[0-9a-b]/x
-  */
-  foreach ( $expanded as &$word )
-  {
-    if ( strstr($word, '-') )
-    {
-      // oh... damn.
-      $word = '(?:' . generate_hex_numeral_range($word) . ')';
-    }
-  }
-
-  // return print_r($expanded, true);  
-  return '^' . implode(':', $expanded) . '$';
+	$range = strtolower(trim($range));
+	$valid = '/^';
+	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}):';
+	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}):';
+	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
+	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
+	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
+	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
+	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4}:|:)?';
+	$valid .= '(?:[0-9a-f]{0,4}|[0-9a-f]{1,4}-[0-9a-f]{1,4})$/';
+	if ( !preg_match($valid, $range) )
+		return false;
+	
+	// expand address range.
+	// this takes short ranges like:
+	//   2001:470-471:054-b02b::5-bb
+	// up to:
+	//   2001:0470-0471:0054-b02b:0000:0000:0000:0005-00bb
+	$range = preg_replace('/^:/', '0000:', $range);
+	$range = explode(':', $range);
+	$expanded = '';
+	$size = count($range);
+	foreach ( $range as $byteset )
+	{
+		if ( empty($byteset) )
+		{
+			// ::
+			while ( $size < 9 )
+			{
+				$expanded .= '0000:';
+				$size++;
+			}
+		}
+		else
+		{
+			if ( strstr($byteset, '-') ) 
+			{
+				// this is a range
+				$sides = explode('-', $byteset);
+				foreach ( $sides as &$bytepair )
+				{
+					while ( strlen($bytepair) < 4 )
+					{
+						$bytepair = "0$bytepair";
+					}
+				}
+				$byteset = implode('-', $sides);
+			}
+			else
+			{
+				while ( strlen($byteset) < 4 )
+				{
+					$byteset = "0$byteset";
+				}
+			}
+			$expanded .= "$byteset:";
+		}
+	}
+	$expanded = explode(':', rtrim($expanded, ':'));
+	
+	// ready to dive in and start generating range regexes.
+	// this has to be pretty optimized... we want to end up with regexes like:
+	// range: 54-b12b
+	/*
+	/005[4-9a-f]|
+	00[6-9a-f][0-9a-f]|
+	0[1-9a-f][0-9a-f][0-9a-f]|
+	[1-9a][0-9a-f][0-9a-f][0-9a-f]|
+	b[0-0][0-1][0-9a-f]|
+	b0[0-1][0-9a-f]|
+	b02[0-9a-b]/x
+	*/
+	foreach ( $expanded as &$word )
+	{
+		if ( strstr($word, '-') )
+		{
+			// oh... damn.
+			$word = '(?:' . generate_hex_numeral_range($word) . ')';
+		}
+	}
+
+	// return print_r($expanded, true);  
+	return '^' . implode(':', $expanded) . '$';
 }
 
 /**
@@ -3606,152 +3606,152 @@
 
 function generate_hex_numeral_range($word)
 {
-  list($low, $high) = explode('-', $word);
-  
-  if ( hexdec($low) > hexdec($high) )
-  {
-    $_ = $low;
-    $low = $high;
-    $high = $_;
-    unset($_);
-  }
-  
-  while ( strlen($low) < strlen($high) )
-  {
-    $low = "0$low";
-  }
-  
-  // trim off everything that's the same
-  $trimmed = '';
-  $len = strlen($low);
-  for ( $i = 0; $i < $len; $i++ )
-  {
-    if ( $low{0} === $high{0} )
-    {
-      $trimmed .= $low{0};
-      $low = substr($low, 1);
-      $high = substr($high, 1);
-    }
-    else
-    {
-      break;
-    }
-  }
-  
-  $len = strlen($high);
-  if ( $len == 1 )
-  {
-    // this does happen sometimes, so we can save a bit of CPU power here.
-    return $trimmed . __hexdigitrange($low, $high);
-  }
-    
-  $return = '';
-  // lower half
-  for ( $i = $len - 1; $i > 0; $i-- )
-  {
-    if ( $low{$i} == 'f' )
-      continue;
-    $return .= $trimmed;
-    for ( $j = 0; $j < $len; $j++ )
-    {
-      if ( $j < $i )
-      {
-        $return .= $low{$j};
-      }
-      else if ( $j == $i && ( $i == $len - 1 || $low{$j} == 'f' ) )
-      {
-        $return .= __hexdigitrange($low{$j}, 'f');
-      }
-      else if ( $j == $i && $i != $len - 1 )
-      {
-        $return .= __hexdigitrange(dechex(hexdec($low{$j}) + 1), 'f');
-      }
-      else
-      {
-        $return .= __hexdigitrange('0', 'f');
-      }
-    }
-    $return .= '|';
-  }
-  // middle block
-  if ( hexdec($low{0}) + 1 < hexdec($high{0}) )
-  {
-    if ( hexdec($low{0}) + 1 < hexdec($high{0}) - 1 )
-      $return .= $trimmed . __hexdigitrange(dechex(hexdec($low{0}) + 1), dechex(hexdec($high{0}) - 1));
-    else
-      $return .= $trimmed . __hexdigitrange($low{0}, $high{0});
-    if ( $len - 1 > 0 )
-      $return .= '[0-9a-f]{' . ( $len - 1 ) . '}|';
-  }
-  // higher half
-  for ( $i = 1; $i < $len; $i++ )
-  {
-    if ( $high{$i} == '0' )
-      continue;
-    $return .= $trimmed;
-    for ( $j = 0; $j < $len; $j++ )
-    {
-      if ( $j < $i )
-      {
-        $return .= $high{$j};
-      }
-      else if ( $j == $i && ( $i == $len - 1 || $high{$j} == '0' ) )
-      {
-        $return .= __hexdigitrange('0', $high{$j});
-      }
-      else if ( $j == $i && $i != $len - 1 )
-      {
-        $return .= __hexdigitrange('0', dechex(hexdec($high{$j}) - 1));
-      }
-      else if ( $j > $i )
-      {
-        $return .= __hexdigitrange('0', 'f');
-      }
-      else
-      {
-        die("I don't know what to do! i $i j $j");
-      }
-    }
-    $return .= '|';
-  }
-  
-  return rtrim($return, '|');
+	list($low, $high) = explode('-', $word);
+	
+	if ( hexdec($low) > hexdec($high) )
+	{
+		$_ = $low;
+		$low = $high;
+		$high = $_;
+		unset($_);
+	}
+	
+	while ( strlen($low) < strlen($high) )
+	{
+		$low = "0$low";
+	}
+	
+	// trim off everything that's the same
+	$trimmed = '';
+	$len = strlen($low);
+	for ( $i = 0; $i < $len; $i++ )
+	{
+		if ( $low{0} === $high{0} )
+		{
+			$trimmed .= $low{0};
+			$low = substr($low, 1);
+			$high = substr($high, 1);
+		}
+		else
+		{
+			break;
+		}
+	}
+	
+	$len = strlen($high);
+	if ( $len == 1 )
+	{
+		// this does happen sometimes, so we can save a bit of CPU power here.
+		return $trimmed . __hexdigitrange($low, $high);
+	}
+		
+	$return = '';
+	// lower half
+	for ( $i = $len - 1; $i > 0; $i-- )
+	{
+		if ( $low{$i} == 'f' )
+			continue;
+		$return .= $trimmed;
+		for ( $j = 0; $j < $len; $j++ )
+		{
+			if ( $j < $i )
+			{
+				$return .= $low{$j};
+			}
+			else if ( $j == $i && ( $i == $len - 1 || $low{$j} == 'f' ) )
+			{
+				$return .= __hexdigitrange($low{$j}, 'f');
+			}
+			else if ( $j == $i && $i != $len - 1 )
+			{
+				$return .= __hexdigitrange(dechex(hexdec($low{$j}) + 1), 'f');
+			}
+			else
+			{
+				$return .= __hexdigitrange('0', 'f');
+			}
+		}
+		$return .= '|';
+	}
+	// middle block
+	if ( hexdec($low{0}) + 1 < hexdec($high{0}) )
+	{
+		if ( hexdec($low{0}) + 1 < hexdec($high{0}) - 1 )
+			$return .= $trimmed . __hexdigitrange(dechex(hexdec($low{0}) + 1), dechex(hexdec($high{0}) - 1));
+		else
+			$return .= $trimmed . __hexdigitrange($low{0}, $high{0});
+		if ( $len - 1 > 0 )
+			$return .= '[0-9a-f]{' . ( $len - 1 ) . '}|';
+	}
+	// higher half
+	for ( $i = 1; $i < $len; $i++ )
+	{
+		if ( $high{$i} == '0' )
+			continue;
+		$return .= $trimmed;
+		for ( $j = 0; $j < $len; $j++ )
+		{
+			if ( $j < $i )
+			{
+				$return .= $high{$j};
+			}
+			else if ( $j == $i && ( $i == $len - 1 || $high{$j} == '0' ) )
+			{
+				$return .= __hexdigitrange('0', $high{$j});
+			}
+			else if ( $j == $i && $i != $len - 1 )
+			{
+				$return .= __hexdigitrange('0', dechex(hexdec($high{$j}) - 1));
+			}
+			else if ( $j > $i )
+			{
+				$return .= __hexdigitrange('0', 'f');
+			}
+			else
+			{
+				die("I don't know what to do! i $i j $j");
+			}
+		}
+		$return .= '|';
+	}
+	
+	return rtrim($return, '|');
 }
 
 function __hexdigitrange($low, $high)
 {
-  if ( $low == $high )
-    return $low;
-  if ( empty($low) )
-    $low = '0';
-  
-  $low_type = ( preg_match('/[0-9]/', $low) ) ? 'num' : 'alph';
-  $high_type = ( preg_match('/[0-9]/', $high) ) ? 'num' : 'alph';
-  if ( ( $low_type == 'num' && $high_type == 'num') || ( $low_type == 'alph' && $high_type == 'alph' ) )
-  {
-    return "[$low-$high]";
-  }
-  else if ( $low_type == 'num' && $high_type == 'alph' )
-  {
-    $ret = '[';
-    
-    if ( $low == '9' )
-      $ret .= '9';
-    else
-      $ret .= "$low-9";
-    if ( $high == 'a' )
-      $ret .= 'a';
-    else
-      $ret .= "a-$high";
-      
-    $ret .= "]";
-    return $ret;
-  }
-  else if ( $low_type == 'alph' && $high_type == 'num' )
-  {
-    // ???? this should never happen
-    return __hexdigitrange($high, $low); 
-  }
+	if ( $low == $high )
+		return $low;
+	if ( empty($low) )
+		$low = '0';
+	
+	$low_type = ( preg_match('/[0-9]/', $low) ) ? 'num' : 'alph';
+	$high_type = ( preg_match('/[0-9]/', $high) ) ? 'num' : 'alph';
+	if ( ( $low_type == 'num' && $high_type == 'num') || ( $low_type == 'alph' && $high_type == 'alph' ) )
+	{
+		return "[$low-$high]";
+	}
+	else if ( $low_type == 'num' && $high_type == 'alph' )
+	{
+		$ret = '[';
+		
+		if ( $low == '9' )
+			$ret .= '9';
+		else
+			$ret .= "$low-9";
+		if ( $high == 'a' )
+			$ret .= 'a';
+		else
+			$ret .= "a-$high";
+			
+		$ret .= "]";
+		return $ret;
+	}
+	else if ( $low_type == 'alph' && $high_type == 'num' )
+	{
+		// ???? this should never happen
+		return __hexdigitrange($high, $low); 
+	}
 }
 
 /**
@@ -3762,26 +3762,26 @@
 
 function expand_ipv6_address($addr)
 {
-  $expanded = array();
-  $addr = explode(':', $addr);
-  foreach ( $addr as $i => $bytepair )
-  {
-    if ( empty($bytepair) )
-    {
-      // ::
-      while ( count($expanded) < (8 - count($addr) + $i + 1) )
-      {
-        $expanded[] = '0000';
-      }
-    }
-    else
-    {
-      while ( strlen($bytepair) < 4 )
-        $bytepair = "0$bytepair";
-      $expanded[] = $bytepair;
-    }
-  }
-  return implode(':', $expanded);
+	$expanded = array();
+	$addr = explode(':', $addr);
+	foreach ( $addr as $i => $bytepair )
+	{
+		if ( empty($bytepair) )
+		{
+			// ::
+			while ( count($expanded) < (8 - count($addr) + $i + 1) )
+			{
+				$expanded[] = '0000';
+			}
+		}
+		else
+		{
+			while ( strlen($bytepair) < 4 )
+				$bytepair = "0$bytepair";
+			$expanded[] = $bytepair;
+		}
+	}
+	return implode(':', $expanded);
 }
 
 /**
@@ -3792,19 +3792,19 @@
 
 function check_email_address($email)
 {
-  static $regexp = '(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)';
-  return ( preg_match("/^$regexp$/", $email) ) ? true : false;
+	static $regexp = '(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*>)';
+	return ( preg_match("/^$regexp$/", $email) ) ? true : false;
 }
 
 function password_score_len($password)
 {
-  if ( !is_string($password) )
-  {
-    return -10;
-  }
-  $len = strlen($password);
-  $score = $len - 7;
-  return $score;
+	if ( !is_string($password) )
+	{
+		return -10;
+	}
+	$len = strlen($password);
+	$score = $len - 7;
+	return $score;
 }
 
 /**
@@ -3818,159 +3818,159 @@
 
 function password_score($password, &$debug = false)
 {
-  if ( !is_string($password) )
-  {
-    return -10;
-  }
-  $score = 0;
-  $debug = array();
-  // length check
-  $lenscore = password_score_len($password);
-  
-  $debug[] = "<b>How this score was calculated</b>\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n";
-  
-  $debug[] = 'Adding '.$lenscore.' points for length';
-  
-  $score += $lenscore;
-    
-  $has_upper_lower = false;
-  $has_symbols     = false;
-  $has_numbers     = false;
-  
-  // contains uppercase and lowercase
-  if ( preg_match('/[A-z]+/', $password) && strtolower($password) != $password )
-  {
-    $score += 1;
-    $has_upper_lower = true;
-    $debug[] = 'Adding 1 point for having uppercase and lowercase';
-  }
-  
-  // contains symbols
-  if ( preg_match('/[^A-z0-9]+/', $password) )
-  {
-    $score += 1;
-    $has_symbols = true;
-    $debug[] = 'Adding 1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)';
-  }
-  
-  // contains numbers
-  if ( preg_match('/[0-9]+/', $password) )
-  {
-    $score += 1;
-    $has_numbers = true;
-    $debug[] = 'Adding 1 point for having numbers';
-  }
-  
-  if ( $has_upper_lower && $has_symbols && $has_numbers && strlen($password) >= 9 )
-  {
-    // if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points
-    $score += 4;
-    $debug[] = 'Adding 4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters';
-  }
-  else if ( $has_upper_lower && $has_symbols && $has_numbers )
-  {
-    // still give some points for passing complexity check
-    $score += 2;
-    $debug[] = 'Adding 2 points for having uppercase and lowercase, numbers, and nonalphanumeric';
-  }
-  else if ( ( $has_upper_lower && $has_symbols ) ||
-            ( $has_upper_lower && $has_numbers ) ||
-            ( $has_symbols && $has_numbers ) )
-  {
-    // if 2 of the three main complexity checks passed, add a point
-    $score += 1;
-    $debug[] = 'Adding 1 point for having 2 of 3 complexity checks';
-  }
-  else if ( preg_match('/^[0-9]*?([a-z]+)[0-9]?$/', $password) )
-  {
-    // password is something like magnum1 which will be cracked in seconds
-    $score += -4;
-    $debug[] = 'Adding -4 points for being of the form [number][word][number]';
-  }
-  else if ( ( !$has_upper_lower && !$has_numbers && $has_symbols ) ||
-            ( !$has_upper_lower && !$has_symbols && $has_numbers ) ||
-            ( !$has_numbers && !$has_symbols && $has_upper_lower ) )
-  {
-    $score += -2;
-    $debug[] = 'Adding -2 points for only meeting 1 complexity check';
-  }
-  else if ( !$has_upper_lower && !$has_numbers && !$has_symbols )
-  {
-    $debug[] = 'Adding -3 points for not meeting any complexity checks';
-    $score += -3;
-  }
-  
-  //
-  // Repetition
-  // Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points
-  //
-  
-  if ( preg_match('/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/', $password) )
-  {
-    $debug[] = 'Adding -2 points for having more than 4 letters of the same case in a row';
-    $score += -2;
-  }
-  else if ( preg_match('/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/', $password) )
-  {
-    $debug[] = 'Adding -1 points for having more than 3 letters of the same case in a row';
-    $score += -1;
-  }
-  else if ( preg_match('/[A-z]/', $password) && !preg_match('/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/', $password) )
-  {
-    $debug[] = 'Adding 1 point for never having more than 2 letters of the same case in a row';
-    $score += 1;
-  }
-  
-  if ( preg_match('/[0-9][0-9][0-9][0-9]/', $password) )
-  {
-    $debug[] = 'Adding -2 points for having 4 or more numbers in a row';
-    $score += -2;
-  }
-  else if ( preg_match('/[0-9][0-9][0-9]/', $password) )
-  {
-    $debug[] = 'Adding -1 points for having 3 or more numbers in a row';
-    $score += -1;
-  }
-  else if ( $has_numbers && !preg_match('/[0-9][0-9][0-9]/', $password) )
-  {
-    $debug[] = 'Adding 1 point for never more than 2 numbers in a row';
-    $score += -1;
-  }
-  
-  // make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row
-  $prev_char = '';
-  $warn = false;
-  $loss = 0;
-  for ( $i = 0; $i < strlen($password); $i++ )
-  {
-    $chr = $password{$i};
-    if ( $chr == $prev_char && $warn )
-    {
-      $loss += -1;
-    }
-    else if ( $chr == $prev_char && !$warn )
-    {
-      $warn = true;
-    }
-    else if ( $chr != $prev_char && $warn )
-    {
-      $warn = false;
-    }
-    $prev_char = $chr;
-  }
-  if ( $loss < 0 )
-  {
-    $debug[] = 'Adding '.$loss.' points for immediate character repetition';
-    $score += $loss;
-    // this can bring the score below -10 sometimes
-    if ( $score < -10 )
-    {
-      $debug[] = 'Setting score to -10 because it went below ('.$score.')';
-      $score = -10;
-    }
-  }
-  
-  return $score;
+	if ( !is_string($password) )
+	{
+		return -10;
+	}
+	$score = 0;
+	$debug = array();
+	// length check
+	$lenscore = password_score_len($password);
+	
+	$debug[] = "<b>How this score was calculated</b>\nYour score was tallied up based on an extensive algorithm which outputted\nthe following scores based on traits of your password. Above you can see the\ncomposite score; your individual scores based on certain tests are below.\n\nThe scale is open-ended, with a minimum score of -10. 10 is very strong, 4\nis strong, 1 is good and -3 is fair. Below -3 scores \"Weak.\"\n";
+	
+	$debug[] = 'Adding '.$lenscore.' points for length';
+	
+	$score += $lenscore;
+		
+	$has_upper_lower = false;
+	$has_symbols     = false;
+	$has_numbers     = false;
+	
+	// contains uppercase and lowercase
+	if ( preg_match('/[A-z]+/', $password) && strtolower($password) != $password )
+	{
+		$score += 1;
+		$has_upper_lower = true;
+		$debug[] = 'Adding 1 point for having uppercase and lowercase';
+	}
+	
+	// contains symbols
+	if ( preg_match('/[^A-z0-9]+/', $password) )
+	{
+		$score += 1;
+		$has_symbols = true;
+		$debug[] = 'Adding 1 point for having nonalphanumeric characters (matching /[^A-z0-9]+/)';
+	}
+	
+	// contains numbers
+	if ( preg_match('/[0-9]+/', $password) )
+	{
+		$score += 1;
+		$has_numbers = true;
+		$debug[] = 'Adding 1 point for having numbers';
+	}
+	
+	if ( $has_upper_lower && $has_symbols && $has_numbers && strlen($password) >= 9 )
+	{
+		// if it has uppercase and lowercase letters, symbols, and numbers, and is of considerable length, add some serious points
+		$score += 4;
+		$debug[] = 'Adding 4 points for having uppercase and lowercase, numbers, and nonalphanumeric and being more than 8 characters';
+	}
+	else if ( $has_upper_lower && $has_symbols && $has_numbers )
+	{
+		// still give some points for passing complexity check
+		$score += 2;
+		$debug[] = 'Adding 2 points for having uppercase and lowercase, numbers, and nonalphanumeric';
+	}
+	else if ( ( $has_upper_lower && $has_symbols ) ||
+						( $has_upper_lower && $has_numbers ) ||
+						( $has_symbols && $has_numbers ) )
+	{
+		// if 2 of the three main complexity checks passed, add a point
+		$score += 1;
+		$debug[] = 'Adding 1 point for having 2 of 3 complexity checks';
+	}
+	else if ( preg_match('/^[0-9]*?([a-z]+)[0-9]?$/', $password) )
+	{
+		// password is something like magnum1 which will be cracked in seconds
+		$score += -4;
+		$debug[] = 'Adding -4 points for being of the form [number][word][number]';
+	}
+	else if ( ( !$has_upper_lower && !$has_numbers && $has_symbols ) ||
+						( !$has_upper_lower && !$has_symbols && $has_numbers ) ||
+						( !$has_numbers && !$has_symbols && $has_upper_lower ) )
+	{
+		$score += -2;
+		$debug[] = 'Adding -2 points for only meeting 1 complexity check';
+	}
+	else if ( !$has_upper_lower && !$has_numbers && !$has_symbols )
+	{
+		$debug[] = 'Adding -3 points for not meeting any complexity checks';
+		$score += -3;
+	}
+	
+	//
+	// Repetition
+	// Example: foobar12345 should be deducted points, where f1o2o3b4a5r should be given points
+	//
+	
+	if ( preg_match('/([A-Z][A-Z][A-Z][A-Z]|[a-z][a-z][a-z][a-z])/', $password) )
+	{
+		$debug[] = 'Adding -2 points for having more than 4 letters of the same case in a row';
+		$score += -2;
+	}
+	else if ( preg_match('/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/', $password) )
+	{
+		$debug[] = 'Adding -1 points for having more than 3 letters of the same case in a row';
+		$score += -1;
+	}
+	else if ( preg_match('/[A-z]/', $password) && !preg_match('/([A-Z][A-Z][A-Z]|[a-z][a-z][a-z])/', $password) )
+	{
+		$debug[] = 'Adding 1 point for never having more than 2 letters of the same case in a row';
+		$score += 1;
+	}
+	
+	if ( preg_match('/[0-9][0-9][0-9][0-9]/', $password) )
+	{
+		$debug[] = 'Adding -2 points for having 4 or more numbers in a row';
+		$score += -2;
+	}
+	else if ( preg_match('/[0-9][0-9][0-9]/', $password) )
+	{
+		$debug[] = 'Adding -1 points for having 3 or more numbers in a row';
+		$score += -1;
+	}
+	else if ( $has_numbers && !preg_match('/[0-9][0-9][0-9]/', $password) )
+	{
+		$debug[] = 'Adding 1 point for never more than 2 numbers in a row';
+		$score += -1;
+	}
+	
+	// make passwords like fooooooooooooooooooooooooooooooooooooo totally die by subtracting a point for each character repeated at least 3 times in a row
+	$prev_char = '';
+	$warn = false;
+	$loss = 0;
+	for ( $i = 0; $i < strlen($password); $i++ )
+	{
+		$chr = $password{$i};
+		if ( $chr == $prev_char && $warn )
+		{
+			$loss += -1;
+		}
+		else if ( $chr == $prev_char && !$warn )
+		{
+			$warn = true;
+		}
+		else if ( $chr != $prev_char && $warn )
+		{
+			$warn = false;
+		}
+		$prev_char = $chr;
+	}
+	if ( $loss < 0 )
+	{
+		$debug[] = 'Adding '.$loss.' points for immediate character repetition';
+		$score += $loss;
+		// this can bring the score below -10 sometimes
+		if ( $score < -10 )
+		{
+			$debug[] = 'Setting score to -10 because it went below ('.$score.')';
+			$score = -10;
+		}
+	}
+	
+	return $score;
 }
 
 /**
@@ -3981,11 +3981,11 @@
 
 function register_cron_task($func, $hour_interval = 24)
 {
-  global $cron_tasks;
-  $hour_interval = strval($hour_interval);
-  if ( !isset($cron_tasks[$hour_interval]) )
-    $cron_tasks[$hour_interval] = array();
-  $cron_tasks[$hour_interval][] = $func;
+	global $cron_tasks;
+	$hour_interval = strval($hour_interval);
+	if ( !isset($cron_tasks[$hour_interval]) )
+		$cron_tasks[$hour_interval] = array();
+	$cron_tasks[$hour_interval][] = $func;
 }
 
 /**
@@ -3995,10 +3995,10 @@
 
 function get_cron_next_run()
 {
-  global $cron_tasks;
-  $lowest_ivl = min(array_keys($cron_tasks));
-  $last_run = intval(getConfig("cron_lastrun_ivl_$lowest_ivl"));
-  return intval($last_run + ( 3600 * $lowest_ivl )) - 30;
+	global $cron_tasks;
+	$lowest_ivl = min(array_keys($cron_tasks));
+	$last_run = intval(getConfig("cron_lastrun_ivl_$lowest_ivl"));
+	return intval($last_run + ( 3600 * $lowest_ivl )) - 30;
 }
 
 /**
@@ -4011,53 +4011,53 @@
 
 function install_language($lang_code, $lang_name_neutral, $lang_name_local, $lang_file = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  $q = $db->sql_query('SELECT 1 FROM '.table_prefix.'language WHERE lang_code = \'' . $db->escape($lang_code) . '\';');
-  if ( !$q )
-    $db->_die('functions.php - checking for language existence');
-  
-  if ( $db->numrows() > 0 )
-    // Language already exists
-    return false;
-  
-  $q = $db->sql_query('INSERT INTO ' . table_prefix . 'language(lang_code, lang_name_default, lang_name_native) 
-                         VALUES(
-                           \'' . $db->escape($lang_code) . '\',
-                           \'' . $db->escape($lang_name_neutral) . '\',
-                           \'' . $db->escape($lang_name_local) . '\'
-                         );');
-  if ( !$q )
-    $db->_die('functions.php - installing language');
-  
-  if ( ENANO_DBLAYER == 'PGSQL' )
-  {
-    // exception for Postgres, which doesn't support insert IDs
-    // This will cause the Language class to just load by lang code
-    // instead of by numeric ID
-    $lang_id = $lang_code;
-  }
-  else
-  {
-    $lang_id = $db->insert_id();
-    if ( empty($lang_id) || $lang_id == 0 )
-    {
-      $db->_die('functions.php - invalid returned lang_id');
-    }
-  }
-  
-  // Do we also need to install a language file?
-  if ( is_string($lang_file) && file_exists($lang_file) )
-  {
-    $lang = new Language($lang_id);
-    $lang->import($lang_file);
-  }
-  else if ( is_string($lang_file) && !file_exists($lang_file) )
-  {
-    echo '<b>Notice:</b> Can\'t load language file, so the specified language wasn\'t fully installed.<br />';
-    return false;
-  }
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	$q = $db->sql_query('SELECT 1 FROM '.table_prefix.'language WHERE lang_code = \'' . $db->escape($lang_code) . '\';');
+	if ( !$q )
+		$db->_die('functions.php - checking for language existence');
+	
+	if ( $db->numrows() > 0 )
+		// Language already exists
+		return false;
+	
+	$q = $db->sql_query('INSERT INTO ' . table_prefix . 'language(lang_code, lang_name_default, lang_name_native) 
+ 												VALUES(
+ 													\'' . $db->escape($lang_code) . '\',
+ 													\'' . $db->escape($lang_name_neutral) . '\',
+ 													\'' . $db->escape($lang_name_local) . '\'
+ 												);');
+	if ( !$q )
+		$db->_die('functions.php - installing language');
+	
+	if ( ENANO_DBLAYER == 'PGSQL' )
+	{
+		// exception for Postgres, which doesn't support insert IDs
+		// This will cause the Language class to just load by lang code
+		// instead of by numeric ID
+		$lang_id = $lang_code;
+	}
+	else
+	{
+		$lang_id = $db->insert_id();
+		if ( empty($lang_id) || $lang_id == 0 )
+		{
+			$db->_die('functions.php - invalid returned lang_id');
+		}
+	}
+	
+	// Do we also need to install a language file?
+	if ( is_string($lang_file) && file_exists($lang_file) )
+	{
+		$lang = new Language($lang_id);
+		$lang->import($lang_file);
+	}
+	else if ( is_string($lang_file) && !file_exists($lang_file) )
+	{
+		echo '<b>Notice:</b> Can\'t load language file, so the specified language wasn\'t fully installed.<br />';
+		return false;
+	}
+	return true;
 }
 
 /**
@@ -4067,43 +4067,43 @@
 
 function list_available_languages()
 {
-  // Pulled from install/includes/common.php
-  
-  // Build a list of available languages
-  $dir = @opendir( ENANO_ROOT . '/language' );
-  if ( !$dir )
-    die('CRITICAL: could not open language directory');
-  
-  $languages = array();
-  
-  while ( $dh = @readdir($dir) )
-  {
-    if ( $dh == '.' || $dh == '..' )
-      continue;
-    if ( file_exists( ENANO_ROOT . "/language/$dh/meta.json" ) )
-    {
-      // Found a language directory, determine metadata
-      $meta = @file_get_contents( ENANO_ROOT . "/language/$dh/meta.json" );
-      if ( empty($meta) )
-        // Could not read metadata file, continue silently
-        continue;
-        
-      // Do some syntax correction on the metadata
-      $meta = enano_clean_json($meta);
-        
-      $meta = enano_json_decode($meta);
-      if ( isset($meta['lang_name_english']) && isset($meta['lang_name_native']) && isset($meta['lang_code']) )
-      {
-        $languages[$meta['lang_code']] = array(
-            'name' => $meta['lang_name_native'],
-            'name_eng' => $meta['lang_name_english'],
-            'dir' => $dh
-          );
-      }
-    }
-  }
-  
-  return $languages;
+	// Pulled from install/includes/common.php
+	
+	// Build a list of available languages
+	$dir = @opendir( ENANO_ROOT . '/language' );
+	if ( !$dir )
+		die('CRITICAL: could not open language directory');
+	
+	$languages = array();
+	
+	while ( $dh = @readdir($dir) )
+	{
+		if ( $dh == '.' || $dh == '..' )
+			continue;
+		if ( file_exists( ENANO_ROOT . "/language/$dh/meta.json" ) )
+		{
+			// Found a language directory, determine metadata
+			$meta = @file_get_contents( ENANO_ROOT . "/language/$dh/meta.json" );
+			if ( empty($meta) )
+				// Could not read metadata file, continue silently
+				continue;
+				
+			// Do some syntax correction on the metadata
+			$meta = enano_clean_json($meta);
+				
+			$meta = enano_json_decode($meta);
+			if ( isset($meta['lang_name_english']) && isset($meta['lang_name_native']) && isset($meta['lang_code']) )
+			{
+				$languages[$meta['lang_code']] = array(
+						'name' => $meta['lang_name_native'],
+						'name_eng' => $meta['lang_name_english'],
+						'dir' => $dh
+					);
+			}
+		}
+	}
+	
+	return $languages;
 }
 
 /**
@@ -4120,154 +4120,154 @@
 
 function scale_image($in_file, $out_file, $width = 225, $height = 225, $unlink = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  if ( !is_int($width) || !is_int($height) )
-    throw new Exception('Invalid height or width.');
-  
-  if ( !file_exists($in_file) )
-    throw new Exception('Input file does not exist');
-  
-  $in_file_sh = escapeshellarg($in_file);
-  $out_file_sh = escapeshellarg($out_file);
-  
-  if ( file_exists($out_file) && !$unlink )
-    throw new Exception('Refusing to write output file as it already exists and $unlink was not specified.');
-  else if ( file_exists($out_file) && $unlink )
-    @unlink($out_file);
-  if ( file_exists($out_file) )
-    // couldn't unlink (delete) the output file
-    throw new Exception('Failed to delete existing output file.');
-    
-  $file_ext = strtolower(substr($in_file, ( strrpos($in_file, '.') + 1)));
-  switch($file_ext)
-  {
-    case 'png':
-      $func = 'imagecreatefrompng';
-      break;
-    case 'jpg':
-    case 'jpeg':
-      $func = 'imagecreatefromjpeg';
-      break;
-    case 'gif':
-      $func = 'imagecreatefromgif';
-      break;
-    case 'xpm':
-      $func = 'imagecreatefromxpm';
-      break;
-    default:
-      throw new Exception('Invalid extension of input file.');
-  }
-    
-  $magick_path = getConfig('imagemagick_path');
-  $can_use_magick = (
-      getConfig('enable_imagemagick') == '1' &&
-      file_exists($magick_path)              &&
-      is_executable($magick_path)
-    );
-  $can_use_gd = (
-      function_exists('getimagesize')         &&
-      function_exists('imagecreatetruecolor') &&
-      function_exists('imagecopyresampled')   &&
-      function_exists($func)
-    );
-  if ( $can_use_magick )
-  {
-    if ( !preg_match('/^([\/A-z0-9:\. _-]+)$/', $magick_path) )
-    {
-      die('SECURITY: ImageMagick path is screwy');
-    }
-    $cmdline = "$magick_path $in_file_sh -resize \"{$width}x{$height}>\" $out_file_sh";
-    system($cmdline, $return);
-    if ( !file_exists($out_file) )
-      throw new Exception('ImageMagick: did not produce output image file.');
-    return true;
-  }
-  else if ( $can_use_gd )
-  {
-    @list($width_orig, $height_orig) = @getimagesize($in_file);
-    if ( !$width_orig || !$height_orig )
-      throw new Exception('GD: Could not get height and width of input file.');
-    // calculate new width and height
-    
-    $ratio = $width_orig / $height_orig;
-    if ( $ratio > 1 )
-    {
-      // orig. width is greater that height
-      $new_width = $width;
-      $new_height = round( $width / $ratio );
-    }
-    else if ( $ratio < 1 )
-    {
-      // orig. height is greater than width
-      $new_width = round( $height / $ratio );
-      $new_height = $height;
-    }
-    else if ( $ratio == 1 )
-    {
-      $new_width = $width;
-      $new_height = $width;
-    }
-    if ( $new_width > $width_orig || $new_height > $height_orig )
-    {
-      // Too big for our britches here; set it to only convert the file
-      $new_width = $width_orig;
-      $new_height = $height_orig;
-    }
-    
-    $newimage = @imagecreatetruecolor($new_width, $new_height);
-    if ( !$newimage )
-      throw new Exception('GD: Request to create new truecolor image refused.');
-    $oldimage = @$func($in_file);
-    if ( !$oldimage )
-      throw new Exception('GD: Request to load input image file failed.');
-    
-    // Perform scaling
-    imagecopyresampled($newimage, $oldimage, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
-    
-    // Get output format
-    $out_ext = strtolower(substr($out_file, ( strrpos($out_file, '.') + 1)));
-    switch($out_ext)
-    {
-      case 'png':
-        $outfunc = 'imagepng';
-        break;
-      case 'jpg':
-      case 'jpeg':
-        $outfunc = 'imagejpeg';
-        break;
-      case 'gif':
-        $outfunc = 'imagegif';
-        break;
-      case 'xpm':
-        $outfunc = 'imagexpm';
-        break;
-      default:
-        imagedestroy($newimage);
-        imagedestroy($oldimage);
-        throw new Exception('GD: Invalid extension of output file.');
-    }
-    
-    // Write output
-    $outfunc($newimage, $out_file);
-    
-    // clean up
-    imagedestroy($newimage);
-    imagedestroy($oldimage);
-    
-    // done!
-    return true;
-  }
-  // Neither scaling method worked; we'll let plugins try to scale it, and then if the file still doesn't exist, die
-  $code = $plugins->setHook('scale_image_failure');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  if ( file_exists($out_file) )
-    return true;
-  
-  throw new Exception('Failed to find an appropriate method for scaling.');
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	if ( !is_int($width) || !is_int($height) )
+		throw new Exception('Invalid height or width.');
+	
+	if ( !file_exists($in_file) )
+		throw new Exception('Input file does not exist');
+	
+	$in_file_sh = escapeshellarg($in_file);
+	$out_file_sh = escapeshellarg($out_file);
+	
+	if ( file_exists($out_file) && !$unlink )
+		throw new Exception('Refusing to write output file as it already exists and $unlink was not specified.');
+	else if ( file_exists($out_file) && $unlink )
+		@unlink($out_file);
+	if ( file_exists($out_file) )
+		// couldn't unlink (delete) the output file
+		throw new Exception('Failed to delete existing output file.');
+		
+	$file_ext = strtolower(substr($in_file, ( strrpos($in_file, '.') + 1)));
+	switch($file_ext)
+	{
+		case 'png':
+			$func = 'imagecreatefrompng';
+			break;
+		case 'jpg':
+		case 'jpeg':
+			$func = 'imagecreatefromjpeg';
+			break;
+		case 'gif':
+			$func = 'imagecreatefromgif';
+			break;
+		case 'xpm':
+			$func = 'imagecreatefromxpm';
+			break;
+		default:
+			throw new Exception('Invalid extension of input file.');
+	}
+		
+	$magick_path = getConfig('imagemagick_path');
+	$can_use_magick = (
+			getConfig('enable_imagemagick') == '1' &&
+			file_exists($magick_path)              &&
+			is_executable($magick_path)
+		);
+	$can_use_gd = (
+			function_exists('getimagesize')         &&
+			function_exists('imagecreatetruecolor') &&
+			function_exists('imagecopyresampled')   &&
+			function_exists($func)
+		);
+	if ( $can_use_magick )
+	{
+		if ( !preg_match('/^([\/A-z0-9:\. _-]+)$/', $magick_path) )
+		{
+			die('SECURITY: ImageMagick path is screwy');
+		}
+		$cmdline = "$magick_path $in_file_sh -resize \"{$width}x{$height}>\" $out_file_sh";
+		system($cmdline, $return);
+		if ( !file_exists($out_file) )
+			throw new Exception('ImageMagick: did not produce output image file.');
+		return true;
+	}
+	else if ( $can_use_gd )
+	{
+		@list($width_orig, $height_orig) = @getimagesize($in_file);
+		if ( !$width_orig || !$height_orig )
+			throw new Exception('GD: Could not get height and width of input file.');
+		// calculate new width and height
+		
+		$ratio = $width_orig / $height_orig;
+		if ( $ratio > 1 )
+		{
+			// orig. width is greater that height
+			$new_width = $width;
+			$new_height = round( $width / $ratio );
+		}
+		else if ( $ratio < 1 )
+		{
+			// orig. height is greater than width
+			$new_width = round( $height / $ratio );
+			$new_height = $height;
+		}
+		else if ( $ratio == 1 )
+		{
+			$new_width = $width;
+			$new_height = $width;
+		}
+		if ( $new_width > $width_orig || $new_height > $height_orig )
+		{
+			// Too big for our britches here; set it to only convert the file
+			$new_width = $width_orig;
+			$new_height = $height_orig;
+		}
+		
+		$newimage = @imagecreatetruecolor($new_width, $new_height);
+		if ( !$newimage )
+			throw new Exception('GD: Request to create new truecolor image refused.');
+		$oldimage = @$func($in_file);
+		if ( !$oldimage )
+			throw new Exception('GD: Request to load input image file failed.');
+		
+		// Perform scaling
+		imagecopyresampled($newimage, $oldimage, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
+		
+		// Get output format
+		$out_ext = strtolower(substr($out_file, ( strrpos($out_file, '.') + 1)));
+		switch($out_ext)
+		{
+			case 'png':
+				$outfunc = 'imagepng';
+				break;
+			case 'jpg':
+			case 'jpeg':
+				$outfunc = 'imagejpeg';
+				break;
+			case 'gif':
+				$outfunc = 'imagegif';
+				break;
+			case 'xpm':
+				$outfunc = 'imagexpm';
+				break;
+			default:
+				imagedestroy($newimage);
+				imagedestroy($oldimage);
+				throw new Exception('GD: Invalid extension of output file.');
+		}
+		
+		// Write output
+		$outfunc($newimage, $out_file);
+		
+		// clean up
+		imagedestroy($newimage);
+		imagedestroy($oldimage);
+		
+		// done!
+		return true;
+	}
+	// Neither scaling method worked; we'll let plugins try to scale it, and then if the file still doesn't exist, die
+	$code = $plugins->setHook('scale_image_failure');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	if ( file_exists($out_file) )
+		return true;
+	
+	throw new Exception('Failed to find an appropriate method for scaling.');
 }
 
 /**
@@ -4280,39 +4280,39 @@
  
 function is_gif_animated($filename)
 {
-  $filecontents = @file_get_contents($filename);
-  if ( empty($filecontents) )
-    return false;
-
-  $str_loc = 0;
-  $count = 0;
-  while ( $count < 2 ) // There is no point in continuing after we find a 2nd frame
-  {
-    $where1 = strpos($filecontents,"\x00\x21\xF9\x04", $str_loc);
-    if ( $where1 === false )
-    {
-      break;
-    }
-    else
-    {
-      $str_loc = $where1 + 1;
-      $where2 = strpos($filecontents,"\x00\x2C", $str_loc);
-      if ( $where2 === false )
-      {
-        break;
-      }
-      else
-      {
-        if ( $where1 + 8 == $where2 )
-        {
-          $count++;
-        }
-        $str_loc = $where2 + 1;
-      }
-    }
-  }
-  
-  return ( $count > 1 ) ? true : false;
+	$filecontents = @file_get_contents($filename);
+	if ( empty($filecontents) )
+		return false;
+
+	$str_loc = 0;
+	$count = 0;
+	while ( $count < 2 ) // There is no point in continuing after we find a 2nd frame
+	{
+		$where1 = strpos($filecontents,"\x00\x21\xF9\x04", $str_loc);
+		if ( $where1 === false )
+		{
+			break;
+		}
+		else
+		{
+			$str_loc = $where1 + 1;
+			$where2 = strpos($filecontents,"\x00\x2C", $str_loc);
+			if ( $where2 === false )
+			{
+				break;
+			}
+			else
+			{
+				if ( $where1 + 8 == $where2 )
+				{
+					$count++;
+				}
+				$str_loc = $where2 + 1;
+			}
+		}
+	}
+	
+	return ( $count > 1 ) ? true : false;
 }
 
 /**
@@ -4323,17 +4323,17 @@
 
 function gif_get_dimensions($filename)
 {
-  $filecontents = @file_get_contents($filename);
-  if ( empty($filecontents) )
-    return false;
-  if ( strlen($filecontents) < 10 )
-    return false;
-  
-  $width = substr($filecontents, 6, 2);
-  $height = substr($filecontents, 8, 2);
-  $width = unpack('v', $width);
-  $height = unpack('v', $height);
-  return array($width[1], $height[1]);
+	$filecontents = @file_get_contents($filename);
+	if ( empty($filecontents) )
+		return false;
+	if ( strlen($filecontents) < 10 )
+		return false;
+	
+	$width = substr($filecontents, 6, 2);
+	$height = substr($filecontents, 8, 2);
+	$width = unpack('v', $width);
+	$height = unpack('v', $height);
+	return array($width[1], $height[1]);
 }
 
 /**
@@ -4344,19 +4344,19 @@
 
 function is_png_animated($filename)
 {
-  $filecontents = @file_get_contents($filename);
-  if ( empty($filecontents) )
-    return false;
-  
-  $parsed = parse_png($filecontents);
-  if ( !$parsed )
-    return false;
-  
-  if ( !isset($parsed['fdAT']) )
-    return false;
-  
-  if ( count($parsed['fdAT']) > 1 )
-    return true;
+	$filecontents = @file_get_contents($filename);
+	if ( empty($filecontents) )
+		return false;
+	
+	$parsed = parse_png($filecontents);
+	if ( !$parsed )
+		return false;
+	
+	if ( !isset($parsed['fdAT']) )
+		return false;
+	
+	if ( count($parsed['fdAT']) > 1 )
+		return true;
 }
 
 /**
@@ -4367,22 +4367,22 @@
 
 function png_get_dimensions($filename)
 {
-  $filecontents = @file_get_contents($filename);
-  if ( empty($filecontents) )
-    return false;
-  
-  $parsed = parse_png($filecontents);
-  if ( !$parsed )
-    return false;
-  
-  $ihdr_stream = $parsed['IHDR'][0];
-  $width = substr($ihdr_stream, 0, 4);
-  $height = substr($ihdr_stream, 4, 4);
-  $width = unpack('N', $width);
-  $height = unpack('N', $height);
-  $x = $width[1];
-  $y = $height[1];
-  return array($x, $y);
+	$filecontents = @file_get_contents($filename);
+	if ( empty($filecontents) )
+		return false;
+	
+	$parsed = parse_png($filecontents);
+	if ( !$parsed )
+		return false;
+	
+	$ihdr_stream = $parsed['IHDR'][0];
+	$width = substr($ihdr_stream, 0, 4);
+	$height = substr($ihdr_stream, 4, 4);
+	$width = unpack('N', $width);
+	$height = unpack('N', $height);
+	$x = $width[1];
+	$y = $height[1];
+	return array($x, $y);
 }
 
 /**
@@ -4393,37 +4393,37 @@
 
 function parse_png($data)
 {
-  // Trim off first 8 bytes to check for PNG header
-  $header = substr($data, 0, 8);
-  if ( $header != "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" )
-  {
-    return false;
-  }
-  $return = array();
-  $data = substr($data, 8);
-  while ( strlen($data) > 0 )
-  {
-    $chunklen_bin = substr($data, 0, 4);
-    $chunk_type = substr($data, 4, 4);
-    $chunklen = unpack('N', $chunklen_bin);
-    $chunklen = $chunklen[1];
-    $chunk_data = substr($data, 8, $chunklen);
-    
-    // If the chunk type is not valid, this may be a malicious PNG with bad offsets. Break out of the loop.
-    if ( !preg_match('/^[A-z]{4}$/', $chunk_type) )
-      break;
-    
-    if ( !isset($return[$chunk_type]) )
-      $return[$chunk_type] = array();
-    $return[$chunk_type][] = $chunk_data;
-    
-    $offset_next = 4 // Length
-                 + 4 // Type
-                 + $chunklen // Data
-                 + 4; // CRC
-    $data = substr($data, $offset_next);
-  }
-  return $return;
+	// Trim off first 8 bytes to check for PNG header
+	$header = substr($data, 0, 8);
+	if ( $header != "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" )
+	{
+		return false;
+	}
+	$return = array();
+	$data = substr($data, 8);
+	while ( strlen($data) > 0 )
+	{
+		$chunklen_bin = substr($data, 0, 4);
+		$chunk_type = substr($data, 4, 4);
+		$chunklen = unpack('N', $chunklen_bin);
+		$chunklen = $chunklen[1];
+		$chunk_data = substr($data, 8, $chunklen);
+		
+		// If the chunk type is not valid, this may be a malicious PNG with bad offsets. Break out of the loop.
+		if ( !preg_match('/^[A-z]{4}$/', $chunk_type) )
+			break;
+		
+		if ( !isset($return[$chunk_type]) )
+			$return[$chunk_type] = array();
+		$return[$chunk_type][] = $chunk_data;
+		
+		$offset_next = 4 // Length
+ 								+ 4 // Type
+ 								+ $chunklen // Data
+ 								+ 4; // CRC
+		$data = substr($data, $offset_next);
+	}
+	return $return;
 }
 
 /**
@@ -4436,51 +4436,51 @@
 
 function get_jpeg_intrinsic_values( $jpeg_header_data )
 {
-  // Create a blank array for the output
-  $Outputarray = array( );
-
-  //Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments
-  $i = 0;
-  while ( ( $i < count( $jpeg_header_data) )  && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) )
-  {
-    $i++;
-  }
-
-  // Check if a SOF segment has been found
-  if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" )
-  {
-    // SOF segment was found, extract the information
-
-    $data = $jpeg_header_data[$i]['SegData'];
-
-    // First byte is Bits per component
-    $Outputarray['Bits per Component'] = ord( $data{0} );
-
-    // Second and third bytes are Image Height
-    $Outputarray['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } );
-
-    // Forth and fifth bytes are Image Width
-    $Outputarray['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } );
-
-    // Sixth byte is number of components
-    $numcomponents = ord( $data{ 5 } );
-
-    // Following this is a table containing information about the components
-    for( $i = 0; $i < $numcomponents; $i++ )
-    {
-      $Outputarray['Components'][] = array (  'Component Identifier' => ord( $data{ 6 + $i * 3 } ),
-                'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16,
-                'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ),
-                'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) );
-    }
-  }
-  else
-  {
-    // Couldn't find Start Of Frame segment, hence can't retrieve info
-    return FALSE;
-  }
-
-  return $Outputarray;
+	// Create a blank array for the output
+	$Outputarray = array( );
+
+	//Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments
+	$i = 0;
+	while ( ( $i < count( $jpeg_header_data) )  && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) )
+	{
+		$i++;
+	}
+
+	// Check if a SOF segment has been found
+	if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" )
+	{
+		// SOF segment was found, extract the information
+
+		$data = $jpeg_header_data[$i]['SegData'];
+
+		// First byte is Bits per component
+		$Outputarray['Bits per Component'] = ord( $data{0} );
+
+		// Second and third bytes are Image Height
+		$Outputarray['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } );
+
+		// Forth and fifth bytes are Image Width
+		$Outputarray['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } );
+
+		// Sixth byte is number of components
+		$numcomponents = ord( $data{ 5 } );
+
+		// Following this is a table containing information about the components
+		for( $i = 0; $i < $numcomponents; $i++ )
+		{
+			$Outputarray['Components'][] = array (  'Component Identifier' => ord( $data{ 6 + $i * 3 } ),
+								'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16,
+								'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ),
+								'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) );
+		}
+	}
+	else
+	{
+		// Couldn't find Start Of Frame segment, hence can't retrieve info
+		return FALSE;
+	}
+
+	return $Outputarray;
 }
 
 /**
@@ -4493,103 +4493,103 @@
 
 function get_jpeg_header_data( $filename )
 {
-  // Attempt to open the jpeg file - the at symbol supresses the error message about
-  // not being able to open files. The file_exists would have been used, but it
-  // does not work with files fetched over http or ftp.
-  $filehnd = @fopen($filename, 'rb');
-
-  // Check if the file opened successfully
-  if ( ! $filehnd  )
-  {
-    // Could't open the file - exit
-    return FALSE;
-  }
-
-
-  // Read the first two characters
-  $data = fread( $filehnd, 2 );
-
-  // Check that the first two characters are 0xFF 0xDA  (SOI - Start of image)
-  if ( $data != "\xFF\xD8" )
-  {
-    // No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
-    fclose($filehnd);
-    return FALSE;
-  }
-
-
-  // Read the third character
-  $data = fread( $filehnd, 2 );
-
-  // Check that the third character is 0xFF (Start of first segment header)
-  if ( $data{0} != "\xFF" )
-  {
-    // NO FF found - close file and return - JPEG is probably corrupted
-    fclose($filehnd);
-    return FALSE;
-  }
-
-  // Flag that we havent yet hit the compressed image data
-  $hit_compressed_image_data = FALSE;
-
-
-  // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
-  //               2) we have hit the compressed image data (no more headers are allowed after data)
-  //               3) or end of file is hit
-
-  while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) ))
-  {
-    // Found a segment to look at.
-    // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
-    if (  ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) )
-    {
-      // Segment isn't a Restart marker
-      // Read the next two bytes (size)
-      $sizestr = fread( $filehnd, 2 );
-
-      // convert the size bytes to an integer
-      $decodedsize = unpack ("nsize", $sizestr);
-
-      // Save the start position of the data
-      $segdatastart = ftell( $filehnd );
-
-      // Read the segment data with length indicated by the previously read size
-      $segdata = fread( $filehnd, $decodedsize['size'] - 2 );
-
-
-      // Store the segment information in the output array
-      $headerdata[] = array(  "SegType" => ord($data{1}),
-            "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data{1}) ],
-            "SegDataStart" => $segdatastart,
-            "SegData" => $segdata );
-    }
-
-    // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
-    if ( $data{1} == "\xDA" )
-    {
-      // Flag that we have hit the compressed image data - exit loop as no more headers available.
-      $hit_compressed_image_data = TRUE;
-    }
-    else
-    {
-      // Not an SOS - Read the next two bytes - should be the segment marker for the next segment
-      $data = fread( $filehnd, 2 );
-
-      // Check that the first byte of the two is 0xFF as it should be for a marker
-      if ( $data{0} != "\xFF" )
-      {
-        // NO FF found - close file and return - JPEG is probably corrupted
-        fclose($filehnd);
-        return FALSE;
-      }
-    }
-  }
-
-  // Close File
-  fclose($filehnd);
-
-  // Return the header data retrieved
-  return $headerdata;
+	// Attempt to open the jpeg file - the at symbol supresses the error message about
+	// not being able to open files. The file_exists would have been used, but it
+	// does not work with files fetched over http or ftp.
+	$filehnd = @fopen($filename, 'rb');
+
+	// Check if the file opened successfully
+	if ( ! $filehnd  )
+	{
+		// Could't open the file - exit
+		return FALSE;
+	}
+
+
+	// Read the first two characters
+	$data = fread( $filehnd, 2 );
+
+	// Check that the first two characters are 0xFF 0xDA  (SOI - Start of image)
+	if ( $data != "\xFF\xD8" )
+	{
+		// No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
+		fclose($filehnd);
+		return FALSE;
+	}
+
+
+	// Read the third character
+	$data = fread( $filehnd, 2 );
+
+	// Check that the third character is 0xFF (Start of first segment header)
+	if ( $data{0} != "\xFF" )
+	{
+		// NO FF found - close file and return - JPEG is probably corrupted
+		fclose($filehnd);
+		return FALSE;
+	}
+
+	// Flag that we havent yet hit the compressed image data
+	$hit_compressed_image_data = FALSE;
+
+
+	// Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
+	//               2) we have hit the compressed image data (no more headers are allowed after data)
+	//               3) or end of file is hit
+
+	while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) ))
+	{
+		// Found a segment to look at.
+		// Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
+		if (  ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) )
+		{
+			// Segment isn't a Restart marker
+			// Read the next two bytes (size)
+			$sizestr = fread( $filehnd, 2 );
+
+			// convert the size bytes to an integer
+			$decodedsize = unpack ("nsize", $sizestr);
+
+			// Save the start position of the data
+			$segdatastart = ftell( $filehnd );
+
+			// Read the segment data with length indicated by the previously read size
+			$segdata = fread( $filehnd, $decodedsize['size'] - 2 );
+
+
+			// Store the segment information in the output array
+			$headerdata[] = array(  "SegType" => ord($data{1}),
+						"SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data{1}) ],
+						"SegDataStart" => $segdatastart,
+						"SegData" => $segdata );
+		}
+
+		// If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
+		if ( $data{1} == "\xDA" )
+		{
+			// Flag that we have hit the compressed image data - exit loop as no more headers available.
+			$hit_compressed_image_data = TRUE;
+		}
+		else
+		{
+			// Not an SOS - Read the next two bytes - should be the segment marker for the next segment
+			$data = fread( $filehnd, 2 );
+
+			// Check that the first byte of the two is 0xFF as it should be for a marker
+			if ( $data{0} != "\xFF" )
+			{
+				// NO FF found - close file and return - JPEG is probably corrupted
+				fclose($filehnd);
+				return FALSE;
+			}
+		}
+	}
+
+	// Close File
+	fclose($filehnd);
+
+	// Return the header data retrieved
+	return $headerdata;
 }
 
 /**
@@ -4600,33 +4600,33 @@
 
 function jpg_get_dimensions($filename)
 {
-  if ( !file_exists($filename) )
-  {
-    echo "Doesn't exist<br />";
-    return false;
-  }
-  
-  $headers = get_jpeg_header_data($filename);
-  if ( !$headers )
-  {
-    echo "Bad headers<br />";
-    return false;
-  }
-  
-  $metadata = get_jpeg_intrinsic_values($headers);
-  if ( !$metadata )
-  {
-    echo "Bad metadata: <pre>" . print_r($metadata, true) . "</pre><br />";
-    return false;
-  }
-  
-  if ( !isset($metadata['Image Width']) || !isset($metadata['Image Height']) )
-  {
-    echo "No metadata<br />";
-    return false;
-  }
-  
-  return array($metadata['Image Width'], $metadata['Image Height']);
+	if ( !file_exists($filename) )
+	{
+		echo "Doesn't exist<br />";
+		return false;
+	}
+	
+	$headers = get_jpeg_header_data($filename);
+	if ( !$headers )
+	{
+		echo "Bad headers<br />";
+		return false;
+	}
+	
+	$metadata = get_jpeg_intrinsic_values($headers);
+	if ( !$metadata )
+	{
+		echo "Bad metadata: <pre>" . print_r($metadata, true) . "</pre><br />";
+		return false;
+	}
+	
+	if ( !isset($metadata['Image Width']) || !isset($metadata['Image Height']) )
+	{
+		echo "No metadata<br />";
+		return false;
+	}
+	
+	return array($metadata['Image Width'], $metadata['Image Height']);
 }
 
 /**
@@ -4639,42 +4639,42 @@
 
 function make_avatar_url($user_id, $avi_type, $user_email = false)
 {
-  static $img_types = array(
-      'png' => IMAGE_TYPE_PNG,
-      'gif' => IMAGE_TYPE_GIF,
-      'jpg' => IMAGE_TYPE_JPG,
-      'grv' => IMAGE_TYPE_GRV
-    );
-  
-  if ( !is_int($user_id) )
-    return false;
-  if ( !isset($img_types[$avi_type]) )
-    return false;
-  
-  if ( $avi_type == 'grv' )
-  {
-    if ( $user_email )
-    {
-      return make_gravatar_url($user_email);
-    }
-  }
-  else
-  {
-    $avi_relative_path = '/' . getConfig('avatar_directory') . '/' . $user_id . '.' . $avi_type;
-    if ( !file_exists(ENANO_ROOT . $avi_relative_path) )
-    {
-      return '';
-    }
-  }
-  
-  $img_type = $img_types[$avi_type];
-  
-  $dateline = @filemtime(ENANO_ROOT . $avi_relative_path);
-  $avi_id = pack('VVv', $dateline, $user_id, $img_type);
-  $avi_id = hexencode($avi_id, '', '');
-    
-  // return scriptPath . $avi_relative_path;
-  return makeUrlNS('Special', "Avatar/$avi_id");
+	static $img_types = array(
+			'png' => IMAGE_TYPE_PNG,
+			'gif' => IMAGE_TYPE_GIF,
+			'jpg' => IMAGE_TYPE_JPG,
+			'grv' => IMAGE_TYPE_GRV
+		);
+	
+	if ( !is_int($user_id) )
+		return false;
+	if ( !isset($img_types[$avi_type]) )
+		return false;
+	
+	if ( $avi_type == 'grv' )
+	{
+		if ( $user_email )
+		{
+			return make_gravatar_url($user_email);
+		}
+	}
+	else
+	{
+		$avi_relative_path = '/' . getConfig('avatar_directory') . '/' . $user_id . '.' . $avi_type;
+		if ( !file_exists(ENANO_ROOT . $avi_relative_path) )
+		{
+			return '';
+		}
+	}
+	
+	$img_type = $img_types[$avi_type];
+	
+	$dateline = @filemtime(ENANO_ROOT . $avi_relative_path);
+	$avi_id = pack('VVv', $dateline, $user_id, $img_type);
+	$avi_id = hexencode($avi_id, '', '');
+		
+	// return scriptPath . $avi_relative_path;
+	return makeUrlNS('Special', "Avatar/$avi_id");
 }
 
 /**
@@ -4686,28 +4686,28 @@
 
 function make_gravatar_url($email, $size = false)
 {
-  $email = md5($email);
-  
-  // gravatar parameters
-  if ( $size )
-  {
-    $max_size = intval($size);
-  }
-  else
-  {
-    $max_x = intval(getConfig('avatar_max_width', '150'));
-    $max_y = intval(getConfig('avatar_max_height', '150'));
-    // ?s=
-    $max_size = ( $max_x > $max_y ) ? $max_y : $max_x;
-  }
-  
-  // ?r=
-  $rating = getConfig('gravatar_rating', 'g');
-  
-  // final URL
-  $url = "http://www.gravatar.com/avatar/$email?r=$rating&s=$max_size";
-  
-  return $url;
+	$email = md5($email);
+	
+	// gravatar parameters
+	if ( $size )
+	{
+		$max_size = intval($size);
+	}
+	else
+	{
+		$max_x = intval(getConfig('avatar_max_width', '150'));
+		$max_y = intval(getConfig('avatar_max_height', '150'));
+		// ?s=
+		$max_size = ( $max_x > $max_y ) ? $max_y : $max_x;
+	}
+	
+	// ?r=
+	$rating = getConfig('gravatar_rating', 'g');
+	
+	// final URL
+	$url = "http://www.gravatar.com/avatar/$email?r=$rating&s=$max_size";
+	
+	return $url;
 }
 
 /**
@@ -4718,20 +4718,20 @@
 
 function get_image_filetype($filename)
 {
-  $filecontents = @file_get_contents($filename);
-  if ( empty($filecontents) )
-    return false;
-  
-  if ( substr($filecontents, 0, 8) == "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" )
-    return 'png';
-  
-  if ( substr($filecontents, 0, 6) == 'GIF87a' || substr($filecontents, 0, 6) == 'GIF89a' )
-    return 'gif';
-  
-  if ( substr($filecontents, 0, 2) == "\xFF\xD8" )
-    return 'jpg';
-  
-  return false;
+	$filecontents = @file_get_contents($filename);
+	if ( empty($filecontents) )
+		return false;
+	
+	if ( substr($filecontents, 0, 8) == "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" )
+		return 'png';
+	
+	if ( substr($filecontents, 0, 6) == 'GIF87a' || substr($filecontents, 0, 6) == 'GIF89a' )
+		return 'gif';
+	
+	if ( substr($filecontents, 0, 2) == "\xFF\xD8" )
+		return 'jpg';
+	
+	return false;
 }
 
 /**
@@ -4741,11 +4741,11 @@
 
 function enano_json_singleton()
 {
-  static $json_obj;
-  if ( !is_object($json_obj) )
-    $json_obj = new Services_JSON(SERVICES_JSON_LOOSE_TYPE | SERVICES_JSON_SUPPRESS_ERRORS);
-  
-  return $json_obj;
+	static $json_obj;
+	if ( !is_object($json_obj) )
+		$json_obj = new Services_JSON(SERVICES_JSON_LOOSE_TYPE | SERVICES_JSON_SUPPRESS_ERRORS);
+	
+	return $json_obj;
 }
 
 /**
@@ -4756,15 +4756,15 @@
 
 function enano_json_encode($data)
 {
-  /*
-  if ( function_exists('json_encode') )
-  {
-    // using PHP5 with JSON support
-    return json_encode($data);
-  }
-  */
-  
-  return Zend_Json::encode($data, true);
+	/*
+	if ( function_exists('json_encode') )
+	{
+		// using PHP5 with JSON support
+		return json_encode($data);
+	}
+	*/
+	
+	return Zend_Json::encode($data, true);
 }
 
 /**
@@ -4775,15 +4775,15 @@
 
 function enano_json_decode($data)
 {
-  /*
-  if ( function_exists('json_decode') )
-  {
-    // using PHP5 with JSON support
-    return json_decode($data);
-  }
-  */
-  
-  return Zend_Json::decode($data, Zend_Json::TYPE_ARRAY);
+	/*
+	if ( function_exists('json_decode') )
+	{
+		// using PHP5 with JSON support
+		return json_decode($data);
+	}
+	*/
+	
+	return Zend_Json::decode($data, Zend_Json::TYPE_ARRAY);
 }
 
 /**
@@ -4794,19 +4794,19 @@
 
 function enano_clean_json($json)
 {
-  // eliminate comments
-  $json = preg_replace(array(
-          // eliminate single line comments in '// ...' form
-          '#^\s*//(.*)$#m',
-          // eliminate multi-line comments in '/* ... */' form, at start of string
-          '#^\s*/\*(.+)\*/#Us',
-          // eliminate multi-line comments in '/* ... */' form, at end of string
-          '#/\*(.+)\*/\s*$#Us'
-        ), '', $json);
-    
-  $json = preg_replace('/([,\{\[])(?:[\r\n]+)([\s]*?)([a-z0-9_]+)([\s]*?):/', '\\1\\2"\\3" :', $json);
-  
-  return $json;
+	// eliminate comments
+	$json = preg_replace(array(
+					// eliminate single line comments in '// ...' form
+					'#^\s*//(.*)$#m',
+					// eliminate multi-line comments in '/* ... */' form, at start of string
+					'#^\s*/\*(.+)\*/#Us',
+					// eliminate multi-line comments in '/* ... */' form, at end of string
+					'#/\*(.+)\*/\s*$#Us'
+				), '', $json);
+		
+	$json = preg_replace('/([,\{\[])(?:[\r\n]+)([\s]*?)([a-z0-9_]+)([\s]*?):/', '\\1\\2"\\3" :', $json);
+	
+	return $json;
 }
 
 /**
@@ -4817,7 +4817,7 @@
 
 function enano_trim_json($json)
 {
-  return preg_replace('/^([^{]+)\{/', '{', preg_replace('/\}([^}]+)$/', '}', $json));
+	return preg_replace('/^([^{]+)\{/', '{', preg_replace('/\}([^}]+)$/', '}', $json));
 }
 
 /**
@@ -4826,22 +4826,22 @@
 
 function profiler_start()
 {
-  global $_profiler;
-  $_profiler = array();
-  
-  if ( !defined('ENANO_DEBUG') )
-    return false;
-  
-  $_profiler[] = array(
-      'point' => 'Profiling started',
-      'time' => microtime_float(),
-      'backtrace' => false,
-      'mem' => false
-    );
-  if ( function_exists('memory_get_usage') )
-  {
-    $_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage();
-  }
+	global $_profiler;
+	$_profiler = array();
+	
+	if ( !defined('ENANO_DEBUG') )
+		return false;
+	
+	$_profiler[] = array(
+			'point' => 'Profiling started',
+			'time' => microtime_float(),
+			'backtrace' => false,
+			'mem' => false
+		);
+	if ( function_exists('memory_get_usage') )
+	{
+		$_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage();
+	}
 }
 
 /**
@@ -4854,27 +4854,27 @@
 
 function profiler_log($point, $allow_backtrace = true, $parent_event = false)
 {
-  if ( !defined('ENANO_DEBUG') )
-    return false;
-  
-  global $_profiler;
-  $backtrace = false;
-  if ( $allow_backtrace && function_exists('debug_print_backtrace') )
-  {
-    list(, $backtrace) = explode("\n", enano_debug_print_backtrace(true));
-  }
-  $_profiler[] = array(
-      'point' => $point,
-      'time' => microtime_float(),
-      'backtrace' => $backtrace,
-      'mem' => false,
-      'parent_event' => $parent_event
-    );
-  if ( function_exists('memory_get_usage') )
-  {
-    $_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage();
-  }
-  return count($_profiler) - 1;
+	if ( !defined('ENANO_DEBUG') )
+		return false;
+	
+	global $_profiler;
+	$backtrace = false;
+	if ( $allow_backtrace && function_exists('debug_print_backtrace') )
+	{
+		list(, $backtrace) = explode("\n", enano_debug_print_backtrace(true));
+	}
+	$_profiler[] = array(
+			'point' => $point,
+			'time' => microtime_float(),
+			'backtrace' => $backtrace,
+			'mem' => false,
+			'parent_event' => $parent_event
+		);
+	if ( function_exists('memory_get_usage') )
+	{
+		$_profiler[ count($_profiler) - 1 ]['mem'] = memory_get_usage();
+	}
+	return count($_profiler) - 1;
 }
 
 /**
@@ -4884,14 +4884,14 @@
 
 function profiler_message($message)
 {
-  if ( !defined('ENANO_DEBUG') )
-    return false;
-  
-  global $_profiler;
-  
-  $_profiler[] = array(
-      'message' => $message,
-    );
+	if ( !defined('ENANO_DEBUG') )
+		return false;
+	
+	global $_profiler;
+	
+	$_profiler[] = array(
+			'message' => $message,
+		);
 }
 
 /**
@@ -4901,7 +4901,7 @@
 
 function profiler_dump()
 {
-  return $GLOBALS['_profiler'];
+	return $GLOBALS['_profiler'];
 }
 
 /**
@@ -4911,91 +4911,91 @@
 
 function profiler_make_html()
 {
-  if ( !defined('ENANO_DEBUG') )
-    return '';
-    
-  $profile = profiler_dump();
-  
-  $html = '<div class="tblholder">';
-  $html .= '<table border="0" cellspacing="1" cellpadding="4">';
-  
-  $time_start = $time_last = $profile[0]['time'];
-  
-  foreach ( $profile as $i => $entry )
-  {
-    // $time_since_last = $entry['time'] - $time_last;
-    // if ( $time_since_last < 0.01 )
-    //   continue;
-    
-    if ( isset($entry['message']) )
-    {
-      $html .= "<!-- ########################################################## -->\n<tr>\n  <th colspan=\"2\">Message $i</th>\n</tr>";
-      
-      $html .= '<tr>' . "\n";
-      $html .= '  <td class="row2">Message:</td>' . "\n";
-      $html .= '  <td class="row1">' . htmlspecialchars($entry['message']) . '</td>' . "\n";
-      $html .= '</tr>' . "\n";
-      continue;
-    }
-    
-    $html .= "<!-- ########################################################## -->\n<tr>\n  <th colspan=\"2\">Event $i</th>\n</tr>";
-    
-    $html .= '<tr>' . "\n";
-    $html .= '  <td class="row2">Event:</td>' . "\n";
-    $html .= '  <td class="row1">' . htmlspecialchars($entry['point']) . '</td>' . "\n";
-    $html .= '</tr>' . "\n";
-    
-    $time = $entry['time'] - $time_start;
-    
-    $html .= '<tr>' . "\n";
-    $html .= '  <td class="row2">Time since start:</td>' . "\n";
-    $html .= '  <td class="row1">' . $time . 's</td>' . "\n";
-    $html .= '</tr>' . "\n";
-    
-    $time_label = 'Time since last event:';
-    if ( $entry['parent_event'] && is_int($entry['parent_event']) && isset($profile[$entry['parent_event']]) )
-    {
-      $time_last = $profile[$entry['parent_event']]['time'];
-      $time_label = "Time since event #{$entry['parent_event']}:";
-    }
-    
-    $time = $entry['time'] - $time_last;
-    if ( $time < 0.0001 )
-      $time_html = 'Marginal';
-    else
-      $time_html = number_format($time, 6) . "s";
-    
-    if ( $time > 0.02 )
-      $time_html = "<span style=\"background-color: #a00; padding: 4px; color: #fff; font-weight: bold;\">$time_html</span>";
-      
-    $html .= '<tr>' . "\n";
-    $html .= '  <td class="row2">' . $time_label . '</td>' . "\n";
-    $html .= '  <td class="row1">' . $time_html . '</td>' . "\n";
-    $html .= '</tr>' . "\n";
-    
-    if ( $entry['backtrace'] )
-    {
-      $html .= '<tr>' . "\n";
-      $html .= '  <td class="row2">Called from:</td>' . "\n";
-      $html .= '  <td class="row1">' . htmlspecialchars($entry['backtrace']) . '</td>' . "\n";
-      $html .= '</tr>' . "\n";
-    }
-    
-    if ( $entry['mem'] )
-    {
-      $html .= '<tr>' . "\n";
-      $html .= '  <td class="row2">Total mem usage:</td>' . "\n";
-      $html .= '  <td class="row1">' . htmlspecialchars($entry['mem']) . ' (bytes)</td>' . "\n";
-      $html .= '</tr>' . "\n";
-    }
-    
-    $html .= "\n";
-    
-    $time_last = $entry['time'];
-  }
-  $html .= '</table></div>';
-  
-  return $html;
+	if ( !defined('ENANO_DEBUG') )
+		return '';
+		
+	$profile = profiler_dump();
+	
+	$html = '<div class="tblholder">';
+	$html .= '<table border="0" cellspacing="1" cellpadding="4">';
+	
+	$time_start = $time_last = $profile[0]['time'];
+	
+	foreach ( $profile as $i => $entry )
+	{
+		// $time_since_last = $entry['time'] - $time_last;
+		// if ( $time_since_last < 0.01 )
+		//   continue;
+		
+		if ( isset($entry['message']) )
+		{
+			$html .= "<!-- ########################################################## -->\n<tr>\n  <th colspan=\"2\">Message $i</th>\n</tr>";
+			
+			$html .= '<tr>' . "\n";
+			$html .= '  <td class="row2">Message:</td>' . "\n";
+			$html .= '  <td class="row1">' . htmlspecialchars($entry['message']) . '</td>' . "\n";
+			$html .= '</tr>' . "\n";
+			continue;
+		}
+		
+		$html .= "<!-- ########################################################## -->\n<tr>\n  <th colspan=\"2\">Event $i</th>\n</tr>";
+		
+		$html .= '<tr>' . "\n";
+		$html .= '  <td class="row2">Event:</td>' . "\n";
+		$html .= '  <td class="row1">' . htmlspecialchars($entry['point']) . '</td>' . "\n";
+		$html .= '</tr>' . "\n";
+		
+		$time = $entry['time'] - $time_start;
+		
+		$html .= '<tr>' . "\n";
+		$html .= '  <td class="row2">Time since start:</td>' . "\n";
+		$html .= '  <td class="row1">' . $time . 's</td>' . "\n";
+		$html .= '</tr>' . "\n";
+		
+		$time_label = 'Time since last event:';
+		if ( $entry['parent_event'] && is_int($entry['parent_event']) && isset($profile[$entry['parent_event']]) )
+		{
+			$time_last = $profile[$entry['parent_event']]['time'];
+			$time_label = "Time since event #{$entry['parent_event']}:";
+		}
+		
+		$time = $entry['time'] - $time_last;
+		if ( $time < 0.0001 )
+			$time_html = 'Marginal';
+		else
+			$time_html = number_format($time, 6) . "s";
+		
+		if ( $time > 0.02 )
+			$time_html = "<span style=\"background-color: #a00; padding: 4px; color: #fff; font-weight: bold;\">$time_html</span>";
+			
+		$html .= '<tr>' . "\n";
+		$html .= '  <td class="row2">' . $time_label . '</td>' . "\n";
+		$html .= '  <td class="row1">' . $time_html . '</td>' . "\n";
+		$html .= '</tr>' . "\n";
+		
+		if ( $entry['backtrace'] )
+		{
+			$html .= '<tr>' . "\n";
+			$html .= '  <td class="row2">Called from:</td>' . "\n";
+			$html .= '  <td class="row1">' . htmlspecialchars($entry['backtrace']) . '</td>' . "\n";
+			$html .= '</tr>' . "\n";
+		}
+		
+		if ( $entry['mem'] )
+		{
+			$html .= '<tr>' . "\n";
+			$html .= '  <td class="row2">Total mem usage:</td>' . "\n";
+			$html .= '  <td class="row1">' . htmlspecialchars($entry['mem']) . ' (bytes)</td>' . "\n";
+			$html .= '</tr>' . "\n";
+		}
+		
+		$html .= "\n";
+		
+		$time_last = $entry['time'];
+	}
+	$html .= '</table></div>';
+	
+	return $html;
 }
 
 // Might as well start the profiler, it has no external dependencies except from this file.
@@ -5010,14 +5010,14 @@
 
 function get_char_count($string, $char)
 {
-  $char = substr($char, 0, 1);
-  $count = 0;
-  for ( $i = 0; $i < strlen($string); $i++ )
-  {
-    if ( $string{$i} == $char )
-      $count++;
-  }
-  return $count;
+	$char = substr($char, 0, 1);
+	$count = 0;
+	for ( $i = 0; $i < strlen($string); $i++ )
+	{
+		if ( $string{$i} == $char )
+			$count++;
+	}
+	return $count;
 }
 
 /**
@@ -5028,52 +5028,52 @@
 
 function get_line_count($string)
 {
-  return ( get_char_count($string, "\n") ) + 1;
+	return ( get_char_count($string, "\n") ) + 1;
 }
 
 if ( !function_exists('sys_get_temp_dir') )
 {
-    // Based on http://www.phpit.net/
-    // article/creating-zip-tar-archives-dynamically-php/2/
-    /**
-     * Attempt to get the system's temp directory.
-     * @return string or bool false on failure
-     */
-    
-    function sys_get_temp_dir()
-    {
-        // Try to get from environment variable
-        if ( !empty($_ENV['TMP']) )
-        {
-            return realpath( $_ENV['TMP'] );
-        }
-        else if ( !empty($_ENV['TMPDIR']) )
-        {
-            return realpath( $_ENV['TMPDIR'] );
-        }
-        else if ( !empty($_ENV['TEMP']) )
-        {
-            return realpath( $_ENV['TEMP'] );
-        }
-
-        // Detect by creating a temporary file
-        else
-        {
-            // Try to use system's temporary directory
-            // as random name shouldn't exist
-            $temp_file = tempnam( md5(uniqid(rand(), TRUE)), '' );
-            if ( $temp_file )
-            {
-                $temp_dir = realpath( dirname($temp_file) );
-                unlink( $temp_file );
-                return $temp_dir;
-            }
-            else
-            {
-                return FALSE;
-            }
-        }
-    }
+		// Based on http://www.phpit.net/
+		// article/creating-zip-tar-archives-dynamically-php/2/
+		/**
+ 		* Attempt to get the system's temp directory.
+ 		* @return string or bool false on failure
+ 		*/
+		
+		function sys_get_temp_dir()
+		{
+				// Try to get from environment variable
+				if ( !empty($_ENV['TMP']) )
+				{
+						return realpath( $_ENV['TMP'] );
+				}
+				else if ( !empty($_ENV['TMPDIR']) )
+				{
+						return realpath( $_ENV['TMPDIR'] );
+				}
+				else if ( !empty($_ENV['TEMP']) )
+				{
+						return realpath( $_ENV['TEMP'] );
+				}
+
+				// Detect by creating a temporary file
+				else
+				{
+						// Try to use system's temporary directory
+						// as random name shouldn't exist
+						$temp_file = tempnam( md5(uniqid(rand(), TRUE)), '' );
+						if ( $temp_file )
+						{
+								$temp_dir = realpath( dirname($temp_file) );
+								unlink( $temp_file );
+								return $temp_dir;
+						}
+						else
+						{
+								return FALSE;
+						}
+				}
+		}
 }
 
 /**
@@ -5082,25 +5082,25 @@
 
 function fetch_rank_data()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $sql = $session->generate_rank_sql();
-  $q = $db->sql_query($sql);
-  if ( !$q )
-    $db->_die();
-  
-  $GLOBALS['user_ranks'] = array();
-  global $user_ranks;
-  
-  while ( $row = $db->fetchrow($q) )
-  {
-    $user_id = $row['user_id'];
-    $username = $row['username'];
-    $row = $session->calculate_user_rank($row);
-    $user_ranks[$username] =  $row;
-    $user_ranks[$user_id]  =& $user_ranks[$username];
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$sql = $session->generate_rank_sql();
+	$q = $db->sql_query($sql);
+	if ( !$q )
+		$db->_die();
+	
+	$GLOBALS['user_ranks'] = array();
+	global $user_ranks;
+	
+	while ( $row = $db->fetchrow($q) )
+	{
+		$user_id = $row['user_id'];
+		$username = $row['username'];
+		$row = $session->calculate_user_rank($row);
+		$user_ranks[$username] =  $row;
+		$user_ranks[$user_id]  =& $user_ranks[$username];
+	}
 }
 
 /**
@@ -5109,42 +5109,42 @@
 
 function generate_cache_userranks()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $user_ranks;
-  
-  fetch_rank_data();
-  
-  $user_ranks_stripped = array();
-  foreach ( $user_ranks as $key => $value )
-  {
-    if ( is_int($key) )
-      $user_ranks_stripped[$key] = $value;
-  }
-  
-  $ranks_exported = "<?php\n\n// Automatically generated user rank cache.\nglobal \$user_ranks;\n" . '$user_ranks = ' . $lang->var_export_string($user_ranks_stripped) . ';';
-  $uid_map = array();
-  foreach ( $user_ranks as $id => $row )
-  {
-    if ( !is_int($id) )
-    {
-      $username = $id;
-      continue;
-    }
-    
-    $un_san = addslashes($username);
-    $ranks_exported .= "\n\$user_ranks['$un_san'] =& \$user_ranks[{$row['user_id']}];";
-  }
-  $ranks_exported .= "\n\ndefine('ENANO_RANKS_CACHE_LOADED', 1); \n?>";
-  
-  // open ranks cache file
-  $fh = @fopen( ENANO_ROOT . '/cache/cache_ranks.php', 'w' );
-  if ( !$fh )
-    return false;
-  fwrite($fh, $ranks_exported);
-  fclose($fh);
-  
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $user_ranks;
+	
+	fetch_rank_data();
+	
+	$user_ranks_stripped = array();
+	foreach ( $user_ranks as $key => $value )
+	{
+		if ( is_int($key) )
+			$user_ranks_stripped[$key] = $value;
+	}
+	
+	$ranks_exported = "<?php\n\n// Automatically generated user rank cache.\nglobal \$user_ranks;\n" . '$user_ranks = ' . $lang->var_export_string($user_ranks_stripped) . ';';
+	$uid_map = array();
+	foreach ( $user_ranks as $id => $row )
+	{
+		if ( !is_int($id) )
+		{
+			$username = $id;
+			continue;
+		}
+		
+		$un_san = addslashes($username);
+		$ranks_exported .= "\n\$user_ranks['$un_san'] =& \$user_ranks[{$row['user_id']}];";
+	}
+	$ranks_exported .= "\n\ndefine('ENANO_RANKS_CACHE_LOADED', 1); \n?>";
+	
+	// open ranks cache file
+	$fh = @fopen( ENANO_ROOT . '/cache/cache_ranks.php', 'w' );
+	if ( !$fh )
+		return false;
+	fwrite($fh, $ranks_exported);
+	fclose($fh);
+	
+	return true;
 }
 
 /**
@@ -5153,14 +5153,14 @@
 
 function load_rank_data()
 {
-  if ( file_exists( ENANO_ROOT . '/cache/cache_ranks.php' ) )
-  {
-    @include(ENANO_ROOT . '/cache/cache_ranks.php');
-  }
-  if ( !defined('ENANO_RANKS_CACHE_LOADED') )
-  {
-    fetch_rank_data();
-  }
+	if ( file_exists( ENANO_ROOT . '/cache/cache_ranks.php' ) )
+	{
+		@include(ENANO_ROOT . '/cache/cache_ranks.php');
+	}
+	if ( !defined('ENANO_RANKS_CACHE_LOADED') )
+	{
+		fetch_rank_data();
+	}
 }
 
 /**
@@ -5169,45 +5169,45 @@
 
 function purge_all_caches()
 {
-  global $cache;
-  if ( $dh = opendir(ENANO_ROOT . '/cache') )
-  {
-    $cache->purge('page_meta');
-    $cache->purge('anon_sidebar');
-    $cache->purge('plugins');
-    $cache->purge('wiki_edit_notice');
-    
-    $data_files = array(
-        'aes_decrypt.php',
-        // ranks cache is stored using a custom engine (not enano's default cache)
-        'cache_ranks.php'
-      );
-    while ( $file = @readdir($dh) )
-    {
-      $fullpath = ENANO_ROOT . "/cache/$file";
-      // we don't want to mess with directories
-      if ( !is_file($fullpath) )
-        continue;
-      
-      // data files
-      if ( in_array($file, $data_files) )
-        unlink($fullpath);
-      // template files
-      else if ( preg_match('/\.(?:tpl|css)\.php$/', $file) )
-        unlink($fullpath);
-      // compressed javascript
-      else if ( preg_match('/^jsres_(?:[A-z0-9_-]+)\.js\.json$/', $file) )
-        unlink($fullpath);
-      // tinymce stuff
-      else if ( preg_match('/^tiny_mce_(?:[a-f0-9]+)\.gz$/', $file) )
-        unlink($fullpath);
-      // language files
-      else if ( preg_match('/^lang_json_(?:[a-f0-9]+?)\.php$/', $file) || preg_match('/^(?:cache_)?lang_(?:[0-9]+?)\.php$/', $file) )
-        unlink($fullpath);
-    }
-    return true;
-  }
-  return false;
+	global $cache;
+	if ( $dh = opendir(ENANO_ROOT . '/cache') )
+	{
+		$cache->purge('page_meta');
+		$cache->purge('anon_sidebar');
+		$cache->purge('plugins');
+		$cache->purge('wiki_edit_notice');
+		
+		$data_files = array(
+				'aes_decrypt.php',
+				// ranks cache is stored using a custom engine (not enano's default cache)
+				'cache_ranks.php'
+			);
+		while ( $file = @readdir($dh) )
+		{
+			$fullpath = ENANO_ROOT . "/cache/$file";
+			// we don't want to mess with directories
+			if ( !is_file($fullpath) )
+				continue;
+			
+			// data files
+			if ( in_array($file, $data_files) )
+				unlink($fullpath);
+			// template files
+			else if ( preg_match('/\.(?:tpl|css)\.php$/', $file) )
+				unlink($fullpath);
+			// compressed javascript
+			else if ( preg_match('/^jsres_(?:[A-z0-9_-]+)\.js\.json$/', $file) )
+				unlink($fullpath);
+			// tinymce stuff
+			else if ( preg_match('/^tiny_mce_(?:[a-f0-9]+)\.gz$/', $file) )
+				unlink($fullpath);
+			// language files
+			else if ( preg_match('/^lang_json_(?:[a-f0-9]+?)\.php$/', $file) || preg_match('/^(?:cache_)?lang_(?:[0-9]+?)\.php$/', $file) )
+				unlink($fullpath);
+		}
+		return true;
+	}
+	return false;
 }
 
 /**
@@ -5218,27 +5218,27 @@
 
 function which($executable)
 {
-  $path = ( isset($_ENV['PATH']) ) ? $_ENV['PATH'] : ( isset($_SERVER['PATH']) ? $_SERVER['PATH'] : false );
-  if ( !$path )
-    // couldn't get OS's PATH
-    return false;
-    
-  $win32 = ( PHP_OS == 'WINNT' || PHP_OS == 'WIN32' );
-  $extensions = $win32 ? array('.exe', '.com', '.bat') : array('');
-  $separator = $win32 ? ';' : ':';
-  $paths = explode($separator, $path);
-  foreach ( $paths as $dir )
-  {
-    foreach ( $extensions as $ext )
-    {
-      $fullpath = "$dir/{$executable}{$ext}";
-      if ( @file_exists($fullpath) && @is_executable($fullpath) )
-      {
-        return $fullpath;
-      }
-    }
-  }
-  return false;
+	$path = ( isset($_ENV['PATH']) ) ? $_ENV['PATH'] : ( isset($_SERVER['PATH']) ? $_SERVER['PATH'] : false );
+	if ( !$path )
+		// couldn't get OS's PATH
+		return false;
+		
+	$win32 = ( PHP_OS == 'WINNT' || PHP_OS == 'WIN32' );
+	$extensions = $win32 ? array('.exe', '.com', '.bat') : array('');
+	$separator = $win32 ? ';' : ':';
+	$paths = explode($separator, $path);
+	foreach ( $paths as $dir )
+	{
+		foreach ( $extensions as $ext )
+		{
+			$fullpath = "$dir/{$executable}{$ext}";
+			if ( @file_exists($fullpath) && @is_executable($fullpath) )
+			{
+				return $fullpath;
+			}
+		}
+	}
+	return false;
 }
 
 /**
@@ -5249,51 +5249,51 @@
 
 function write_test($filename)
 {
-  // We need to actually _open_ the file to make sure it can be written, because sometimes this fails even when is_writable() returns
-  // true on Windows/IIS servers. Don't ask me why.
-  
-  $file = ENANO_ROOT . '/' . $filename;
-  if ( is_dir($file) )
-  {
-    $file = rtrim($file, '/') . '/' . 'enanoinstalltest.txt';
-    if ( file_exists($file) )
-    {
-      $fp = @fopen($file, 'a+');
-      if ( !$fp )
-        return false;
-      fclose($fp);
-      unlink($file);
-      return true;
-    }
-    else
-    {
-      $fp = @fopen($file, 'w');
-      if ( !$fp )
-        return false;
-      fclose($fp);
-      unlink($file);
-      return true;
-    }
-  }
-  else
-  {
-    if ( file_exists($file) )
-    {
-      $fp = @fopen($file, 'a+');
-      if ( !$fp )
-        return false;
-      fclose($fp);
-      return true;
-    }
-    else
-    {
-      $fp = @fopen($file, 'w');
-      if ( !$fp )
-        return false;
-      fclose($fp);
-      return true;
-    }
-  }
+	// We need to actually _open_ the file to make sure it can be written, because sometimes this fails even when is_writable() returns
+	// true on Windows/IIS servers. Don't ask me why.
+	
+	$file = ENANO_ROOT . '/' . $filename;
+	if ( is_dir($file) )
+	{
+		$file = rtrim($file, '/') . '/' . 'enanoinstalltest.txt';
+		if ( file_exists($file) )
+		{
+			$fp = @fopen($file, 'a+');
+			if ( !$fp )
+				return false;
+			fclose($fp);
+			unlink($file);
+			return true;
+		}
+		else
+		{
+			$fp = @fopen($file, 'w');
+			if ( !$fp )
+				return false;
+			fclose($fp);
+			unlink($file);
+			return true;
+		}
+	}
+	else
+	{
+		if ( file_exists($file) )
+		{
+			$fp = @fopen($file, 'a+');
+			if ( !$fp )
+				return false;
+			fclose($fp);
+			return true;
+		}
+		else
+		{
+			$fp = @fopen($file, 'w');
+			if ( !$fp )
+				return false;
+			fclose($fp);
+			return true;
+		}
+	}
 }
 
 /**
@@ -5303,20 +5303,20 @@
 
 function install_get_crypto_backend()
 {
-  $crypto_backend = 'none';
-
-  // Extension test: BCMath
-  if ( function_exists('bcadd') )
-    $crypto_backend = 'bcmath';
-  
-  // Extension test: Big_Int
-  if ( function_exists('bi_from_str') )
-    $crypto_backend = 'bigint';
-  
-  // Extension test: GMP
-  if ( function_exists('gmp_init') )
-    $crypto_backend = 'gmp';
-  
-  return $crypto_backend;
+	$crypto_backend = 'none';
+
+	// Extension test: BCMath
+	if ( function_exists('bcadd') )
+		$crypto_backend = 'bcmath';
+	
+	// Extension test: Big_Int
+	if ( function_exists('bi_from_str') )
+		$crypto_backend = 'bigint';
+	
+	// Extension test: GMP
+	if ( function_exists('gmp_init') )
+		$crypto_backend = 'gmp';
+	
+	return $crypto_backend;
 }
 
--- a/includes/hmac.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/hmac.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,68 +14,68 @@
 
 function hmac_core($message, $key, $hashfunc)
 {
-  if ( strlen($key) % 2 == 1 )
-    $key .= '0';
-  
-  if ( strlen($key) > 128 )
-    $key = $hashfunc($key);
-  
-  while ( strlen($key) < 128 )
-  {
-    $key .= '00';
-  }
-  $opad = hmac_hexbytearray($key);
-  $ipad = $opad;
-  for ( $i = 0; $i < count($ipad); $i++ )
-  {
-    $opad[$i] = $opad[$i] ^ 0x5c;
-    $ipad[$i] = $ipad[$i] ^ 0x36;
-  }
-  $opad = hmac_bytearraytostring($opad);
-  $ipad = hmac_bytearraytostring($ipad);
-  return $hashfunc($opad . hexdecode($hashfunc($ipad . $message)));
+	if ( strlen($key) % 2 == 1 )
+		$key .= '0';
+	
+	if ( strlen($key) > 128 )
+		$key = $hashfunc($key);
+	
+	while ( strlen($key) < 128 )
+	{
+		$key .= '00';
+	}
+	$opad = hmac_hexbytearray($key);
+	$ipad = $opad;
+	for ( $i = 0; $i < count($ipad); $i++ )
+	{
+		$opad[$i] = $opad[$i] ^ 0x5c;
+		$ipad[$i] = $ipad[$i] ^ 0x36;
+	}
+	$opad = hmac_bytearraytostring($opad);
+	$ipad = hmac_bytearraytostring($ipad);
+	return $hashfunc($opad . hexdecode($hashfunc($ipad . $message)));
 }
 
 function hmac_hexbytearray($val)
 {
-  $val = hexdecode($val);
-  return hmac_bytearray($val);
+	$val = hexdecode($val);
+	return hmac_bytearray($val);
 }
 
 function hmac_bytearray($val)
 {
-  $val = str_split($val, 1);
-  foreach ( $val as &$char )
-  {
-    $char = ord($char);
-  }
-  return $val;
+	$val = str_split($val, 1);
+	foreach ( $val as &$char )
+	{
+		$char = ord($char);
+	}
+	return $val;
 }
 
 function hmac_bytearraytostring($val)
 {
-  foreach ( $val as &$char )
-  {
-    $char = chr($char);
-  }
-  return implode('', $val);
+	foreach ( $val as &$char )
+	{
+		$char = chr($char);
+	}
+	return implode('', $val);
 }
 
 function hmac_md5($message, $key)
 {
-  return hmac_core($message, $key, 'md5');
+	return hmac_core($message, $key, 'md5');
 }
 
 function hmac_sha1($message, $key)
 {
-  return hmac_core($message, $key, 'sha1');
+	return hmac_core($message, $key, 'sha1');
 }
 
 function hmac_sha256($message, $key)
 {
-  require_once(ENANO_ROOT . '/includes/math.php');
-  require_once(ENANO_ROOT . '/includes/diffiehellman.php');
-  return hmac_core($message, $key, 'sha256');
+	require_once(ENANO_ROOT . '/includes/math.php');
+	require_once(ENANO_ROOT . '/includes/diffiehellman.php');
+	return hmac_core($message, $key, 'sha256');
 }
 
 ?>
--- a/includes/http.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/http.php	Sun Mar 28 23:10:46 2010 -0400
@@ -88,844 +88,844 @@
 
 class Request_HTTP
 {
-  
-  /**
-   * Switch to enable or disable debugging. You want this off on production sites.
-   * @var bool
-   */
-  
-  var $debug = false;
-  
-  /**
-   * The host the request will be sent to.
-   * @var string
-   */
-  
-  var $host = '';
-  
-  /**
-   * The TCP port our connection is (will be) on.
-   * @var int
-   */
-  
-  var $port = 80;
-  
-  /**
-   * The request method. Can be GET or POST, defaults to GET.
-   * @var string
-   */
-  
-  var $method = 'GET';
-  
-  /**
-   * The URI to the remote script.
-   * @var string
-   */
-  
-  var $uri = '';
-  
-  /**
-   * The parameters to be sent on GET.
-   * @var array (associative)
-   */
-  
-  var $parms_get = array();
-  
-  /**
-   * The parameters to be sent on POST. Ignored if $this->method == GET.
-   * @var array (associative)
-   */
-  
-  var $parms_post = array();
-  
-  /**
-   * The list of cookies that will be sent.
-   * @var array (associative)
-   */
-  
-  var $cookies_out = array();
-  
-  /**
-   * Additional request headers.
-   * @var array (associative)
-   */
-  
-  var $headers = array();
-  
-  /**
-   * Follow server-side redirects; defaults to true.
-   * @var bool
-   */
-  
-  var $follow_redirects = true;
-  
-  /**
-   * Cached response.
-   * @var string, or bool:false if the request hasn't been sent yet
-   */
-  
-  var $response = false;
-  
-  /**
-   * Cached response code
-   * @var int set to -1 if request hasn't been sent yet
-   */
-  
-  var $response_code = -1;
-  
-  /**
-   * Cached response code string
-   * @var string or bool:false if the request hasn't been sent yet
-   */
-  
-  var $response_string = false;
-  
-  /**
-   * Resource for the socket. False if a connection currently isn't going.
-   * @var resource
-   */
-  
-  var $socket = false;
-  
-  /**
-   * True if SSL is on, defaults to false
-   * @var bool
-   */
-  
-  var $ssl = false;
-  
-  /**
-   * The state of our request. 0 means it hasn't been made yet. 1 means the socket is open, 2 means the socket is open and the request has been written, 3 means the headers have been fetched, and 4 means the request is completed.
-   * @var int
-   */
-  
-  var $state = 0;
-  
-  /**
-   * Constructor.
-   * @param string Hostname to send to
-   * @param string URI (/index.php)
-   * @param string Request method - GET or POST.
-   * @param int Optional. The port to open the request on. Defaults to 80.
-   * @param bool If true, uses SSL (and defaults the port to 443)
-   */
-  
-  function Request_HTTP($host, $uri, $method = 'GET', $port = 'default', $ssl = false)
-  {
-    if ( !preg_match('/^(?:(([a-z0-9-]+\.)*?)([a-z0-9-]+)|\[[a-f0-9:]+\])$/', $host) )
-      throw new Exception(__CLASS__ . ': Invalid hostname');
-    if ( $ssl )
-    {
-      $this->ssl = true;
-      $port = ( $port === 'default' ) ? 443 : $port;
-    }
-    else
-    {
-      $this->ssl = false;
-      $port = ( $port === 'default' ) ? 80 : $port;
-    }
-    // Yes - this really does support IPv6 URLs!
-    $this->host = $host;
-    $this->uri = $uri;
-    if ( is_int($port) && $port >= 1 && $port <= 65535 )
-      $this->port = $port;
-    else
-      throw new Exception(__CLASS__ . ': Invalid port');
-    $method = strtoupper($method);
-    if ( $method == 'GET' || $method == 'POST' )
-      $this->method = $method;
-    else
-      throw new Exception(__CLASS__ . ': Invalid request method');
-      
-    $newline = "\r\n";
-    $php_ver = PHP_VERSION;
-    $server = ( isset($_SERVER['SERVER_SOFTWARE']) ) ? "Server: {$_SERVER['SERVER_SOFTWARE']}" : "CLI";
-    $this->add_header('User-Agent', "PHP/$php_ver ({$server}; automated bot request)");
-  }
-  
-  /**
-   * Sets one or more cookies to be sent to the server.
-   * @param string or array If a string, the cookie name. If an array, associative array in the form of cookiename => cookievalue
-   * @param string or bool If a string, the cookie value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
-   */
-  
-  function add_cookie($cookiename, $cookievalue = false)
-  {
-    if ( is_array($cookiename) && !$cookievalue )
-    {
-      foreach ( $cookiename as $name => $value )
-      {
-        $this->cookies_out[$name] = $value;
-      }
-    }
-    else if ( is_string($cookiename) && is_string($cookievalue) )
-    {
-      $this->cookies_out[$cookiename] = $cookievalue;
-    }
-    else
-    {
-      throw new Exception(__METHOD__ . ': Invalid argument(s)');
-    }
-  }
-  
-  /**
-   * Sets one or more request header values.
-   * @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
-   * @param string or bool If a string, the header value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
-   */
-  
-  function add_header($headername, $headervalue = false)
-  {
-    if ( is_array($headername) && !$headervalue )
-    {
-      foreach ( $headername as $name => $value )
-      {
-        $this->headers[$name] = $value;
-      }
-    }
-    else if ( is_string($headername) && is_string($headervalue) )
-    {
-      $this->headers[$headername] = $headervalue;
-    }
-    else
-    {
-      throw new Exception(__METHOD__ . ': Invalid argument(s)');
-    }
-  }
-  
-  /**
-   * Adds one or more values to be passed on GET.
-   * @param string or array If a string, the parameter name. If an array, associative array in the form of parametername => parametervalue
-   * @param string or bool If a string, the parameter value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
-   */
-  
-  function add_get($getname, $getvalue = false)
-  {
-    if ( is_array($getname) && !$getvalue )
-    {
-      foreach ( $getname as $name => $value )
-      {
-        $this->parms_get[$name] = $value;
-      }
-    }
-    else if ( is_string($getname) && is_string($getvalue) )
-    {
-      $this->parms_get[$getname] = $getvalue;
-    }
-    else
-    {
-      throw new Exception(__METHOD__ . ': Invalid argument(s)');
-    }
-  }
-  
-  /**
-   * Adds one or more values to be passed on POST.
-   * @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
-   * @param string or bool If a string, the header value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
-   */
-  
-  function add_post($postname, $postvalue = false)
-  {
-    if ( is_array($postname) && !$postvalue )
-    {
-      foreach ( $postname as $name => $value )
-      {
-        $this->parms_post[$name] = $value;
-      }
-    }
-    else if ( is_string($postname) && is_string($postvalue) )
-    {
-      $this->parms_post[$postname] = $postvalue;
-    }
-    else
-    {
-      throw new Exception(__METHOD__ . ': Invalid argument(s)');
-    }
-  }
-  
-  /**
-   * Internal function to open up the socket.
-   * @access private
-   */
-  
-  function _sock_open(&$connection)
-  {
-    // Open connection
-    $ssl_prepend = ( $this->ssl ) ? 'ssl://' : '';
-    $connection = fsockopen($ssl_prepend . $this->host, $this->port, $errno, $errstr);
-    if ( !$connection )
-      throw new Exception(__METHOD__ . ": Could not make connection"); // to {$this->host}:{$this->port}: error $errno: $errstr");
-    
-    // 1 = socket open
-    $this->state = 1;
-  }
-  
-  /**
-   * Internal function to actually write the request into the socket.
-   * @access private
-   */
-  
-  function _write_request(&$connection, &$headers, &$cookies, &$get, &$post)
-  {
-    $newline = "\r\n";
-    
-    if ( $this->debug )
-      echo '<p>Connection opened. Writing main request to socket. Raw socket data follows.</p><pre>';
-    
-    if ( $this->debug )
-    {
-      echo '<hr /><div style="white-space: nowrap;">';
-      echo '<p><b>' . __CLASS__ . ': Sending request</b></p><p>Request parameters:</p>';
-      echo "<p><b>Headers:</b></p><pre>$headers</pre>";
-      echo "<p><b>Cookies:</b> $cookies</p>";
-      echo "<p><b>GET URI:</b> " . htmlspecialchars($this->uri . $get) . "</p>";
-      echo "<p><b>POST DATA:</b> " . htmlspecialchars($post) . "</p>";
-      echo "<pre>";
-    }
-    
-    $portline = ( $this->port == 80 ) ? '' : ":$this->port";
-    
-    $this->_fputs($connection, "{$this->method} {$this->uri}{$get} HTTP/1.1{$newline}");
-    $this->_fputs($connection, "Host: {$this->host}$portline{$newline}");
-    $this->_fputs($connection, $headers);
-    $this->_fputs($connection, $cookies);
-    
-    if ( $this->method == 'POST' )
-    {
-      // POST-specific parameters
-      $post_length = strlen($post);
-      $this->_fputs($connection, "Content-type: application/x-www-form-urlencoded{$newline}");
-      $this->_fputs($connection, "Content-length: {$post_length}{$newline}");
-    }
-    
-    $this->_fputs($connection, "Connection: close{$newline}");
-    $this->_fputs($connection, "{$newline}");
-    
-    if ( $this->method == 'POST' )
-    {
-      $this->_fputs($connection, $post);
-    }
-    
-    if ( $this->debug )
-      echo '</pre><p>Request written. Fetching response.</p>';
-    
-    // 2 = request written
-    $this->state = 2;
-  }
-  
-  /**
-   * Wrap up and close the socket. Nothing more than a call to fsockclose() except in debug mode.
-   * @access private
-   */
-  
-  function sock_close(&$connection)
-  {
-    if ( $this->debug )
-    {
-      echo '<p>Response fetched. Closing connection. Response text follows.</p><pre>';
-      echo htmlspecialchars($this->response);
-      echo '</pre></div><hr />';
-    }
-    
-    fclose($connection);
-    $this->state = 0;
-  }
-  
-  /**
-   * Internal function to grab the response code and status string
-   * @access string
-   */
-  
-  function _parse_response_code($buffer)
-  {
-    // Retrieve response code and status
-    $pos_newline = strpos($buffer, "\n");
-    $pos_carriage_return = strpos($buffer, "\r");
-    $pos_end_first_line = ( $pos_carriage_return < $pos_newline && $pos_carriage_return > 0 ) ? $pos_carriage_return : $pos_newline;
-    
-    // First line is in format of:
-    // HTTP/1.1 ### Blah blah blah(\r?)\n
-    $response_code = substr($buffer, 9, 3);
-    $response_string = substr($buffer, 13, ( $pos_end_first_line - 13 ) );
-    $this->response_code = intval($response_code);
-    $this->response_string = $response_string;
-  }
-  
-  /**
-   * Internal function to send the request.
-   * @access private
-   */
-  
-  function _send_request()
-  {
-    $this->concat_headers($headers, $cookies, $get, $post);
-    
-    if ( $this->state < 1 )
-    {
-      $this->_sock_open($this->socket);
-    }
-    if ( $this->state < 2 )
-    {
-      $this->_write_request($this->socket, $headers, $cookies, $get, $post);
-    }
-    if ( $this->state == 2 )
-    {
-      $buffer = $this->_read_until_newlines($this->socket);
-      $this->state = 3;
-      $this->_parse_response_code($buffer);
-      $this->response = $buffer;
-    }
-    // obey redirects
-    $i = 0;
-    while ( $i < 20 && $this->follow_redirects )
-    {
-      $incoming_headers = $this->get_response_headers_array();
-      if ( !$incoming_headers )
-        break;
-      if ( isset($incoming_headers['Location']) )
-      {
-        // we've been redirected...
-        $new_uri = $this->_resolve_uri($incoming_headers['Location']);
-        if ( !$new_uri )
-        {
-          // ... bad URI, ignore Location header.
-          break;
-        }
-        // change location
-        $this->host = $new_uri['host'];
-        $this->port = $new_uri['port'];
-        $this->uri  = $new_uri['uri'];
-        $get = '';
-        
-        // reset
-        $this->sock_close($this->socket);
-        $this->_sock_open($this->socket);
-        $this->_write_request($this->socket, $headers, $cookies, $get, $post);
-        $buffer = $this->_read_until_newlines($this->socket);
-        $this->state = 3;
-        $this->_parse_response_code($buffer);
-        $this->response = $buffer;
-        $i++;
-      }
-      else
-      {
-        break;
-      }
-    }
-    if ( $i == 20 )
-    {
-      throw new Exception(__METHOD__ . ": Redirect trap. Request_HTTP doesn't do cookies, btw.");
-    }
-    
-    if ( $this->state == 3 )
-    {
-      // Determine transfer encoding
-      $is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
-      if ( preg_match("/^Content-Length: ([0-9]+)[\s]*$/mi", $this->response, $match) && !$is_chunked )
-      {
-        $size = intval($match[1]);
-        if ( $this->debug )
-        {
-          echo "Pulling response using fread(), size $size\n";
-        }
-        $this->response .= fread($this->socket, $size);
-      }
-      else
-      {
-        if ( $this->debug )
-          echo "Pulling response using chunked handler\n";
-          
-        $buffer = '';
-        while ( !feof($this->socket) )
-        {
-          $part = fgets($this->socket, 1024);
-          if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
-          {
-            $chunklen = hexdec($match[1]);
-            $part = ( $chunklen > 0 ) ? fread($this->socket, $chunklen) : '';
-            // remove the last newline from $part
-            $part = preg_replace("/\r?\n\$/", "", $part);
-          }
-          $buffer .= $part;
-        }
-        $this->response .= $buffer;
-      }
-    }
-    $this->state = 4;
-    
-    $this->sock_close($this->socket);
-    $this->socket = false;
-  }
-  
-  /**
-   * Internal function to send the request but only fetch the headers. Leaves a connection open for a finish-up function.
-   * @access private
-   */
-  
-  function _send_request_headers_only()
-  {
-    $this->concat_headers($headers, $cookies, $get, $post);
-    
-    if ( $this->state < 1 )
-    {
-      $this->_sock_open($this->socket);
-    }
-    if ( $this->state < 2 )
-    {
-      $this->_write_request($this->socket, $headers, $cookies, $get, $post);
-    }
-    if ( $this->state == 2 )
-    {
-      $buffer = $this->_read_until_newlines($this->socket);
-      $this->state = 3;
-      $this->_parse_response_code($buffer);
-      $this->response = $buffer;
-    }
-  }
-  
-  /**
-   * Internal function to read from a socket until two consecutive newlines are hit.
-   * @access private
-   */
-  
-  function _read_until_newlines($sock)
-  {
-    $prev_char = '';
-    $prev1_char = '';
-    $prev2_char = '';
-    $buf = '';
-    while ( !feof($sock) )
-    {
-      $chr = fread($sock, 1);
-      $buf .= $chr;
-      if ( ( $chr == "\n" && $prev_char == "\n" ) ||
-           ( $chr == "\n" && $prev_char == "\r" && $prev1_char == "\n" && $prev2_char == "\r" ) )
-      {
-        return $buf;
-      }
-      $prev2_char = $prev1_char;
-      $prev1_char = $prev_char;
-      $prev_char = $chr;
-    }
-    return $buf;
-  }
-  
-  /**
-   * Returns the response text. If the request hasn't been sent, it will be sent here.
-   * @return string
-   */
-  
-  function get_response()
-  {
-    if ( $this->state == 4 )
-      return $this->response;
-    $this->_send_request();
-    return $this->response;
-  }
-  
-  /**
-   * Writes the response body to a file. This is good for conserving memory when downloading large files. If the file already exists it will be overwritten.
-   * @param string File to write to
-   * @param int Chunk size in KB to read from the socket. Optional and should only be needed in circumstances when extreme memory conservation is needed. Defaults to 768.
-   * @param int Maximum file size. Defaults to 0, which means no limit.
-   * @return bool True on success, false on failure
-   */
-  
-  function write_response_to_file($file, $chunklen = 768, $max_file_size = 0)
-  {
-    if ( !is_writeable( dirname($file) ) || !file_exists( dirname($file) ) )
-    {
-      return false;
-    }
-    $handle = @fopen($file, 'w');
-    if ( !$handle )
-      return false;
-    $chunklen = intval($chunklen);
-    if ( $chunklen < 1 )
-      return false;
-    if ( $this->state == 4 )
-    {
-      // we already have the response, so cheat
-      $response = $this->get_response_body();
-      fwrite($handle, $response);
-    }
-    else
-    {
-      // read data from the socket, write it immediately, and unset to free memory
-      $headers = $this->get_response_headers();
-      $transferred_bytes = 0;
-      $bandwidth_exceeded = false;
-      // if transfer-encoding is chunked, read using chunk sizes the server specifies
-      $is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
-      if ( $is_chunked )
-      {
-        $buffer = '';
-        while ( !feof($this->socket) )
-        {
-          $part = fgets($this->socket, ( 1024 * $chunklen ));
-          // Theoretically if the encoding is really chunked then this should always match.
-          if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
-          {
-            $chunk_length = hexdec($match[1]);
-            $part = ( $chunk_length > 0 ) ? fread($this->socket, $chunk_length) : '';
-            // remove the last newline from $part
-            $part = preg_replace("/\r?\n\$/m", "", $part);
-          }
-          
-          $transferred_bytes += strlen($part);
-          if ( $max_file_size && $transferred_bytes > $max_file_size )
-          {
-            // truncate output to $max_file_size bytes
-            $partlen = $max_file_size - ( $transferred_bytes - strlen($part) );
-            $part = substr($part, 0, $partlen);
-            $bandwidth_exceeded = true;
-          }
-          fwrite($handle, $part);
-          if ( $bandwidth_exceeded )
-          {
-            break;
-          }
-        }
-      }
-      else
-      {
-        $first_chunk = fread($this->socket, ( 1024 * $chunklen ));
-        fwrite($handle, $first_chunk);
-        while ( !feof($this->socket) )
-        {
-          $chunk = fread($this->socket, ( 1024 * $chunklen ));
-          
-          $transferred_bytes += strlen($chunk);
-          if ( $max_file_size && $transferred_bytes > $max_file_size )
-          {
-            // truncate output to $max_file_size bytes
-            $partlen = $max_file_size - ( $transferred_bytes - strlen($chunk) );
-            $chunk = substr($chunk, 0, $partlen);
-            $bandwidth_exceeded = true;
-          }
-          
-          fwrite($handle, $chunk);
-          unset($chunk);
-          
-          if ( $bandwidth_exceeded )
-          {
-            break;
-          }
-        }
-      }
-    }
-    fclose($handle);
-    // close socket and reset state, since we haven't cached the response
-    $this->sock_close($this->socket);
-    $this->state = 0;
-    return ($bandwidth_exceeded) ? false : true;
-  }
-  
-  /**
-   * Resolves, based on current settings and URI, a URI string to an array consisting of a host, port, and new URI. Returns false on error.
-   * @param string
-   * @return array
-   */
-  
-  function _resolve_uri($uri)
-  {
-    // long ass regexp w00t
-    if ( !preg_match('#^(?:https?://((?:(?:[a-z0-9-]+\.)*)(?:[a-z0-9-]+)|\[[a-f0-9:]+\])(?::([0-9]+))?)?(/)(.*)$#i', $uri, $match) )
-    {
-      // bad target URI
-      return false;
-    }
-    $hostpart = $match[1];
-    if ( empty($hostpart) )
-    {
-      // use existing host
-      $host = $this->host;
-      $port = $this->port;
-    }
-    else
-    {
-      $host = $match[1];
-      $port = empty($match[2]) ? 80 : intval($match[2]);
-    }
-    // is this an absolute URI, or relative?
-    if ( empty($match[3]) )
-    {
-      // relative
-      $uri = dirname($this->uri) . $match[4];
-    }
-    else
-    {
-      // absolute
-      $uri = '/' . $match[4];
-    }
-    return array(
-        'host' => $host,
-        'port' => $port,
-        'uri'  => $uri
-      );
-  }
-  
-  /**
-   * Returns only the response headers.
-   * @return string
-   */
-  
-  function get_response_headers()
-  {
-    if ( $this->state == 3 )
-    {
-      return $this->response;
-    }
-    else if ( $this->state == 4 )
-    {
-      $pos_end = strpos($this->response, "\r\n\r\n");
-      if ( empty($pos_end) )
-      {
-        $pos_end = strpos($this->response, "\n\n");
-      }
-      $data = substr($this->response, 0, $pos_end);
-      return $data;
-    }
-    else
-    {
-      $this->_send_request_headers_only();
-      return $this->response;
-    }
-  }
-  
-  /**
-   * Returns only the response headers, as an associative array.
-   * @return array
-   */
-  
-  function get_response_headers_array()
-  {
-    $data = $this->get_response_headers();
-    preg_match_all("/(^|\n)([A-z0-9_-]+?): (.+?)(\r|\n|\$)/", $data, $matches);
-    $headers = array();
-    for ( $i = 0; $i < count($matches[0]); $i++ )
-    {
-      $headers[ $matches[2][$i] ] = $matches[3][$i];
-    }
-    return $headers;
-  }
-  
-  /**
-   * Returns only the body (not the headers) of the response. If the request hasn't been sent, it will be sent here.
-   * @return string
-   */
-  
-  function get_response_body()
-  {
-    $data = $this->get_response();
-    $pos_start = strpos($data, "\r\n\r\n") + 4;
-    if ( $pos_start == 4 )
-    {
-      $pos_start = strpos($data, "\n\n") + 4;
-    }
-    $data = substr($data, $pos_start);
-    return $data;
-  }
-  
-  /**
-   * Returns all cookies requested to be set by the server as an associative array. If the request hasn't been sent, it will be sent here.
-   * @return array
-   */
-  
-  function get_cookies()
-  {
-    $data = $this->get_response();
-    $data = str_replace("\r\n", "\n", $data);
-    $pos = strpos($data, "\n\n");
-    $headers = substr($data, 0, $pos);
-    preg_match_all("/Set-Cookie: ([a-z0-9_]+)=([^;]+);( expires=([^;]+);)?( path=(.*?))?\n/", $headers, $cookiematch);
-    if ( count($cookiematch[0]) < 1 )
-      return array();
-    $cookies = array();
-    foreach ( $cookiematch[0] as $i => $cookie )
-    {
-      $cookies[$cookiematch[1][$i]] = $cookiematch[2][$i];
-    }
-    return $cookies;
-  }
-  
-  /**
-   * Internal method to write data to a socket with debugging possibility.
-   * @access private
-   */
-  
-  function _fputs($socket, $data)
-  {
-    if ( $this->debug )
-      echo htmlspecialchars($data);
-    
-    return fputs($socket, $data);
-  }
-  
-  /**
-   * Internal function to stringify cookies, headers, get, and post.
-   * @access private
-   */
-  
-  function concat_headers(&$headers, &$cookies, &$get, &$post)
-  {
-    $headers = '';
-    $cookies = '';
-    foreach ( $this->headers as $name => $value )
-    {
-      $value = str_replace('\\n', '\\\\n', $value);
-      $value = str_replace("\n", '\\n', $value);
-      $headers .= "$name: $value\r\n";
-    }
-    unset($value);
-    if ( count($this->cookies_out) > 0 )
-    {
-      $i = 0;
-      $cookie_header = 'Cookie: ';
-      foreach ( $this->cookies_out as $name => $value )
-      {
-        $i++;
-        if ( $i > 1 )
-          $cookie_header .= '; ';
-        $value = str_replace(';', rawurlencode(';'), $value);
-        $value = str_replace('\\n', '\\\\n', $value);
-        $value = str_replace("\n", '\\n', $value);
-        $cookie_header .= "$name=$value";
-      }
-      $cookie_header .= "\r\n";
-      $cookies = $cookie_header;
-      unset($value, $cookie_header);
-    }
-    if ( count($this->parms_get) > 0 )
-    {
-      $get = '?';
-      $i = 0;
-      foreach ( $this->parms_get as $name => $value )
-      {
-        $i++;
-        if ( $i > 1 )
-          $get .= '&';
-        $value = urlencode($value);
-        if ( !empty($value) || is_string($value) )
-          $get .= "$name=$value";
-        else
-          $get .= "$name";
-      }
-    }
-    if ( count($this->parms_post) > 0 )
-    {
-      $post = '';
-      $i = 0;
-      foreach ( $this->parms_post as $name => $value )
-      {
-        $i++;
-        if ( $i > 1 )
-          $post .= '&';
-        $value = urlencode($value);
-        $post .= "$name=$value";
-      }
-    }
-  }
-  
+	
+	/**
+ 	* Switch to enable or disable debugging. You want this off on production sites.
+ 	* @var bool
+ 	*/
+	
+	var $debug = false;
+	
+	/**
+ 	* The host the request will be sent to.
+ 	* @var string
+ 	*/
+	
+	var $host = '';
+	
+	/**
+ 	* The TCP port our connection is (will be) on.
+ 	* @var int
+ 	*/
+	
+	var $port = 80;
+	
+	/**
+ 	* The request method. Can be GET or POST, defaults to GET.
+ 	* @var string
+ 	*/
+	
+	var $method = 'GET';
+	
+	/**
+ 	* The URI to the remote script.
+ 	* @var string
+ 	*/
+	
+	var $uri = '';
+	
+	/**
+ 	* The parameters to be sent on GET.
+ 	* @var array (associative)
+ 	*/
+	
+	var $parms_get = array();
+	
+	/**
+ 	* The parameters to be sent on POST. Ignored if $this->method == GET.
+ 	* @var array (associative)
+ 	*/
+	
+	var $parms_post = array();
+	
+	/**
+ 	* The list of cookies that will be sent.
+ 	* @var array (associative)
+ 	*/
+	
+	var $cookies_out = array();
+	
+	/**
+ 	* Additional request headers.
+ 	* @var array (associative)
+ 	*/
+	
+	var $headers = array();
+	
+	/**
+ 	* Follow server-side redirects; defaults to true.
+ 	* @var bool
+ 	*/
+	
+	var $follow_redirects = true;
+	
+	/**
+ 	* Cached response.
+ 	* @var string, or bool:false if the request hasn't been sent yet
+ 	*/
+	
+	var $response = false;
+	
+	/**
+ 	* Cached response code
+ 	* @var int set to -1 if request hasn't been sent yet
+ 	*/
+	
+	var $response_code = -1;
+	
+	/**
+ 	* Cached response code string
+ 	* @var string or bool:false if the request hasn't been sent yet
+ 	*/
+	
+	var $response_string = false;
+	
+	/**
+ 	* Resource for the socket. False if a connection currently isn't going.
+ 	* @var resource
+ 	*/
+	
+	var $socket = false;
+	
+	/**
+ 	* True if SSL is on, defaults to false
+ 	* @var bool
+ 	*/
+	
+	var $ssl = false;
+	
+	/**
+ 	* The state of our request. 0 means it hasn't been made yet. 1 means the socket is open, 2 means the socket is open and the request has been written, 3 means the headers have been fetched, and 4 means the request is completed.
+ 	* @var int
+ 	*/
+	
+	var $state = 0;
+	
+	/**
+ 	* Constructor.
+ 	* @param string Hostname to send to
+ 	* @param string URI (/index.php)
+ 	* @param string Request method - GET or POST.
+ 	* @param int Optional. The port to open the request on. Defaults to 80.
+ 	* @param bool If true, uses SSL (and defaults the port to 443)
+ 	*/
+	
+	function Request_HTTP($host, $uri, $method = 'GET', $port = 'default', $ssl = false)
+	{
+		if ( !preg_match('/^(?:(([a-z0-9-]+\.)*?)([a-z0-9-]+)|\[[a-f0-9:]+\])$/', $host) )
+			throw new Exception(__CLASS__ . ': Invalid hostname');
+		if ( $ssl )
+		{
+			$this->ssl = true;
+			$port = ( $port === 'default' ) ? 443 : $port;
+		}
+		else
+		{
+			$this->ssl = false;
+			$port = ( $port === 'default' ) ? 80 : $port;
+		}
+		// Yes - this really does support IPv6 URLs!
+		$this->host = $host;
+		$this->uri = $uri;
+		if ( is_int($port) && $port >= 1 && $port <= 65535 )
+			$this->port = $port;
+		else
+			throw new Exception(__CLASS__ . ': Invalid port');
+		$method = strtoupper($method);
+		if ( $method == 'GET' || $method == 'POST' )
+			$this->method = $method;
+		else
+			throw new Exception(__CLASS__ . ': Invalid request method');
+			
+		$newline = "\r\n";
+		$php_ver = PHP_VERSION;
+		$server = ( isset($_SERVER['SERVER_SOFTWARE']) ) ? "Server: {$_SERVER['SERVER_SOFTWARE']}" : "CLI";
+		$this->add_header('User-Agent', "PHP/$php_ver ({$server}; automated bot request)");
+	}
+	
+	/**
+ 	* Sets one or more cookies to be sent to the server.
+ 	* @param string or array If a string, the cookie name. If an array, associative array in the form of cookiename => cookievalue
+ 	* @param string or bool If a string, the cookie value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
+ 	*/
+	
+	function add_cookie($cookiename, $cookievalue = false)
+	{
+		if ( is_array($cookiename) && !$cookievalue )
+		{
+			foreach ( $cookiename as $name => $value )
+			{
+				$this->cookies_out[$name] = $value;
+			}
+		}
+		else if ( is_string($cookiename) && is_string($cookievalue) )
+		{
+			$this->cookies_out[$cookiename] = $cookievalue;
+		}
+		else
+		{
+			throw new Exception(__METHOD__ . ': Invalid argument(s)');
+		}
+	}
+	
+	/**
+ 	* Sets one or more request header values.
+ 	* @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
+ 	* @param string or bool If a string, the header value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
+ 	*/
+	
+	function add_header($headername, $headervalue = false)
+	{
+		if ( is_array($headername) && !$headervalue )
+		{
+			foreach ( $headername as $name => $value )
+			{
+				$this->headers[$name] = $value;
+			}
+		}
+		else if ( is_string($headername) && is_string($headervalue) )
+		{
+			$this->headers[$headername] = $headervalue;
+		}
+		else
+		{
+			throw new Exception(__METHOD__ . ': Invalid argument(s)');
+		}
+	}
+	
+	/**
+ 	* Adds one or more values to be passed on GET.
+ 	* @param string or array If a string, the parameter name. If an array, associative array in the form of parametername => parametervalue
+ 	* @param string or bool If a string, the parameter value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
+ 	*/
+	
+	function add_get($getname, $getvalue = false)
+	{
+		if ( is_array($getname) && !$getvalue )
+		{
+			foreach ( $getname as $name => $value )
+			{
+				$this->parms_get[$name] = $value;
+			}
+		}
+		else if ( is_string($getname) && is_string($getvalue) )
+		{
+			$this->parms_get[$getname] = $getvalue;
+		}
+		else
+		{
+			throw new Exception(__METHOD__ . ': Invalid argument(s)');
+		}
+	}
+	
+	/**
+ 	* Adds one or more values to be passed on POST.
+ 	* @param string or array If a string, the header name. If an array, associative array in the form of headername => headervalue
+ 	* @param string or bool If a string, the header value. If boolean, defaults to false, param 1 should be an array, and this should not be passed.
+ 	*/
+	
+	function add_post($postname, $postvalue = false)
+	{
+		if ( is_array($postname) && !$postvalue )
+		{
+			foreach ( $postname as $name => $value )
+			{
+				$this->parms_post[$name] = $value;
+			}
+		}
+		else if ( is_string($postname) && is_string($postvalue) )
+		{
+			$this->parms_post[$postname] = $postvalue;
+		}
+		else
+		{
+			throw new Exception(__METHOD__ . ': Invalid argument(s)');
+		}
+	}
+	
+	/**
+ 	* Internal function to open up the socket.
+ 	* @access private
+ 	*/
+	
+	function _sock_open(&$connection)
+	{
+		// Open connection
+		$ssl_prepend = ( $this->ssl ) ? 'ssl://' : '';
+		$connection = fsockopen($ssl_prepend . $this->host, $this->port, $errno, $errstr);
+		if ( !$connection )
+			throw new Exception(__METHOD__ . ": Could not make connection"); // to {$this->host}:{$this->port}: error $errno: $errstr");
+		
+		// 1 = socket open
+		$this->state = 1;
+	}
+	
+	/**
+ 	* Internal function to actually write the request into the socket.
+ 	* @access private
+ 	*/
+	
+	function _write_request(&$connection, &$headers, &$cookies, &$get, &$post)
+	{
+		$newline = "\r\n";
+		
+		if ( $this->debug )
+			echo '<p>Connection opened. Writing main request to socket. Raw socket data follows.</p><pre>';
+		
+		if ( $this->debug )
+		{
+			echo '<hr /><div style="white-space: nowrap;">';
+			echo '<p><b>' . __CLASS__ . ': Sending request</b></p><p>Request parameters:</p>';
+			echo "<p><b>Headers:</b></p><pre>$headers</pre>";
+			echo "<p><b>Cookies:</b> $cookies</p>";
+			echo "<p><b>GET URI:</b> " . htmlspecialchars($this->uri . $get) . "</p>";
+			echo "<p><b>POST DATA:</b> " . htmlspecialchars($post) . "</p>";
+			echo "<pre>";
+		}
+		
+		$portline = ( $this->port == 80 ) ? '' : ":$this->port";
+		
+		$this->_fputs($connection, "{$this->method} {$this->uri}{$get} HTTP/1.1{$newline}");
+		$this->_fputs($connection, "Host: {$this->host}$portline{$newline}");
+		$this->_fputs($connection, $headers);
+		$this->_fputs($connection, $cookies);
+		
+		if ( $this->method == 'POST' )
+		{
+			// POST-specific parameters
+			$post_length = strlen($post);
+			$this->_fputs($connection, "Content-type: application/x-www-form-urlencoded{$newline}");
+			$this->_fputs($connection, "Content-length: {$post_length}{$newline}");
+		}
+		
+		$this->_fputs($connection, "Connection: close{$newline}");
+		$this->_fputs($connection, "{$newline}");
+		
+		if ( $this->method == 'POST' )
+		{
+			$this->_fputs($connection, $post);
+		}
+		
+		if ( $this->debug )
+			echo '</pre><p>Request written. Fetching response.</p>';
+		
+		// 2 = request written
+		$this->state = 2;
+	}
+	
+	/**
+ 	* Wrap up and close the socket. Nothing more than a call to fsockclose() except in debug mode.
+ 	* @access private
+ 	*/
+	
+	function sock_close(&$connection)
+	{
+		if ( $this->debug )
+		{
+			echo '<p>Response fetched. Closing connection. Response text follows.</p><pre>';
+			echo htmlspecialchars($this->response);
+			echo '</pre></div><hr />';
+		}
+		
+		fclose($connection);
+		$this->state = 0;
+	}
+	
+	/**
+ 	* Internal function to grab the response code and status string
+ 	* @access string
+ 	*/
+	
+	function _parse_response_code($buffer)
+	{
+		// Retrieve response code and status
+		$pos_newline = strpos($buffer, "\n");
+		$pos_carriage_return = strpos($buffer, "\r");
+		$pos_end_first_line = ( $pos_carriage_return < $pos_newline && $pos_carriage_return > 0 ) ? $pos_carriage_return : $pos_newline;
+		
+		// First line is in format of:
+		// HTTP/1.1 ### Blah blah blah(\r?)\n
+		$response_code = substr($buffer, 9, 3);
+		$response_string = substr($buffer, 13, ( $pos_end_first_line - 13 ) );
+		$this->response_code = intval($response_code);
+		$this->response_string = $response_string;
+	}
+	
+	/**
+ 	* Internal function to send the request.
+ 	* @access private
+ 	*/
+	
+	function _send_request()
+	{
+		$this->concat_headers($headers, $cookies, $get, $post);
+		
+		if ( $this->state < 1 )
+		{
+			$this->_sock_open($this->socket);
+		}
+		if ( $this->state < 2 )
+		{
+			$this->_write_request($this->socket, $headers, $cookies, $get, $post);
+		}
+		if ( $this->state == 2 )
+		{
+			$buffer = $this->_read_until_newlines($this->socket);
+			$this->state = 3;
+			$this->_parse_response_code($buffer);
+			$this->response = $buffer;
+		}
+		// obey redirects
+		$i = 0;
+		while ( $i < 20 && $this->follow_redirects )
+		{
+			$incoming_headers = $this->get_response_headers_array();
+			if ( !$incoming_headers )
+				break;
+			if ( isset($incoming_headers['Location']) )
+			{
+				// we've been redirected...
+				$new_uri = $this->_resolve_uri($incoming_headers['Location']);
+				if ( !$new_uri )
+				{
+					// ... bad URI, ignore Location header.
+					break;
+				}
+				// change location
+				$this->host = $new_uri['host'];
+				$this->port = $new_uri['port'];
+				$this->uri  = $new_uri['uri'];
+				$get = '';
+				
+				// reset
+				$this->sock_close($this->socket);
+				$this->_sock_open($this->socket);
+				$this->_write_request($this->socket, $headers, $cookies, $get, $post);
+				$buffer = $this->_read_until_newlines($this->socket);
+				$this->state = 3;
+				$this->_parse_response_code($buffer);
+				$this->response = $buffer;
+				$i++;
+			}
+			else
+			{
+				break;
+			}
+		}
+		if ( $i == 20 )
+		{
+			throw new Exception(__METHOD__ . ": Redirect trap. Request_HTTP doesn't do cookies, btw.");
+		}
+		
+		if ( $this->state == 3 )
+		{
+			// Determine transfer encoding
+			$is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
+			if ( preg_match("/^Content-Length: ([0-9]+)[\s]*$/mi", $this->response, $match) && !$is_chunked )
+			{
+				$size = intval($match[1]);
+				if ( $this->debug )
+				{
+					echo "Pulling response using fread(), size $size\n";
+				}
+				$this->response .= fread($this->socket, $size);
+			}
+			else
+			{
+				if ( $this->debug )
+					echo "Pulling response using chunked handler\n";
+					
+				$buffer = '';
+				while ( !feof($this->socket) )
+				{
+					$part = fgets($this->socket, 1024);
+					if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
+					{
+						$chunklen = hexdec($match[1]);
+						$part = ( $chunklen > 0 ) ? fread($this->socket, $chunklen) : '';
+						// remove the last newline from $part
+						$part = preg_replace("/\r?\n\$/", "", $part);
+					}
+					$buffer .= $part;
+				}
+				$this->response .= $buffer;
+			}
+		}
+		$this->state = 4;
+		
+		$this->sock_close($this->socket);
+		$this->socket = false;
+	}
+	
+	/**
+ 	* Internal function to send the request but only fetch the headers. Leaves a connection open for a finish-up function.
+ 	* @access private
+ 	*/
+	
+	function _send_request_headers_only()
+	{
+		$this->concat_headers($headers, $cookies, $get, $post);
+		
+		if ( $this->state < 1 )
+		{
+			$this->_sock_open($this->socket);
+		}
+		if ( $this->state < 2 )
+		{
+			$this->_write_request($this->socket, $headers, $cookies, $get, $post);
+		}
+		if ( $this->state == 2 )
+		{
+			$buffer = $this->_read_until_newlines($this->socket);
+			$this->state = 3;
+			$this->_parse_response_code($buffer);
+			$this->response = $buffer;
+		}
+	}
+	
+	/**
+ 	* Internal function to read from a socket until two consecutive newlines are hit.
+ 	* @access private
+ 	*/
+	
+	function _read_until_newlines($sock)
+	{
+		$prev_char = '';
+		$prev1_char = '';
+		$prev2_char = '';
+		$buf = '';
+		while ( !feof($sock) )
+		{
+			$chr = fread($sock, 1);
+			$buf .= $chr;
+			if ( ( $chr == "\n" && $prev_char == "\n" ) ||
+ 					( $chr == "\n" && $prev_char == "\r" && $prev1_char == "\n" && $prev2_char == "\r" ) )
+			{
+				return $buf;
+			}
+			$prev2_char = $prev1_char;
+			$prev1_char = $prev_char;
+			$prev_char = $chr;
+		}
+		return $buf;
+	}
+	
+	/**
+ 	* Returns the response text. If the request hasn't been sent, it will be sent here.
+ 	* @return string
+ 	*/
+	
+	function get_response()
+	{
+		if ( $this->state == 4 )
+			return $this->response;
+		$this->_send_request();
+		return $this->response;
+	}
+	
+	/**
+ 	* Writes the response body to a file. This is good for conserving memory when downloading large files. If the file already exists it will be overwritten.
+ 	* @param string File to write to
+ 	* @param int Chunk size in KB to read from the socket. Optional and should only be needed in circumstances when extreme memory conservation is needed. Defaults to 768.
+ 	* @param int Maximum file size. Defaults to 0, which means no limit.
+ 	* @return bool True on success, false on failure
+ 	*/
+	
+	function write_response_to_file($file, $chunklen = 768, $max_file_size = 0)
+	{
+		if ( !is_writeable( dirname($file) ) || !file_exists( dirname($file) ) )
+		{
+			return false;
+		}
+		$handle = @fopen($file, 'w');
+		if ( !$handle )
+			return false;
+		$chunklen = intval($chunklen);
+		if ( $chunklen < 1 )
+			return false;
+		if ( $this->state == 4 )
+		{
+			// we already have the response, so cheat
+			$response = $this->get_response_body();
+			fwrite($handle, $response);
+		}
+		else
+		{
+			// read data from the socket, write it immediately, and unset to free memory
+			$headers = $this->get_response_headers();
+			$transferred_bytes = 0;
+			$bandwidth_exceeded = false;
+			// if transfer-encoding is chunked, read using chunk sizes the server specifies
+			$is_chunked = preg_match("/Transfer-Encoding: (chunked)\r?\n/", $this->response);
+			if ( $is_chunked )
+			{
+				$buffer = '';
+				while ( !feof($this->socket) )
+				{
+					$part = fgets($this->socket, ( 1024 * $chunklen ));
+					// Theoretically if the encoding is really chunked then this should always match.
+					if ( $is_chunked && preg_match("/^([a-f0-9]+)\x0D\x0A$/", $part, $match) )
+					{
+						$chunk_length = hexdec($match[1]);
+						$part = ( $chunk_length > 0 ) ? fread($this->socket, $chunk_length) : '';
+						// remove the last newline from $part
+						$part = preg_replace("/\r?\n\$/m", "", $part);
+					}
+					
+					$transferred_bytes += strlen($part);
+					if ( $max_file_size && $transferred_bytes > $max_file_size )
+					{
+						// truncate output to $max_file_size bytes
+						$partlen = $max_file_size - ( $transferred_bytes - strlen($part) );
+						$part = substr($part, 0, $partlen);
+						$bandwidth_exceeded = true;
+					}
+					fwrite($handle, $part);
+					if ( $bandwidth_exceeded )
+					{
+						break;
+					}
+				}
+			}
+			else
+			{
+				$first_chunk = fread($this->socket, ( 1024 * $chunklen ));
+				fwrite($handle, $first_chunk);
+				while ( !feof($this->socket) )
+				{
+					$chunk = fread($this->socket, ( 1024 * $chunklen ));
+					
+					$transferred_bytes += strlen($chunk);
+					if ( $max_file_size && $transferred_bytes > $max_file_size )
+					{
+						// truncate output to $max_file_size bytes
+						$partlen = $max_file_size - ( $transferred_bytes - strlen($chunk) );
+						$chunk = substr($chunk, 0, $partlen);
+						$bandwidth_exceeded = true;
+					}
+					
+					fwrite($handle, $chunk);
+					unset($chunk);
+					
+					if ( $bandwidth_exceeded )
+					{
+						break;
+					}
+				}
+			}
+		}
+		fclose($handle);
+		// close socket and reset state, since we haven't cached the response
+		$this->sock_close($this->socket);
+		$this->state = 0;
+		return ($bandwidth_exceeded) ? false : true;
+	}
+	
+	/**
+ 	* Resolves, based on current settings and URI, a URI string to an array consisting of a host, port, and new URI. Returns false on error.
+ 	* @param string
+ 	* @return array
+ 	*/
+	
+	function _resolve_uri($uri)
+	{
+		// long ass regexp w00t
+		if ( !preg_match('#^(?:https?://((?:(?:[a-z0-9-]+\.)*)(?:[a-z0-9-]+)|\[[a-f0-9:]+\])(?::([0-9]+))?)?(/)(.*)$#i', $uri, $match) )
+		{
+			// bad target URI
+			return false;
+		}
+		$hostpart = $match[1];
+		if ( empty($hostpart) )
+		{
+			// use existing host
+			$host = $this->host;
+			$port = $this->port;
+		}
+		else
+		{
+			$host = $match[1];
+			$port = empty($match[2]) ? 80 : intval($match[2]);
+		}
+		// is this an absolute URI, or relative?
+		if ( empty($match[3]) )
+		{
+			// relative
+			$uri = dirname($this->uri) . $match[4];
+		}
+		else
+		{
+			// absolute
+			$uri = '/' . $match[4];
+		}
+		return array(
+				'host' => $host,
+				'port' => $port,
+				'uri'  => $uri
+			);
+	}
+	
+	/**
+ 	* Returns only the response headers.
+ 	* @return string
+ 	*/
+	
+	function get_response_headers()
+	{
+		if ( $this->state == 3 )
+		{
+			return $this->response;
+		}
+		else if ( $this->state == 4 )
+		{
+			$pos_end = strpos($this->response, "\r\n\r\n");
+			if ( empty($pos_end) )
+			{
+				$pos_end = strpos($this->response, "\n\n");
+			}
+			$data = substr($this->response, 0, $pos_end);
+			return $data;
+		}
+		else
+		{
+			$this->_send_request_headers_only();
+			return $this->response;
+		}
+	}
+	
+	/**
+ 	* Returns only the response headers, as an associative array.
+ 	* @return array
+ 	*/
+	
+	function get_response_headers_array()
+	{
+		$data = $this->get_response_headers();
+		preg_match_all("/(^|\n)([A-z0-9_-]+?): (.+?)(\r|\n|\$)/", $data, $matches);
+		$headers = array();
+		for ( $i = 0; $i < count($matches[0]); $i++ )
+		{
+			$headers[ $matches[2][$i] ] = $matches[3][$i];
+		}
+		return $headers;
+	}
+	
+	/**
+ 	* Returns only the body (not the headers) of the response. If the request hasn't been sent, it will be sent here.
+ 	* @return string
+ 	*/
+	
+	function get_response_body()
+	{
+		$data = $this->get_response();
+		$pos_start = strpos($data, "\r\n\r\n") + 4;
+		if ( $pos_start == 4 )
+		{
+			$pos_start = strpos($data, "\n\n") + 4;
+		}
+		$data = substr($data, $pos_start);
+		return $data;
+	}
+	
+	/**
+ 	* Returns all cookies requested to be set by the server as an associative array. If the request hasn't been sent, it will be sent here.
+ 	* @return array
+ 	*/
+	
+	function get_cookies()
+	{
+		$data = $this->get_response();
+		$data = str_replace("\r\n", "\n", $data);
+		$pos = strpos($data, "\n\n");
+		$headers = substr($data, 0, $pos);
+		preg_match_all("/Set-Cookie: ([a-z0-9_]+)=([^;]+);( expires=([^;]+);)?( path=(.*?))?\n/", $headers, $cookiematch);
+		if ( count($cookiematch[0]) < 1 )
+			return array();
+		$cookies = array();
+		foreach ( $cookiematch[0] as $i => $cookie )
+		{
+			$cookies[$cookiematch[1][$i]] = $cookiematch[2][$i];
+		}
+		return $cookies;
+	}
+	
+	/**
+ 	* Internal method to write data to a socket with debugging possibility.
+ 	* @access private
+ 	*/
+	
+	function _fputs($socket, $data)
+	{
+		if ( $this->debug )
+			echo htmlspecialchars($data);
+		
+		return fputs($socket, $data);
+	}
+	
+	/**
+ 	* Internal function to stringify cookies, headers, get, and post.
+ 	* @access private
+ 	*/
+	
+	function concat_headers(&$headers, &$cookies, &$get, &$post)
+	{
+		$headers = '';
+		$cookies = '';
+		foreach ( $this->headers as $name => $value )
+		{
+			$value = str_replace('\\n', '\\\\n', $value);
+			$value = str_replace("\n", '\\n', $value);
+			$headers .= "$name: $value\r\n";
+		}
+		unset($value);
+		if ( count($this->cookies_out) > 0 )
+		{
+			$i = 0;
+			$cookie_header = 'Cookie: ';
+			foreach ( $this->cookies_out as $name => $value )
+			{
+				$i++;
+				if ( $i > 1 )
+					$cookie_header .= '; ';
+				$value = str_replace(';', rawurlencode(';'), $value);
+				$value = str_replace('\\n', '\\\\n', $value);
+				$value = str_replace("\n", '\\n', $value);
+				$cookie_header .= "$name=$value";
+			}
+			$cookie_header .= "\r\n";
+			$cookies = $cookie_header;
+			unset($value, $cookie_header);
+		}
+		if ( count($this->parms_get) > 0 )
+		{
+			$get = '?';
+			$i = 0;
+			foreach ( $this->parms_get as $name => $value )
+			{
+				$i++;
+				if ( $i > 1 )
+					$get .= '&';
+				$value = urlencode($value);
+				if ( !empty($value) || is_string($value) )
+					$get .= "$name=$value";
+				else
+					$get .= "$name";
+			}
+		}
+		if ( count($this->parms_post) > 0 )
+		{
+			$post = '';
+			$i = 0;
+			foreach ( $this->parms_post as $name => $value )
+			{
+				$i++;
+				if ( $i > 1 )
+					$post .= '&';
+				$value = urlencode($value);
+				$post .= "$name=$value";
+			}
+		}
+	}
+	
 }
 
--- a/includes/js-compressor.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/js-compressor.php	Sun Mar 28 23:10:46 2010 -0400
@@ -84,8 +84,8 @@
 
 	/**
 	 * public variables
-         * 	stats:string		after every compression has some informations
-         *      version:string		version of this class
+ 				* 	stats:string		after every compression has some informations
+ 				*      version:string		version of this class
 	 */
 	var	$stats = '',
 		$version = '0.8';
@@ -104,7 +104,7 @@
 
 	/**
 	 * public constructor
-         * 	creates a new BaseConvert class variable (base 36)
+ 				* 	creates a new BaseConvert class variable (base 36)
 	 */
 	function __construct() {
 		$this->__SourceMap = new SourceMap();
@@ -120,9 +120,9 @@
 
 	/**
 	 * public method
-         * 	getClean(mixed [, bool]):string
-         *      compress JavaScript removing comments and somespaces (on by default)
-         * @param	mixed		view example and notes on class comments
+ 				* 	getClean(mixed [, bool]):string
+ 				*      compress JavaScript removing comments and somespaces (on by default)
+ 				* @param	mixed		view example and notes on class comments
 	 */
 	function getClean($jsSource) {
 		return $this->__commonInitMethods($jsSource, false);
@@ -130,9 +130,9 @@
 	
 	/**
 	 * public method
-         * 	getPacked(mixed):string
-         *      compress JavaScript replaceing words and removing comments and some spaces
-         * @param	mixed		view example and notes on class comments
+ 				* 	getPacked(mixed):string
+ 				*      compress JavaScript replaceing words and removing comments and some spaces
+ 				* @param	mixed		view example and notes on class comments
 	 */
 	function getPacked($jsSource) {
 		return $this->__commonInitMethods($jsSource, true);
@@ -373,124 +373,124 @@
 * @Application        Last version of JavaScriptCompressor class use this one to map source code.
 */
 class SourceMap {
-    
-    /**
-     * public method
-         *     getMap(&$source:string, &$delimeters:array):array
-     * Maps the source code using $delimeters rules and returns map as an array
-         * NOTE: read comments to know more about map and delimeter
-         *
-         * @param    string        generic source code
-         * @param    array        array with nested array with code rules
-     */
-    function getMap(&$source, &$delimeters) {
-        
-        # "unsigned" integer variables
-        $sourcePosition = 0;
-        $delimetersPosition = 0;
-        $findLength = 0;
-        $len = 0;
-        $tempIndex = 0;
-        $sourceLength = strlen($source);
-        $delimetersLength = count($delimeters);
-        
-        # integer variables
-        $tempPosition = -1;
-        $endPosition = -1;
-        
-        # array variables
-        $map = array();
-        $tempMap = array();
-        $tempDelimeter = array();
-        
-        while($sourcePosition < $sourceLength) {
-            $endPosition = -1;
-            for($delimetersPosition = 0; $delimetersPosition < $delimetersLength; $delimetersPosition++) {
-                $tempPosition = strpos($source, $delimeters[$delimetersPosition]['start'], $sourcePosition);
-                if($tempPosition !== false && ($tempPosition < $endPosition || $endPosition === -1)) {
-                    $endPosition = $tempPosition;
-                    $tempIndex = $delimetersPosition;
-                }
-            }
-            if($endPosition !== -1) {
-                $sourcePosition = $endPosition;
-                $tempDelimeter = &$delimeters[$tempIndex];
-                $findLength = strlen($tempDelimeter['start']);
-                if(is_array($tempDelimeter['end'])) {
-                    $delimetersPosition = 0;
-                    $endPosition = -1;
-                    for($len = count($tempDelimeter['end']); $delimetersPosition < $len; $delimetersPosition++) {
-                        $tempPosition = strpos($source, $tempDelimeter['end'][$delimetersPosition], $sourcePosition + $findLength);
-                        if($tempPosition !== false && ($tempPosition < $endPosition || $endPosition === -1)) {
-                            $endPosition = $tempPosition;
-                            $tempIndex = $delimetersPosition;
-                        }    
-                    }
-                    if($endPosition !== -1)
-                        $endPosition = $endPosition + strlen($tempDelimeter['end'][$tempIndex]);
-                    else
-                        $endPosition = $sourceLength;
-                    array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition));
-                    $sourcePosition = $endPosition - 1;
-                }
-                elseif(isset($tempDelimeter['match'])) {
-                    $tempPosition = strpos($source, $tempDelimeter['end'], $sourcePosition + $findLength);
-                    $len = strlen($tempDelimeter['end']);
-                    if($tempPosition !== false && preg_match($tempDelimeter['match'], substr($source, $sourcePosition, $tempPosition - $sourcePosition + $len))) {
-                        $endPosition = isset($tempDelimeter['noslash']) ? $this->__endCharNoSlash($source, $sourcePosition, $tempDelimeter['end'], $sourceLength) : $tempPosition + $len;
-                        array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition));
-                        $sourcePosition = $endPosition - 1;
-                    }
-                }
-                else {
-                    if(isset($tempDelimeter['noslash']))
-                        $endPosition = $this->__endCharNoSlash($source, $sourcePosition, $tempDelimeter['end'], $sourceLength);
-                    else {
-                        $tempPosition = strpos($source, $tempDelimeter['end'], $sourcePosition + $findLength);
-                        if($tempPosition !== false)
-                            $endPosition = $tempPosition + strlen($tempDelimeter['end']);
-                        else
-                            $endPosition = $sourceLength;
-                    }
-                    array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition));
-                    $sourcePosition = $endPosition - 1;
-                }
-            }
-            else
-                $sourcePosition = $sourceLength - 1;
-            ++$sourcePosition;
-        }
-        $len = count($map);
-        if($len === 0)
-            array_push($tempMap, array('name'=>'code', 'start'=>0, 'end'=>$sourceLength));
-        else {
-            for($tempIndex = 0; $tempIndex < $len; $tempIndex++) {
-                if($tempIndex === 0 && $map[$tempIndex]['start'] > 0)
-                    array_push($tempMap, array('name'=>'code', 'start'=>0, 'end'=>$map[$tempIndex]['start']));
-                elseif($tempIndex > 0 && $map[$tempIndex]['start'] > $map[$tempIndex-1]['end'])
-                    array_push($tempMap, array('name'=>'code', 'start'=>$map[$tempIndex-1]['end'], 'end'=>$map[$tempIndex]['start']));
-                array_push($tempMap, array('name'=>$map[$tempIndex]['name'], 'start'=>$map[$tempIndex]['start'], 'end'=>$map[$tempIndex]['end']));
-                if($tempIndex + 1 === $len && $map[$tempIndex]['end'] < $sourceLength)
-                    array_push($tempMap, array('name'=>'code', 'start'=>$map[$tempIndex]['end'], 'end'=>$sourceLength));
-            }
-        }
-        return $tempMap;
-    }
-    
-    function __endCharNoSlash(&$source, $position, &$find, &$len) {
-        $temp = strlen($find);
-        do {
-            $position = strpos($source, $find, $position + 1);
-        }while($position !== false && !$this->__charNoSlash($source, $position));
-        if($position === false) $position = $len - $temp;
-        return $position + $temp;
-    }
-    
-    function __charNoSlash(&$source, &$position) {
-        $next = 1; $len = $position - $next;
-        while($len > 0 && $source{$len} === '\\') $len = $position - (++$next);
-        return (($next - 1) % 2 === 0);
-    }
+		
+		/**
+ 		* public method
+ 				*     getMap(&$source:string, &$delimeters:array):array
+ 		* Maps the source code using $delimeters rules and returns map as an array
+ 				* NOTE: read comments to know more about map and delimeter
+ 				*
+ 				* @param    string        generic source code
+ 				* @param    array        array with nested array with code rules
+ 		*/
+		function getMap(&$source, &$delimeters) {
+				
+				# "unsigned" integer variables
+				$sourcePosition = 0;
+				$delimetersPosition = 0;
+				$findLength = 0;
+				$len = 0;
+				$tempIndex = 0;
+				$sourceLength = strlen($source);
+				$delimetersLength = count($delimeters);
+				
+				# integer variables
+				$tempPosition = -1;
+				$endPosition = -1;
+				
+				# array variables
+				$map = array();
+				$tempMap = array();
+				$tempDelimeter = array();
+				
+				while($sourcePosition < $sourceLength) {
+						$endPosition = -1;
+						for($delimetersPosition = 0; $delimetersPosition < $delimetersLength; $delimetersPosition++) {
+								$tempPosition = strpos($source, $delimeters[$delimetersPosition]['start'], $sourcePosition);
+								if($tempPosition !== false && ($tempPosition < $endPosition || $endPosition === -1)) {
+										$endPosition = $tempPosition;
+										$tempIndex = $delimetersPosition;
+								}
+						}
+						if($endPosition !== -1) {
+								$sourcePosition = $endPosition;
+								$tempDelimeter = &$delimeters[$tempIndex];
+								$findLength = strlen($tempDelimeter['start']);
+								if(is_array($tempDelimeter['end'])) {
+										$delimetersPosition = 0;
+										$endPosition = -1;
+										for($len = count($tempDelimeter['end']); $delimetersPosition < $len; $delimetersPosition++) {
+												$tempPosition = strpos($source, $tempDelimeter['end'][$delimetersPosition], $sourcePosition + $findLength);
+												if($tempPosition !== false && ($tempPosition < $endPosition || $endPosition === -1)) {
+														$endPosition = $tempPosition;
+														$tempIndex = $delimetersPosition;
+												}    
+										}
+										if($endPosition !== -1)
+												$endPosition = $endPosition + strlen($tempDelimeter['end'][$tempIndex]);
+										else
+												$endPosition = $sourceLength;
+										array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition));
+										$sourcePosition = $endPosition - 1;
+								}
+								elseif(isset($tempDelimeter['match'])) {
+										$tempPosition = strpos($source, $tempDelimeter['end'], $sourcePosition + $findLength);
+										$len = strlen($tempDelimeter['end']);
+										if($tempPosition !== false && preg_match($tempDelimeter['match'], substr($source, $sourcePosition, $tempPosition - $sourcePosition + $len))) {
+												$endPosition = isset($tempDelimeter['noslash']) ? $this->__endCharNoSlash($source, $sourcePosition, $tempDelimeter['end'], $sourceLength) : $tempPosition + $len;
+												array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition));
+												$sourcePosition = $endPosition - 1;
+										}
+								}
+								else {
+										if(isset($tempDelimeter['noslash']))
+												$endPosition = $this->__endCharNoSlash($source, $sourcePosition, $tempDelimeter['end'], $sourceLength);
+										else {
+												$tempPosition = strpos($source, $tempDelimeter['end'], $sourcePosition + $findLength);
+												if($tempPosition !== false)
+														$endPosition = $tempPosition + strlen($tempDelimeter['end']);
+												else
+														$endPosition = $sourceLength;
+										}
+										array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition));
+										$sourcePosition = $endPosition - 1;
+								}
+						}
+						else
+								$sourcePosition = $sourceLength - 1;
+						++$sourcePosition;
+				}
+				$len = count($map);
+				if($len === 0)
+						array_push($tempMap, array('name'=>'code', 'start'=>0, 'end'=>$sourceLength));
+				else {
+						for($tempIndex = 0; $tempIndex < $len; $tempIndex++) {
+								if($tempIndex === 0 && $map[$tempIndex]['start'] > 0)
+										array_push($tempMap, array('name'=>'code', 'start'=>0, 'end'=>$map[$tempIndex]['start']));
+								elseif($tempIndex > 0 && $map[$tempIndex]['start'] > $map[$tempIndex-1]['end'])
+										array_push($tempMap, array('name'=>'code', 'start'=>$map[$tempIndex-1]['end'], 'end'=>$map[$tempIndex]['start']));
+								array_push($tempMap, array('name'=>$map[$tempIndex]['name'], 'start'=>$map[$tempIndex]['start'], 'end'=>$map[$tempIndex]['end']));
+								if($tempIndex + 1 === $len && $map[$tempIndex]['end'] < $sourceLength)
+										array_push($tempMap, array('name'=>'code', 'start'=>$map[$tempIndex]['end'], 'end'=>$sourceLength));
+						}
+				}
+				return $tempMap;
+		}
+		
+		function __endCharNoSlash(&$source, $position, &$find, &$len) {
+				$temp = strlen($find);
+				do {
+						$position = strpos($source, $find, $position + 1);
+				}while($position !== false && !$this->__charNoSlash($source, $position));
+				if($position === false) $position = $len - $temp;
+				return $position + $temp;
+		}
+		
+		function __charNoSlash(&$source, &$position) {
+				$next = 1; $len = $position - $next;
+				while($len > 0 && $source{$len} === '\\') $len = $position - (++$next);
+				return (($next - 1) % 2 === 0);
+		}
 }
 
 /**
@@ -502,25 +502,25 @@
 
 function perform_js_compress($text_or_file, $aggressive = false)
 {
-  static $compressor = false;
-  
-  if ( !is_object($compressor) )
-    $compressor = new JavaScriptCompressor();
-  
-  if ( strpos($text_or_file, "\n") )
-  {
-    $text =& $text_or_file;
-  }
-  else if ( file_exists($text_or_file) )
-  {
-    $text = file_get_contents($text_or_file);
-  }
-  else
-  {
-    $text =& $text_or_file;
-  }
-  
-  return ( $aggressive ) ? $compressor->getPacked($text) : $compressor->getClean($text);
+	static $compressor = false;
+	
+	if ( !is_object($compressor) )
+		$compressor = new JavaScriptCompressor();
+	
+	if ( strpos($text_or_file, "\n") )
+	{
+		$text =& $text_or_file;
+	}
+	else if ( file_exists($text_or_file) )
+	{
+		$text = file_get_contents($text_or_file);
+	}
+	else
+	{
+		$text =& $text_or_file;
+	}
+	
+	return ( $aggressive ) ? $compressor->getPacked($text) : $compressor->getClean($text);
 }
 
 ?>
\ No newline at end of file
--- a/includes/json.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/json.php	Sun Mar 28 23:10:46 2010 -0400
@@ -95,711 +95,711 @@
  * Converts to and from JSON format.
  *
  * @example <code>
-   // create a new instance of Services_JSON
-   $json = new Services_JSON();
-  
-   // convert a complexe value to JSON notation, and send it to the browser
-   $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
-   $output = $json->encode($value);
-  
-   print($output);
-   // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
-  
-   // accept incoming POST data, assumed to be in JSON notation
-   $input = file_get_contents('php://input', 1000000);
-   $value = $json->decode($input);
-   </code>
+ 	// create a new instance of Services_JSON
+ 	$json = new Services_JSON();
+	
+ 	// convert a complexe value to JSON notation, and send it to the browser
+ 	$value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
+ 	$output = $json->encode($value);
+	
+ 	print($output);
+ 	// prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
+	
+ 	// accept incoming POST data, assumed to be in JSON notation
+ 	$input = file_get_contents('php://input', 1000000);
+ 	$value = $json->decode($input);
+ 	</code>
  *
  */
 class Services_JSON
 {
-   /**
-    * constructs a new JSON instance
-    *
-    * @param    int     $use    object behavior flags; combine with boolean-OR
-    *
-    *                           possible values:
-    *                           - SERVICES_JSON_LOOSE_TYPE:  loose typing.
-    *                                   "{...}" syntax creates associative arrays
-    *                                   instead of objects in decode().
-    *                           - SERVICES_JSON_SUPPRESS_ERRORS:  error suppression.
-    *                                   Values which can't be encoded (e.g. resources)
-    *                                   appear as NULL instead of throwing errors.
-    *                                   By default, a deeply-nested resource will
-    *                                   bubble up with an error, so all return values
-    *                                   from encode() should be checked with isError()
-    */
-    function Services_JSON($use = 0)
-    {
-        $this->use = $use;
-    }
+ 	/**
+		* constructs a new JSON instance
+		*
+		* @param    int     $use    object behavior flags; combine with boolean-OR
+		*
+		*                           possible values:
+		*                           - SERVICES_JSON_LOOSE_TYPE:  loose typing.
+		*                                   "{...}" syntax creates associative arrays
+		*                                   instead of objects in decode().
+		*                           - SERVICES_JSON_SUPPRESS_ERRORS:  error suppression.
+		*                                   Values which can't be encoded (e.g. resources)
+		*                                   appear as NULL instead of throwing errors.
+		*                                   By default, a deeply-nested resource will
+		*                                   bubble up with an error, so all return values
+		*                                   from encode() should be checked with isError()
+		*/
+		function Services_JSON($use = 0)
+		{
+				$this->use = $use;
+		}
 
-   /**
-    * convert a string from one UTF-16 char to one UTF-8 char
-    *
-    * Normally should be handled by mb_convert_encoding, but
-    * provides a slower PHP-only method for installations
-    * that lack the multibye string extension.
-    *
-    * @param    string  $utf16  UTF-16 character
-    * @return   string  UTF-8 character
-    * @access   private
-    */
-    function utf162utf8($utf16)
-    {
-        // oh please oh please oh please oh please oh please
-        if(function_exists('mb_convert_encoding')) {
-            return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
-        }
+ 	/**
+		* convert a string from one UTF-16 char to one UTF-8 char
+		*
+		* Normally should be handled by mb_convert_encoding, but
+		* provides a slower PHP-only method for installations
+		* that lack the multibye string extension.
+		*
+		* @param    string  $utf16  UTF-16 character
+		* @return   string  UTF-8 character
+		* @access   private
+		*/
+		function utf162utf8($utf16)
+		{
+				// oh please oh please oh please oh please oh please
+				if(function_exists('mb_convert_encoding')) {
+						return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
+				}
 
-        $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
+				$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
 
-        switch(true) {
-            case ((0x7F & $bytes) == $bytes):
-                // this case should never be reached, because we are in ASCII range
-                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                return chr(0x7F & $bytes);
+				switch(true) {
+						case ((0x7F & $bytes) == $bytes):
+								// this case should never be reached, because we are in ASCII range
+								// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+								return chr(0x7F & $bytes);
 
-            case (0x07FF & $bytes) == $bytes:
-                // return a 2-byte UTF-8 character
-                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                return chr(0xC0 | (($bytes >> 6) & 0x1F))
-                     . chr(0x80 | ($bytes & 0x3F));
+						case (0x07FF & $bytes) == $bytes:
+								// return a 2-byte UTF-8 character
+								// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+								return chr(0xC0 | (($bytes >> 6) & 0x1F))
+ 										. chr(0x80 | ($bytes & 0x3F));
 
-            case (0xFFFF & $bytes) == $bytes:
-                // return a 3-byte UTF-8 character
-                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                return chr(0xE0 | (($bytes >> 12) & 0x0F))
-                     . chr(0x80 | (($bytes >> 6) & 0x3F))
-                     . chr(0x80 | ($bytes & 0x3F));
-        }
+						case (0xFFFF & $bytes) == $bytes:
+								// return a 3-byte UTF-8 character
+								// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+								return chr(0xE0 | (($bytes >> 12) & 0x0F))
+ 										. chr(0x80 | (($bytes >> 6) & 0x3F))
+ 										. chr(0x80 | ($bytes & 0x3F));
+				}
 
-        // ignoring UTF-32 for now, sorry
-        return '';
-    }
+				// ignoring UTF-32 for now, sorry
+				return '';
+		}
 
-   /**
-    * convert a string from one UTF-8 char to one UTF-16 char
-    *
-    * Normally should be handled by mb_convert_encoding, but
-    * provides a slower PHP-only method for installations
-    * that lack the multibye string extension.
-    *
-    * @param    string  $utf8   UTF-8 character
-    * @return   string  UTF-16 character
-    * @access   private
-    */
-    function utf82utf16($utf8)
-    {
-        // oh please oh please oh please oh please oh please
-        if(function_exists('mb_convert_encoding')) {
-            return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
-        }
+ 	/**
+		* convert a string from one UTF-8 char to one UTF-16 char
+		*
+		* Normally should be handled by mb_convert_encoding, but
+		* provides a slower PHP-only method for installations
+		* that lack the multibye string extension.
+		*
+		* @param    string  $utf8   UTF-8 character
+		* @return   string  UTF-16 character
+		* @access   private
+		*/
+		function utf82utf16($utf8)
+		{
+				// oh please oh please oh please oh please oh please
+				if(function_exists('mb_convert_encoding')) {
+						return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
+				}
 
-        switch(strlen($utf8)) {
-            case 1:
-                // this case should never be reached, because we are in ASCII range
-                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                return $utf8;
+				switch(strlen($utf8)) {
+						case 1:
+								// this case should never be reached, because we are in ASCII range
+								// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+								return $utf8;
 
-            case 2:
-                // return a UTF-16 character from a 2-byte UTF-8 char
-                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                return chr(0x07 & (ord($utf8{0}) >> 2))
-                     . chr((0xC0 & (ord($utf8{0}) << 6))
-                         | (0x3F & ord($utf8{1})));
+						case 2:
+								// return a UTF-16 character from a 2-byte UTF-8 char
+								// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+								return chr(0x07 & (ord($utf8{0}) >> 2))
+ 										. chr((0xC0 & (ord($utf8{0}) << 6))
+ 												| (0x3F & ord($utf8{1})));
 
-            case 3:
-                // return a UTF-16 character from a 3-byte UTF-8 char
-                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                return chr((0xF0 & (ord($utf8{0}) << 4))
-                         | (0x0F & (ord($utf8{1}) >> 2)))
-                     . chr((0xC0 & (ord($utf8{1}) << 6))
-                         | (0x7F & ord($utf8{2})));
-        }
+						case 3:
+								// return a UTF-16 character from a 3-byte UTF-8 char
+								// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+								return chr((0xF0 & (ord($utf8{0}) << 4))
+ 												| (0x0F & (ord($utf8{1}) >> 2)))
+ 										. chr((0xC0 & (ord($utf8{1}) << 6))
+ 												| (0x7F & ord($utf8{2})));
+				}
 
-        // ignoring UTF-32 for now, sorry
-        return '';
-    }
+				// ignoring UTF-32 for now, sorry
+				return '';
+		}
 
-   /**
-    * encodes an arbitrary variable into JSON format
-    *
-    * @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
-    *                           see argument 1 to Services_JSON() above for array-parsing behavior.
-    *                           if var is a strng, note that encode() always expects it
-    *                           to be in ASCII or UTF-8 format!
-    *
-    * @return   mixed   JSON string representation of input var or an error if a problem occurs
-    * @access   public
-    */
-    function encode($var)
-    {
-        switch (gettype($var)) {
-            case 'boolean':
-                return $var ? 'true' : 'false';
+ 	/**
+		* encodes an arbitrary variable into JSON format
+		*
+		* @param    mixed   $var    any number, boolean, string, array, or object to be encoded.
+		*                           see argument 1 to Services_JSON() above for array-parsing behavior.
+		*                           if var is a strng, note that encode() always expects it
+		*                           to be in ASCII or UTF-8 format!
+		*
+		* @return   mixed   JSON string representation of input var or an error if a problem occurs
+		* @access   public
+		*/
+		function encode($var)
+		{
+				switch (gettype($var)) {
+						case 'boolean':
+								return $var ? 'true' : 'false';
 
-            case 'NULL':
-                return 'null';
+						case 'NULL':
+								return 'null';
 
-            case 'integer':
-                return (int) $var;
+						case 'integer':
+								return (int) $var;
 
-            case 'double':
-            case 'float':
-                return (float) $var;
+						case 'double':
+						case 'float':
+								return (float) $var;
 
-            case 'string':
-                // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
-                $ascii = '';
-                $strlen_var = strlen($var);
+						case 'string':
+								// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+								$ascii = '';
+								$strlen_var = strlen($var);
 
-               /*
-                * Iterate over every character in the string,
-                * escaping with a slash or encoding to UTF-8 where necessary
-                */
-                for ($c = 0; $c < $strlen_var; ++$c) {
+ 							/*
+								* Iterate over every character in the string,
+								* escaping with a slash or encoding to UTF-8 where necessary
+								*/
+								for ($c = 0; $c < $strlen_var; ++$c) {
 
-                    $ord_var_c = ord($var{$c});
+										$ord_var_c = ord($var{$c});
 
-                    switch (true) {
-                        case $ord_var_c == 0x08:
-                            $ascii .= '\b';
-                            break;
-                        case $ord_var_c == 0x09:
-                            $ascii .= '\t';
-                            break;
-                        case $ord_var_c == 0x0A:
-                            $ascii .= '\n';
-                            break;
-                        case $ord_var_c == 0x0C:
-                            $ascii .= '\f';
-                            break;
-                        case $ord_var_c == 0x0D:
-                            $ascii .= '\r';
-                            break;
+										switch (true) {
+												case $ord_var_c == 0x08:
+														$ascii .= '\b';
+														break;
+												case $ord_var_c == 0x09:
+														$ascii .= '\t';
+														break;
+												case $ord_var_c == 0x0A:
+														$ascii .= '\n';
+														break;
+												case $ord_var_c == 0x0C:
+														$ascii .= '\f';
+														break;
+												case $ord_var_c == 0x0D:
+														$ascii .= '\r';
+														break;
 
-                        case $ord_var_c == 0x22:
-                        case $ord_var_c == 0x2F:
-                        case $ord_var_c == 0x5C:
-                            // double quote, slash, slosh
-                            $ascii .= '\\'.$var{$c};
-                            break;
+												case $ord_var_c == 0x22:
+												case $ord_var_c == 0x2F:
+												case $ord_var_c == 0x5C:
+														// double quote, slash, slosh
+														$ascii .= '\\'.$var{$c};
+														break;
 
-                        case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
-                            // characters U-00000000 - U-0000007F (same as ASCII)
-                            $ascii .= $var{$c};
-                            break;
+												case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+														// characters U-00000000 - U-0000007F (same as ASCII)
+														$ascii .= $var{$c};
+														break;
 
-                        case (($ord_var_c & 0xE0) == 0xC0):
-                            // characters U-00000080 - U-000007FF, mask 110XXXXX
-                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                            $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
-                            $c += 1;
-                            $utf16 = $this->utf82utf16($char);
-                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
-                            break;
+												case (($ord_var_c & 0xE0) == 0xC0):
+														// characters U-00000080 - U-000007FF, mask 110XXXXX
+														// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+														$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
+														$c += 1;
+														$utf16 = $this->utf82utf16($char);
+														$ascii .= sprintf('\u%04s', bin2hex($utf16));
+														break;
 
-                        case (($ord_var_c & 0xF0) == 0xE0):
-                            // characters U-00000800 - U-0000FFFF, mask 1110XXXX
-                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                            $char = pack('C*', $ord_var_c,
-                                         ord($var{$c + 1}),
-                                         ord($var{$c + 2}));
-                            $c += 2;
-                            $utf16 = $this->utf82utf16($char);
-                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
-                            break;
+												case (($ord_var_c & 0xF0) == 0xE0):
+														// characters U-00000800 - U-0000FFFF, mask 1110XXXX
+														// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+														$char = pack('C*', $ord_var_c,
+ 																				ord($var{$c + 1}),
+ 																				ord($var{$c + 2}));
+														$c += 2;
+														$utf16 = $this->utf82utf16($char);
+														$ascii .= sprintf('\u%04s', bin2hex($utf16));
+														break;
 
-                        case (($ord_var_c & 0xF8) == 0xF0):
-                            // characters U-00010000 - U-001FFFFF, mask 11110XXX
-                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                            $char = pack('C*', $ord_var_c,
-                                         ord($var{$c + 1}),
-                                         ord($var{$c + 2}),
-                                         ord($var{$c + 3}));
-                            $c += 3;
-                            $utf16 = $this->utf82utf16($char);
-                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
-                            break;
+												case (($ord_var_c & 0xF8) == 0xF0):
+														// characters U-00010000 - U-001FFFFF, mask 11110XXX
+														// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+														$char = pack('C*', $ord_var_c,
+ 																				ord($var{$c + 1}),
+ 																				ord($var{$c + 2}),
+ 																				ord($var{$c + 3}));
+														$c += 3;
+														$utf16 = $this->utf82utf16($char);
+														$ascii .= sprintf('\u%04s', bin2hex($utf16));
+														break;
 
-                        case (($ord_var_c & 0xFC) == 0xF8):
-                            // characters U-00200000 - U-03FFFFFF, mask 111110XX
-                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                            $char = pack('C*', $ord_var_c,
-                                         ord($var{$c + 1}),
-                                         ord($var{$c + 2}),
-                                         ord($var{$c + 3}),
-                                         ord($var{$c + 4}));
-                            $c += 4;
-                            $utf16 = $this->utf82utf16($char);
-                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
-                            break;
+												case (($ord_var_c & 0xFC) == 0xF8):
+														// characters U-00200000 - U-03FFFFFF, mask 111110XX
+														// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+														$char = pack('C*', $ord_var_c,
+ 																				ord($var{$c + 1}),
+ 																				ord($var{$c + 2}),
+ 																				ord($var{$c + 3}),
+ 																				ord($var{$c + 4}));
+														$c += 4;
+														$utf16 = $this->utf82utf16($char);
+														$ascii .= sprintf('\u%04s', bin2hex($utf16));
+														break;
 
-                        case (($ord_var_c & 0xFE) == 0xFC):
-                            // characters U-04000000 - U-7FFFFFFF, mask 1111110X
-                            // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                            $char = pack('C*', $ord_var_c,
-                                         ord($var{$c + 1}),
-                                         ord($var{$c + 2}),
-                                         ord($var{$c + 3}),
-                                         ord($var{$c + 4}),
-                                         ord($var{$c + 5}));
-                            $c += 5;
-                            $utf16 = $this->utf82utf16($char);
-                            $ascii .= sprintf('\u%04s', bin2hex($utf16));
-                            break;
-                    }
-                }
+												case (($ord_var_c & 0xFE) == 0xFC):
+														// characters U-04000000 - U-7FFFFFFF, mask 1111110X
+														// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+														$char = pack('C*', $ord_var_c,
+ 																				ord($var{$c + 1}),
+ 																				ord($var{$c + 2}),
+ 																				ord($var{$c + 3}),
+ 																				ord($var{$c + 4}),
+ 																				ord($var{$c + 5}));
+														$c += 5;
+														$utf16 = $this->utf82utf16($char);
+														$ascii .= sprintf('\u%04s', bin2hex($utf16));
+														break;
+										}
+								}
 
-                return '"'.$ascii.'"';
+								return '"'.$ascii.'"';
 
-            case 'array':
-               /*
-                * As per JSON spec if any array key is not an integer
-                * we must treat the the whole array as an object. We
-                * also try to catch a sparsely populated associative
-                * array with numeric keys here because some JS engines
-                * will create an array with empty indexes up to
-                * max_index which can cause memory issues and because
-                * the keys, which may be relevant, will be remapped
-                * otherwise.
-                *
-                * As per the ECMA and JSON specification an object may
-                * have any string as a property. Unfortunately due to
-                * a hole in the ECMA specification if the key is a
-                * ECMA reserved word or starts with a digit the
-                * parameter is only accessible using ECMAScript's
-                * bracket notation.
-                */
+						case 'array':
+ 							/*
+								* As per JSON spec if any array key is not an integer
+								* we must treat the the whole array as an object. We
+								* also try to catch a sparsely populated associative
+								* array with numeric keys here because some JS engines
+								* will create an array with empty indexes up to
+								* max_index which can cause memory issues and because
+								* the keys, which may be relevant, will be remapped
+								* otherwise.
+								*
+								* As per the ECMA and JSON specification an object may
+								* have any string as a property. Unfortunately due to
+								* a hole in the ECMA specification if the key is a
+								* ECMA reserved word or starts with a digit the
+								* parameter is only accessible using ECMAScript's
+								* bracket notation.
+								*/
 
-                // treat as a JSON object
-                if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
-                    $properties = array_map(array($this, 'name_value'),
-                                            array_keys($var),
-                                            array_values($var));
+								// treat as a JSON object
+								if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+										$properties = array_map(array($this, 'name_value'),
+																						array_keys($var),
+																						array_values($var));
 
-                    foreach($properties as $property) {
-                        if(Services_JSON::isError($property)) {
-                            return $property;
-                        }
-                    }
+										foreach($properties as $property) {
+												if(Services_JSON::isError($property)) {
+														return $property;
+												}
+										}
 
-                    return '{' . join(',', $properties) . '}';
-                }
+										return '{' . join(',', $properties) . '}';
+								}
 
-                // treat it like a regular array
-                $elements = array_map(array($this, 'encode'), $var);
+								// treat it like a regular array
+								$elements = array_map(array($this, 'encode'), $var);
 
-                foreach($elements as $element) {
-                    if(Services_JSON::isError($element)) {
-                        return $element;
-                    }
-                }
+								foreach($elements as $element) {
+										if(Services_JSON::isError($element)) {
+												return $element;
+										}
+								}
 
-                return '[' . join(',', $elements) . ']';
+								return '[' . join(',', $elements) . ']';
 
-            case 'object':
-                $vars = get_object_vars($var);
+						case 'object':
+								$vars = get_object_vars($var);
 
-                $properties = array_map(array($this, 'name_value'),
-                                        array_keys($vars),
-                                        array_values($vars));
+								$properties = array_map(array($this, 'name_value'),
+																				array_keys($vars),
+																				array_values($vars));
 
-                foreach($properties as $property) {
-                    if(Services_JSON::isError($property)) {
-                        return $property;
-                    }
-                }
+								foreach($properties as $property) {
+										if(Services_JSON::isError($property)) {
+												return $property;
+										}
+								}
 
-                return '{' . join(',', $properties) . '}';
+								return '{' . join(',', $properties) . '}';
 
-            default:
-                return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
-                    ? 'null'
-                    : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
-        }
-    }
+						default:
+								return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
+										? 'null'
+										: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
+				}
+		}
 
-   /**
-    * array-walking function for use in generating JSON-formatted name-value pairs
-    *
-    * @param    string  $name   name of key to use
-    * @param    mixed   $value  reference to an array element to be encoded
-    *
-    * @return   string  JSON-formatted name-value pair, like '"name":value'
-    * @access   private
-    */
-    function name_value($name, $value)
-    {
-        $encoded_value = $this->encode($value);
+ 	/**
+		* array-walking function for use in generating JSON-formatted name-value pairs
+		*
+		* @param    string  $name   name of key to use
+		* @param    mixed   $value  reference to an array element to be encoded
+		*
+		* @return   string  JSON-formatted name-value pair, like '"name":value'
+		* @access   private
+		*/
+		function name_value($name, $value)
+		{
+				$encoded_value = $this->encode($value);
 
-        if(Services_JSON::isError($encoded_value)) {
-            return $encoded_value;
-        }
+				if(Services_JSON::isError($encoded_value)) {
+						return $encoded_value;
+				}
 
-        return $this->encode(strval($name)) . ':' . $encoded_value;
-    }
+				return $this->encode(strval($name)) . ':' . $encoded_value;
+		}
 
-   /**
-    * reduce a string by removing leading and trailing comments and whitespace
-    *
-    * @param    $str    string      string value to strip of comments and whitespace
-    *
-    * @return   string  string value stripped of comments and whitespace
-    * @access   private
-    */
-    function reduce_string($str)
-    {
-        $str = preg_replace(array(
+ 	/**
+		* reduce a string by removing leading and trailing comments and whitespace
+		*
+		* @param    $str    string      string value to strip of comments and whitespace
+		*
+		* @return   string  string value stripped of comments and whitespace
+		* @access   private
+		*/
+		function reduce_string($str)
+		{
+				$str = preg_replace(array(
 
-                // eliminate single line comments in '// ...' form
-                '#^\s*//(.+)$#m',
+								// eliminate single line comments in '// ...' form
+								'#^\s*//(.+)$#m',
 
-                // eliminate multi-line comments in '/* ... */' form, at start of string
-                '#^\s*/\*(.+)\*/#Us',
+								// eliminate multi-line comments in '/* ... */' form, at start of string
+								'#^\s*/\*(.+)\*/#Us',
 
-                // eliminate multi-line comments in '/* ... */' form, at end of string
-                '#/\*(.+)\*/\s*$#Us'
+								// eliminate multi-line comments in '/* ... */' form, at end of string
+								'#/\*(.+)\*/\s*$#Us'
 
-            ), '', $str);
+						), '', $str);
 
-        // eliminate extraneous space
-        return trim($str);
-    }
+				// eliminate extraneous space
+				return trim($str);
+		}
 
-   /**
-    * decodes a JSON string into appropriate variable
-    *
-    * @param    string  $str    JSON-formatted string
-    *
-    * @return   mixed   number, boolean, string, array, or object
-    *                   corresponding to given JSON input string.
-    *                   See argument 1 to Services_JSON() above for object-output behavior.
-    *                   Note that decode() always returns strings
-    *                   in ASCII or UTF-8 format!
-    * @access   public
-    */
-    function decode($str)
-    {
-        $str = $this->reduce_string($str);
+ 	/**
+		* decodes a JSON string into appropriate variable
+		*
+		* @param    string  $str    JSON-formatted string
+		*
+		* @return   mixed   number, boolean, string, array, or object
+		*                   corresponding to given JSON input string.
+		*                   See argument 1 to Services_JSON() above for object-output behavior.
+		*                   Note that decode() always returns strings
+		*                   in ASCII or UTF-8 format!
+		* @access   public
+		*/
+		function decode($str)
+		{
+				$str = $this->reduce_string($str);
 
-        switch (strtolower($str)) {
-            case 'true':
-                return true;
+				switch (strtolower($str)) {
+						case 'true':
+								return true;
 
-            case 'false':
-                return false;
+						case 'false':
+								return false;
 
-            case 'null':
-                return null;
+						case 'null':
+								return null;
 
-            default:
-                $m = array();
+						default:
+								$m = array();
 
-                if (is_numeric($str)) {
-                    // Lookie-loo, it's a number
+								if (is_numeric($str)) {
+										// Lookie-loo, it's a number
 
-                    // This would work on its own, but I'm trying to be
-                    // good about returning integers where appropriate:
-                    // return (float)$str;
+										// This would work on its own, but I'm trying to be
+										// good about returning integers where appropriate:
+										// return (float)$str;
 
-                    // Return float or int, as appropriate
-                    return ((float)$str == (integer)$str)
-                        ? (integer)$str
-                        : (float)$str;
+										// Return float or int, as appropriate
+										return ((float)$str == (integer)$str)
+												? (integer)$str
+												: (float)$str;
 
-                } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
-                    // STRINGS RETURNED IN UTF-8 FORMAT
-                    $delim = substr($str, 0, 1);
-                    $chrs = substr($str, 1, -1);
-                    $utf8 = '';
-                    $strlen_chrs = strlen($chrs);
+								} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
+										// STRINGS RETURNED IN UTF-8 FORMAT
+										$delim = substr($str, 0, 1);
+										$chrs = substr($str, 1, -1);
+										$utf8 = '';
+										$strlen_chrs = strlen($chrs);
 
-                    for ($c = 0; $c < $strlen_chrs; ++$c) {
+										for ($c = 0; $c < $strlen_chrs; ++$c) {
 
-                        $substr_chrs_c_2 = substr($chrs, $c, 2);
-                        $ord_chrs_c = ord($chrs{$c});
+												$substr_chrs_c_2 = substr($chrs, $c, 2);
+												$ord_chrs_c = ord($chrs{$c});
 
-                        switch (true) {
-                            case $substr_chrs_c_2 == '\b':
-                                $utf8 .= chr(0x08);
-                                ++$c;
-                                break;
-                            case $substr_chrs_c_2 == '\t':
-                                $utf8 .= chr(0x09);
-                                ++$c;
-                                break;
-                            case $substr_chrs_c_2 == '\n':
-                                $utf8 .= chr(0x0A);
-                                ++$c;
-                                break;
-                            case $substr_chrs_c_2 == '\f':
-                                $utf8 .= chr(0x0C);
-                                ++$c;
-                                break;
-                            case $substr_chrs_c_2 == '\r':
-                                $utf8 .= chr(0x0D);
-                                ++$c;
-                                break;
+												switch (true) {
+														case $substr_chrs_c_2 == '\b':
+																$utf8 .= chr(0x08);
+																++$c;
+																break;
+														case $substr_chrs_c_2 == '\t':
+																$utf8 .= chr(0x09);
+																++$c;
+																break;
+														case $substr_chrs_c_2 == '\n':
+																$utf8 .= chr(0x0A);
+																++$c;
+																break;
+														case $substr_chrs_c_2 == '\f':
+																$utf8 .= chr(0x0C);
+																++$c;
+																break;
+														case $substr_chrs_c_2 == '\r':
+																$utf8 .= chr(0x0D);
+																++$c;
+																break;
 
-                            case $substr_chrs_c_2 == '\\"':
-                            case $substr_chrs_c_2 == '\\\'':
-                            case $substr_chrs_c_2 == '\\\\':
-                            case $substr_chrs_c_2 == '\\/':
-                                if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
-                                   ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
-                                    $utf8 .= $chrs{++$c};
-                                }
-                                break;
+														case $substr_chrs_c_2 == '\\"':
+														case $substr_chrs_c_2 == '\\\'':
+														case $substr_chrs_c_2 == '\\\\':
+														case $substr_chrs_c_2 == '\\/':
+																if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
+ 																	($delim == "'" && $substr_chrs_c_2 != '\\"')) {
+																		$utf8 .= $chrs{++$c};
+																}
+																break;
 
-                            case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
-                                // single, escaped unicode character
-                                $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
-                                       . chr(hexdec(substr($chrs, ($c + 4), 2)));
-                                $utf8 .= $this->utf162utf8($utf16);
-                                $c += 5;
-                                break;
+														case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
+																// single, escaped unicode character
+																$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
+ 																			. chr(hexdec(substr($chrs, ($c + 4), 2)));
+																$utf8 .= $this->utf162utf8($utf16);
+																$c += 5;
+																break;
 
-                            case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
-                                $utf8 .= $chrs{$c};
-                                break;
+														case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
+																$utf8 .= $chrs{$c};
+																break;
 
-                            case ($ord_chrs_c & 0xE0) == 0xC0:
-                                // characters U-00000080 - U-000007FF, mask 110XXXXX
-                                //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                                $utf8 .= substr($chrs, $c, 2);
-                                ++$c;
-                                break;
+														case ($ord_chrs_c & 0xE0) == 0xC0:
+																// characters U-00000080 - U-000007FF, mask 110XXXXX
+																//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+																$utf8 .= substr($chrs, $c, 2);
+																++$c;
+																break;
 
-                            case ($ord_chrs_c & 0xF0) == 0xE0:
-                                // characters U-00000800 - U-0000FFFF, mask 1110XXXX
-                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                                $utf8 .= substr($chrs, $c, 3);
-                                $c += 2;
-                                break;
+														case ($ord_chrs_c & 0xF0) == 0xE0:
+																// characters U-00000800 - U-0000FFFF, mask 1110XXXX
+																// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+																$utf8 .= substr($chrs, $c, 3);
+																$c += 2;
+																break;
 
-                            case ($ord_chrs_c & 0xF8) == 0xF0:
-                                // characters U-00010000 - U-001FFFFF, mask 11110XXX
-                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                                $utf8 .= substr($chrs, $c, 4);
-                                $c += 3;
-                                break;
+														case ($ord_chrs_c & 0xF8) == 0xF0:
+																// characters U-00010000 - U-001FFFFF, mask 11110XXX
+																// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+																$utf8 .= substr($chrs, $c, 4);
+																$c += 3;
+																break;
 
-                            case ($ord_chrs_c & 0xFC) == 0xF8:
-                                // characters U-00200000 - U-03FFFFFF, mask 111110XX
-                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                                $utf8 .= substr($chrs, $c, 5);
-                                $c += 4;
-                                break;
+														case ($ord_chrs_c & 0xFC) == 0xF8:
+																// characters U-00200000 - U-03FFFFFF, mask 111110XX
+																// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+																$utf8 .= substr($chrs, $c, 5);
+																$c += 4;
+																break;
 
-                            case ($ord_chrs_c & 0xFE) == 0xFC:
-                                // characters U-04000000 - U-7FFFFFFF, mask 1111110X
-                                // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
-                                $utf8 .= substr($chrs, $c, 6);
-                                $c += 5;
-                                break;
+														case ($ord_chrs_c & 0xFE) == 0xFC:
+																// characters U-04000000 - U-7FFFFFFF, mask 1111110X
+																// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+																$utf8 .= substr($chrs, $c, 6);
+																$c += 5;
+																break;
 
-                        }
+												}
 
-                    }
+										}
 
-                    return $utf8;
+										return $utf8;
 
-                } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
-                    // array, or object notation
+								} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
+										// array, or object notation
 
-                    if ($str{0} == '[') {
-                        $stk = array(SERVICES_JSON_IN_ARR);
-                        $arr = array();
-                    } else {
-                        if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
-                            $stk = array(SERVICES_JSON_IN_OBJ);
-                            $obj = array();
-                        } else {
-                            $stk = array(SERVICES_JSON_IN_OBJ);
-                            $obj = new stdClass();
-                        }
-                    }
+										if ($str{0} == '[') {
+												$stk = array(SERVICES_JSON_IN_ARR);
+												$arr = array();
+										} else {
+												if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+														$stk = array(SERVICES_JSON_IN_OBJ);
+														$obj = array();
+												} else {
+														$stk = array(SERVICES_JSON_IN_OBJ);
+														$obj = new stdClass();
+												}
+										}
 
-                    array_push($stk, array('what'  => SERVICES_JSON_SLICE,
-                                           'where' => 0,
-                                           'delim' => false));
+										array_push($stk, array('what'  => SERVICES_JSON_SLICE,
+ 																					'where' => 0,
+ 																					'delim' => false));
 
-                    $chrs = substr($str, 1, -1);
-                    $chrs = $this->reduce_string($chrs);
+										$chrs = substr($str, 1, -1);
+										$chrs = $this->reduce_string($chrs);
 
-                    if ($chrs == '') {
-                        if (reset($stk) == SERVICES_JSON_IN_ARR) {
-                            return $arr;
+										if ($chrs == '') {
+												if (reset($stk) == SERVICES_JSON_IN_ARR) {
+														return $arr;
 
-                        } else {
-                            return $obj;
+												} else {
+														return $obj;
 
-                        }
-                    }
+												}
+										}
 
-                    //print("\nparsing {$chrs}\n");
+										//print("\nparsing {$chrs}\n");
 
-                    $strlen_chrs = strlen($chrs);
+										$strlen_chrs = strlen($chrs);
 
-                    for ($c = 0; $c <= $strlen_chrs; ++$c) {
+										for ($c = 0; $c <= $strlen_chrs; ++$c) {
 
-                        $top = end($stk);
-                        $substr_chrs_c_2 = substr($chrs, $c, 2);
+												$top = end($stk);
+												$substr_chrs_c_2 = substr($chrs, $c, 2);
 
-                        if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
-                            // found a comma that is not inside a string, array, etc.,
-                            // OR we've reached the end of the character list
-                            $slice = substr($chrs, $top['where'], ($c - $top['where']));
-                            array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
-                            //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+												if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
+														// found a comma that is not inside a string, array, etc.,
+														// OR we've reached the end of the character list
+														$slice = substr($chrs, $top['where'], ($c - $top['where']));
+														array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
+														//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
-                            if (reset($stk) == SERVICES_JSON_IN_ARR) {
-                                // we are in an array, so just push an element onto the stack
-                                array_push($arr, $this->decode($slice));
+														if (reset($stk) == SERVICES_JSON_IN_ARR) {
+																// we are in an array, so just push an element onto the stack
+																array_push($arr, $this->decode($slice));
 
-                            } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
-                                // we are in an object, so figure
-                                // out the property name and set an
-                                // element in an associative array,
-                                // for now
-                                $parts = array();
-                                
-                                if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
-                                    // "name":value pair
-                                    $key = $this->decode($parts[1]);
-                                    $val = $this->decode($parts[2]);
+														} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
+																// we are in an object, so figure
+																// out the property name and set an
+																// element in an associative array,
+																// for now
+																$parts = array();
+																
+																if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+																		// "name":value pair
+																		$key = $this->decode($parts[1]);
+																		$val = $this->decode($parts[2]);
 
-                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
-                                        $obj[$key] = $val;
-                                    } else {
-                                        $obj->$key = $val;
-                                    }
-                                } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
-                                    // name:value pair, where name is unquoted
-                                    $key = $parts[1];
-                                    $val = $this->decode($parts[2]);
+																		if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+																				$obj[$key] = $val;
+																		} else {
+																				$obj->$key = $val;
+																		}
+																} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+																		// name:value pair, where name is unquoted
+																		$key = $parts[1];
+																		$val = $this->decode($parts[2]);
 
-                                    if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
-                                        $obj[$key] = $val;
-                                    } else {
-                                        $obj->$key = $val;
-                                    }
-                                }
+																		if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
+																				$obj[$key] = $val;
+																		} else {
+																				$obj->$key = $val;
+																		}
+																}
 
-                            }
+														}
 
-                        } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
-                            // found a quote, and we are not inside a string
-                            array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
-                            //print("Found start of string at {$c}\n");
+												} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
+														// found a quote, and we are not inside a string
+														array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
+														//print("Found start of string at {$c}\n");
 
-                        } elseif (($chrs{$c} == $top['delim']) &&
-                                 ($top['what'] == SERVICES_JSON_IN_STR) &&
-                                 ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
-                            // found a quote, we're in a string, and it's not escaped
-                            // we know that it's not escaped becase there is _not_ an
-                            // odd number of backslashes at the end of the string so far
-                            array_pop($stk);
-                            //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
+												} elseif (($chrs{$c} == $top['delim']) &&
+ 																($top['what'] == SERVICES_JSON_IN_STR) &&
+ 																((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
+														// found a quote, we're in a string, and it's not escaped
+														// we know that it's not escaped becase there is _not_ an
+														// odd number of backslashes at the end of the string so far
+														array_pop($stk);
+														//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
 
-                        } elseif (($chrs{$c} == '[') &&
-                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
-                            // found a left-bracket, and we are in an array, object, or slice
-                            array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
-                            //print("Found start of array at {$c}\n");
+												} elseif (($chrs{$c} == '[') &&
+ 																in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+														// found a left-bracket, and we are in an array, object, or slice
+														array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
+														//print("Found start of array at {$c}\n");
 
-                        } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
-                            // found a right-bracket, and we're in an array
-                            array_pop($stk);
-                            //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+												} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
+														// found a right-bracket, and we're in an array
+														array_pop($stk);
+														//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
-                        } elseif (($chrs{$c} == '{') &&
-                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
-                            // found a left-brace, and we are in an array, object, or slice
-                            array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
-                            //print("Found start of object at {$c}\n");
+												} elseif (($chrs{$c} == '{') &&
+ 																in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+														// found a left-brace, and we are in an array, object, or slice
+														array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
+														//print("Found start of object at {$c}\n");
 
-                        } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
-                            // found a right-brace, and we're in an object
-                            array_pop($stk);
-                            //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+												} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
+														// found a right-brace, and we're in an object
+														array_pop($stk);
+														//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
-                        } elseif (($substr_chrs_c_2 == '/*') &&
-                                 in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
-                            // found a comment start, and we are in an array, object, or slice
-                            array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
-                            $c++;
-                            //print("Found start of comment at {$c}\n");
+												} elseif (($substr_chrs_c_2 == '/*') &&
+ 																in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
+														// found a comment start, and we are in an array, object, or slice
+														array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
+														$c++;
+														//print("Found start of comment at {$c}\n");
 
-                        } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
-                            // found a comment end, and we're in one now
-                            array_pop($stk);
-                            $c++;
+												} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
+														// found a comment end, and we're in one now
+														array_pop($stk);
+														$c++;
 
-                            for ($i = $top['where']; $i <= $c; ++$i)
-                                $chrs = substr_replace($chrs, ' ', $i, 1);
+														for ($i = $top['where']; $i <= $c; ++$i)
+																$chrs = substr_replace($chrs, ' ', $i, 1);
 
-                            //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+														//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
-                        }
+												}
 
-                    }
+										}
 
-                    if (reset($stk) == SERVICES_JSON_IN_ARR) {
-                        return $arr;
+										if (reset($stk) == SERVICES_JSON_IN_ARR) {
+												return $arr;
 
-                    } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
-                        return $obj;
+										} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
+												return $obj;
 
-                    }
+										}
 
-                }
-        }
-    }
+								}
+				}
+		}
 
-    /**
-     * @todo Ultimately, this should just call PEAR::isError()
-     */
-    function isError($data, $code = null)
-    {
-        if (class_exists('pear')) {
-            return PEAR::isError($data, $code);
-        } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
-                                 is_subclass_of($data, 'services_json_error'))) {
-            return true;
-        }
+		/**
+ 		* @todo Ultimately, this should just call PEAR::isError()
+ 		*/
+		function isError($data, $code = null)
+		{
+				if (class_exists('pear')) {
+						return PEAR::isError($data, $code);
+				} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
+ 																is_subclass_of($data, 'services_json_error'))) {
+						return true;
+				}
 
-        return false;
-    }
+				return false;
+		}
 }
 
 if (class_exists('PEAR_Error')) {
 
-    class Services_JSON_Error extends PEAR_Error
-    {
-        function Services_JSON_Error($message = 'unknown error', $code = null,
-                                     $mode = null, $options = null, $userinfo = null)
-        {
-            parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
-        }
-    }
+		class Services_JSON_Error extends PEAR_Error
+		{
+				function Services_JSON_Error($message = 'unknown error', $code = null,
+ 																		$mode = null, $options = null, $userinfo = null)
+				{
+						parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
+				}
+		}
 
 } else {
 
-    /**
-     * @todo Ultimately, this class shall be descended from PEAR_Error
-     */
-    class Services_JSON_Error
-    {
-        function Services_JSON_Error($message = 'unknown error', $code = null,
-                                     $mode = null, $options = null, $userinfo = null)
-        {
+		/**
+ 		* @todo Ultimately, this class shall be descended from PEAR_Error
+ 		*/
+		class Services_JSON_Error
+		{
+				function Services_JSON_Error($message = 'unknown error', $code = null,
+ 																		$mode = null, $options = null, $userinfo = null)
+				{
 
-        }
-    }
+				}
+		}
 
 }
-    
+		
 ?>
\ No newline at end of file
--- a/includes/json2.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/json2.php	Sun Mar 28 23:10:46 2010 -0400
@@ -29,379 +29,379 @@
  */
 class Zend_Json_Encoder
 {
-    /**
-     * Whether or not to check for possible cycling
-     *
-     * @var boolean
-     */
-    protected $_cycleCheck;
+		/**
+ 		* Whether or not to check for possible cycling
+ 		*
+ 		* @var boolean
+ 		*/
+		protected $_cycleCheck;
 
-    /**
-     * Array of visited objects; used to prevent cycling.
-     *
-     * @var array
-     */
-    protected $_visited = array();
+		/**
+ 		* Array of visited objects; used to prevent cycling.
+ 		*
+ 		* @var array
+ 		*/
+		protected $_visited = array();
 
-    /**
-     * Constructor
-     *
-     * @param boolean $cycleCheck Whether or not to check for recursion when encoding
-     * @return void
-     */
-    protected function __construct($cycleCheck = false)
-    {
-        $this->_cycleCheck = $cycleCheck;
-    }
+		/**
+ 		* Constructor
+ 		*
+ 		* @param boolean $cycleCheck Whether or not to check for recursion when encoding
+ 		* @return void
+ 		*/
+		protected function __construct($cycleCheck = false)
+		{
+				$this->_cycleCheck = $cycleCheck;
+		}
 
-    /**
-     * Use the JSON encoding scheme for the value specified
-     *
-     * @param mixed $value The value to be encoded
-     * @param boolean $cycleCheck Whether or not to check for possible object recursion when encoding
-     * @return string  The encoded value
-     */
-    public static function encode($value, $cycleCheck = false)
-    {
-        $encoder = new Zend_Json_Encoder(($cycleCheck) ? true : false);
+		/**
+ 		* Use the JSON encoding scheme for the value specified
+ 		*
+ 		* @param mixed $value The value to be encoded
+ 		* @param boolean $cycleCheck Whether or not to check for possible object recursion when encoding
+ 		* @return string  The encoded value
+ 		*/
+		public static function encode($value, $cycleCheck = false)
+		{
+				$encoder = new Zend_Json_Encoder(($cycleCheck) ? true : false);
 
-        return $encoder->_encodeValue($value);
-    }
+				return $encoder->_encodeValue($value);
+		}
 
-    /**
-     * Recursive driver which determines the type of value to be encoded
-     * and then dispatches to the appropriate method. $values are either
-     *    - objects (returns from {@link _encodeObject()})
-     *    - arrays (returns from {@link _encodeArray()})
-     *    - basic datums (e.g. numbers or strings) (returns from {@link _encodeDatum()})
-     *
-     * @param $value mixed The value to be encoded
-     * @return string Encoded value
-     */
-    protected function _encodeValue(&$value)
-    {
-        if (is_object($value)) {
-            return $this->_encodeObject($value);
-        } else if (is_array($value)) {
-            return $this->_encodeArray($value);
-        }
+		/**
+ 		* Recursive driver which determines the type of value to be encoded
+ 		* and then dispatches to the appropriate method. $values are either
+ 		*    - objects (returns from {@link _encodeObject()})
+ 		*    - arrays (returns from {@link _encodeArray()})
+ 		*    - basic datums (e.g. numbers or strings) (returns from {@link _encodeDatum()})
+ 		*
+ 		* @param $value mixed The value to be encoded
+ 		* @return string Encoded value
+ 		*/
+		protected function _encodeValue(&$value)
+		{
+				if (is_object($value)) {
+						return $this->_encodeObject($value);
+				} else if (is_array($value)) {
+						return $this->_encodeArray($value);
+				}
 
-        return $this->_encodeDatum($value);
-    }
+				return $this->_encodeDatum($value);
+		}
 
 
 
-    /**
-     * Encode an object to JSON by encoding each of the public properties
-     *
-     * A special property is added to the JSON object called '__className'
-     * that contains the name of the class of $value. This is used to decode
-     * the object on the client into a specific class.
-     *
-     * @param $value object
-     * @return string
-     * @throws Zend_Json_Exception If recursive checks are enabled and the object has been serialized previously
-     */
-    protected function _encodeObject(&$value)
-    {
-        if ($this->_cycleCheck) {
-            if ($this->_wasVisited($value)) {
-                throw new Zend_Json_Exception(
-                    'Cycles not supported in JSON encoding, cycle introduced by '
-                    . 'class "' . get_class($value) . '"'
-                );
-            }
+		/**
+ 		* Encode an object to JSON by encoding each of the public properties
+ 		*
+ 		* A special property is added to the JSON object called '__className'
+ 		* that contains the name of the class of $value. This is used to decode
+ 		* the object on the client into a specific class.
+ 		*
+ 		* @param $value object
+ 		* @return string
+ 		* @throws Zend_Json_Exception If recursive checks are enabled and the object has been serialized previously
+ 		*/
+		protected function _encodeObject(&$value)
+		{
+				if ($this->_cycleCheck) {
+						if ($this->_wasVisited($value)) {
+								throw new Zend_Json_Exception(
+										'Cycles not supported in JSON encoding, cycle introduced by '
+										. 'class "' . get_class($value) . '"'
+								);
+						}
 
-            $this->_visited[] = $value;
-        }
+						$this->_visited[] = $value;
+				}
 
-        $props = '';
-        foreach (get_object_vars($value) as $name => $propValue) {
-            if (isset($propValue)) {
-                $props .= ','
-                        . $this->_encodeValue($name)
-                        . ':'
-                        . $this->_encodeValue($propValue);
-            }
-        }
+				$props = '';
+				foreach (get_object_vars($value) as $name => $propValue) {
+						if (isset($propValue)) {
+								$props .= ','
+												. $this->_encodeValue($name)
+												. ':'
+												. $this->_encodeValue($propValue);
+						}
+				}
 
-        return '{"__className":"' . get_class($value) . '"'
-                . $props . '}';
-    }
+				return '{"__className":"' . get_class($value) . '"'
+								. $props . '}';
+		}
 
 
-    /**
-     * Determine if an object has been serialized already
-     *
-     * @param mixed $value
-     * @return boolean
-     */
-    protected function _wasVisited(&$value)
-    {
-        if (in_array($value, $this->_visited, true)) {
-            return true;
-        }
+		/**
+ 		* Determine if an object has been serialized already
+ 		*
+ 		* @param mixed $value
+ 		* @return boolean
+ 		*/
+		protected function _wasVisited(&$value)
+		{
+				if (in_array($value, $this->_visited, true)) {
+						return true;
+				}
 
-        return false;
-    }
+				return false;
+		}
 
 
-    /**
-     * JSON encode an array value
-     *
-     * Recursively encodes each value of an array and returns a JSON encoded
-     * array string.
-     *
-     * Arrays are defined as integer-indexed arrays starting at index 0, where
-     * the last index is (count($array) -1); any deviation from that is
-     * considered an associative array, and will be encoded as such.
-     *
-     * @param $array array
-     * @return string
-     */
-    protected function _encodeArray(&$array)
-    {
-        $tmpArray = array();
+		/**
+ 		* JSON encode an array value
+ 		*
+ 		* Recursively encodes each value of an array and returns a JSON encoded
+ 		* array string.
+ 		*
+ 		* Arrays are defined as integer-indexed arrays starting at index 0, where
+ 		* the last index is (count($array) -1); any deviation from that is
+ 		* considered an associative array, and will be encoded as such.
+ 		*
+ 		* @param $array array
+ 		* @return string
+ 		*/
+		protected function _encodeArray(&$array)
+		{
+				$tmpArray = array();
 
-        // Check for associative array
-        if (!empty($array) && (array_keys($array) !== range(0, count($array) - 1))) {
-            // Associative array
-            $result = '{';
-            foreach ($array as $key => $value) {
-                $key = (string) $key;
-                $tmpArray[] = $this->_encodeString($key)
-                            . ':'
-                            . $this->_encodeValue($value);
-            }
-            $result .= implode(',', $tmpArray);
-            $result .= '}';
-        } else {
-            // Indexed array
-            $result = '[';
-            $length = count($array);
-            for ($i = 0; $i < $length; $i++) {
-                $tmpArray[] = $this->_encodeValue($array[$i]);
-            }
-            $result .= implode(',', $tmpArray);
-            $result .= ']';
-        }
+				// Check for associative array
+				if (!empty($array) && (array_keys($array) !== range(0, count($array) - 1))) {
+						// Associative array
+						$result = '{';
+						foreach ($array as $key => $value) {
+								$key = (string) $key;
+								$tmpArray[] = $this->_encodeString($key)
+														. ':'
+														. $this->_encodeValue($value);
+						}
+						$result .= implode(',', $tmpArray);
+						$result .= '}';
+				} else {
+						// Indexed array
+						$result = '[';
+						$length = count($array);
+						for ($i = 0; $i < $length; $i++) {
+								$tmpArray[] = $this->_encodeValue($array[$i]);
+						}
+						$result .= implode(',', $tmpArray);
+						$result .= ']';
+				}
 
-        return $result;
-    }
+				return $result;
+		}
 
 
-    /**
-     * JSON encode a basic data type (string, number, boolean, null)
-     *
-     * If value type is not a string, number, boolean, or null, the string
-     * 'null' is returned.
-     *
-     * @param $value mixed
-     * @return string
-     */
-    protected function _encodeDatum(&$value)
-    {
-        $result = 'null';
+		/**
+ 		* JSON encode a basic data type (string, number, boolean, null)
+ 		*
+ 		* If value type is not a string, number, boolean, or null, the string
+ 		* 'null' is returned.
+ 		*
+ 		* @param $value mixed
+ 		* @return string
+ 		*/
+		protected function _encodeDatum(&$value)
+		{
+				$result = 'null';
 
-        if (is_int($value) || is_float($value)) {
-            $result = (string)$value;
-        } elseif (is_string($value)) {
-            $result = $this->_encodeString($value);
-        } elseif (is_bool($value)) {
-            $result = $value ? 'true' : 'false';
-        }
+				if (is_int($value) || is_float($value)) {
+						$result = (string)$value;
+				} elseif (is_string($value)) {
+						$result = $this->_encodeString($value);
+				} elseif (is_bool($value)) {
+						$result = $value ? 'true' : 'false';
+				}
 
-        return $result;
-    }
+				return $result;
+		}
 
 
-    /**
-     * JSON encode a string value by escaping characters as necessary
-     *
-     * @param $value string
-     * @return string
-     */
-    protected function _encodeString(&$string)
-    {
-        // Escape these characters with a backslash:
-        // " \ / \n \r \t \b \f
-        $search  = array('\\', "\n", "\t", "\r", "\b", "\f", '"');
-        $replace = array('\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\"');
-        $string  = str_replace($search, $replace, $string);
+		/**
+ 		* JSON encode a string value by escaping characters as necessary
+ 		*
+ 		* @param $value string
+ 		* @return string
+ 		*/
+		protected function _encodeString(&$string)
+		{
+				// Escape these characters with a backslash:
+				// " \ / \n \r \t \b \f
+				$search  = array('\\', "\n", "\t", "\r", "\b", "\f", '"');
+				$replace = array('\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\"');
+				$string  = str_replace($search, $replace, $string);
 
-        // Escape certain ASCII characters:
-        // 0x08 => \b
-        // 0x0c => \f
-        $string = str_replace(array(chr(0x08), chr(0x0C)), array('\b', '\f'), $string);
+				// Escape certain ASCII characters:
+				// 0x08 => \b
+				// 0x0c => \f
+				$string = str_replace(array(chr(0x08), chr(0x0C)), array('\b', '\f'), $string);
 
-        return '"' . $string . '"';
-    }
+				return '"' . $string . '"';
+		}
 
 
-    /**
-     * Encode the constants associated with the ReflectionClass
-     * parameter. The encoding format is based on the class2 format
-     *
-     * @param $cls ReflectionClass
-     * @return string Encoded constant block in class2 format
-     */
-    private static function _encodeConstants(ReflectionClass $cls)
-    {
-        $result    = "constants : {";
-        $constants = $cls->getConstants();
+		/**
+ 		* Encode the constants associated with the ReflectionClass
+ 		* parameter. The encoding format is based on the class2 format
+ 		*
+ 		* @param $cls ReflectionClass
+ 		* @return string Encoded constant block in class2 format
+ 		*/
+		private static function _encodeConstants(ReflectionClass $cls)
+		{
+				$result    = "constants : {";
+				$constants = $cls->getConstants();
 
-        $tmpArray = array();
-        if (!empty($constants)) {
-            foreach ($constants as $key => $value) {
-                $tmpArray[] = "$key: " . self::encode($value);
-            }
+				$tmpArray = array();
+				if (!empty($constants)) {
+						foreach ($constants as $key => $value) {
+								$tmpArray[] = "$key: " . self::encode($value);
+						}
 
-            $result .= implode(', ', $tmpArray);
-        }
+						$result .= implode(', ', $tmpArray);
+				}
 
-        return $result . "}";
-    }
+				return $result . "}";
+		}
 
 
-    /**
-     * Encode the public methods of the ReflectionClass in the
-     * class2 format
-     *
-     * @param $cls ReflectionClass
-     * @return string Encoded method fragment
-     *
-     */
-    private static function _encodeMethods(ReflectionClass $cls)
-    {
-        $methods = $cls->getMethods();
-        $result = 'methods:{';
+		/**
+ 		* Encode the public methods of the ReflectionClass in the
+ 		* class2 format
+ 		*
+ 		* @param $cls ReflectionClass
+ 		* @return string Encoded method fragment
+ 		*
+ 		*/
+		private static function _encodeMethods(ReflectionClass $cls)
+		{
+				$methods = $cls->getMethods();
+				$result = 'methods:{';
 
-        $started = false;
-        foreach ($methods as $method) {
-            if (! $method->isPublic() || !$method->isUserDefined()) {
-                continue;
-            }
+				$started = false;
+				foreach ($methods as $method) {
+						if (! $method->isPublic() || !$method->isUserDefined()) {
+								continue;
+						}
 
-            if ($started) {
-                $result .= ',';
-            }
-            $started = true;
+						if ($started) {
+								$result .= ',';
+						}
+						$started = true;
 
-            $result .= '' . $method->getName(). ':function(';
+						$result .= '' . $method->getName(). ':function(';
 
-            if ('__construct' != $method->getName()) {
-                $parameters  = $method->getParameters();
-                $paramCount  = count($parameters);
-                $argsStarted = false;
+						if ('__construct' != $method->getName()) {
+								$parameters  = $method->getParameters();
+								$paramCount  = count($parameters);
+								$argsStarted = false;
 
-                $argNames = "var argNames=[";
-                foreach ($parameters as $param) {
-                    if ($argsStarted) {
-                        $result .= ',';
-                    }
+								$argNames = "var argNames=[";
+								foreach ($parameters as $param) {
+										if ($argsStarted) {
+												$result .= ',';
+										}
 
-                    $result .= $param->getName();
+										$result .= $param->getName();
 
-                    if ($argsStarted) {
-                        $argNames .= ',';
-                    }
+										if ($argsStarted) {
+												$argNames .= ',';
+										}
 
-                    $argNames .= '"' . $param->getName() . '"';
+										$argNames .= '"' . $param->getName() . '"';
 
-                    $argsStarted = true;
-                }
-                $argNames .= "];";
+										$argsStarted = true;
+								}
+								$argNames .= "];";
 
-                $result .= "){"
-                         . $argNames
-                         . 'var result = ZAjaxEngine.invokeRemoteMethod('
-                         . "this, '" . $method->getName()
-                         . "',argNames,arguments);"
-                         . 'return(result);}';
-            } else {
-                $result .= "){}";
-            }
-        }
+								$result .= "){"
+ 												. $argNames
+ 												. 'var result = ZAjaxEngine.invokeRemoteMethod('
+ 												. "this, '" . $method->getName()
+ 												. "',argNames,arguments);"
+ 												. 'return(result);}';
+						} else {
+								$result .= "){}";
+						}
+				}
 
-        return $result . "}";
-    }
+				return $result . "}";
+		}
 
 
-    /**
-     * Encode the public properties of the ReflectionClass in the class2
-     * format.
-     *
-     * @param $cls ReflectionClass
-     * @return string Encode properties list
-     *
-     */
-    private static function _encodeVariables(ReflectionClass $cls)
-    {
-        $properties = $cls->getProperties();
-        $propValues = get_class_vars($cls->getName());
-        $result = "variables:{";
-        $cnt = 0;
+		/**
+ 		* Encode the public properties of the ReflectionClass in the class2
+ 		* format.
+ 		*
+ 		* @param $cls ReflectionClass
+ 		* @return string Encode properties list
+ 		*
+ 		*/
+		private static function _encodeVariables(ReflectionClass $cls)
+		{
+				$properties = $cls->getProperties();
+				$propValues = get_class_vars($cls->getName());
+				$result = "variables:{";
+				$cnt = 0;
 
-        $tmpArray = array();
-        foreach ($properties as $prop) {
-            if (! $prop->isPublic()) {
-                continue;
-            }
+				$tmpArray = array();
+				foreach ($properties as $prop) {
+						if (! $prop->isPublic()) {
+								continue;
+						}
 
-            $tmpArray[] = $prop->getName()
-                        . ':'
-                        . self::encode($propValues[$prop->getName()]);
-        }
-        $result .= implode(',', $tmpArray);
+						$tmpArray[] = $prop->getName()
+												. ':'
+												. self::encode($propValues[$prop->getName()]);
+				}
+				$result .= implode(',', $tmpArray);
 
-        return $result . "}";
-    }
+				return $result . "}";
+		}
 
-    /**
-     * Encodes the given $className into the class2 model of encoding PHP
-     * classes into JavaScript class2 classes.
-     * NOTE: Currently only public methods and variables are proxied onto
-     * the client machine
-     *
-     * @param $className string The name of the class, the class must be
-     * instantiable using a null constructor
-     * @param $package string Optional package name appended to JavaScript
-     * proxy class name
-     * @return string The class2 (JavaScript) encoding of the class
-     * @throws Zend_Json_Exception
-     */
-    public static function encodeClass($className, $package = '')
-    {
-        $cls = new ReflectionClass($className);
-        if (! $cls->isInstantiable()) {
-            throw new Zend_Json_Exception("$className must be instantiable");
-        }
+		/**
+ 		* Encodes the given $className into the class2 model of encoding PHP
+ 		* classes into JavaScript class2 classes.
+ 		* NOTE: Currently only public methods and variables are proxied onto
+ 		* the client machine
+ 		*
+ 		* @param $className string The name of the class, the class must be
+ 		* instantiable using a null constructor
+ 		* @param $package string Optional package name appended to JavaScript
+ 		* proxy class name
+ 		* @return string The class2 (JavaScript) encoding of the class
+ 		* @throws Zend_Json_Exception
+ 		*/
+		public static function encodeClass($className, $package = '')
+		{
+				$cls = new ReflectionClass($className);
+				if (! $cls->isInstantiable()) {
+						throw new Zend_Json_Exception("$className must be instantiable");
+				}
 
-        return "Class.create('$package$className',{"
-                . self::_encodeConstants($cls)    .","
-                . self::_encodeMethods($cls)      .","
-                . self::_encodeVariables($cls)    .'});';
-    }
+				return "Class.create('$package$className',{"
+								. self::_encodeConstants($cls)    .","
+								. self::_encodeMethods($cls)      .","
+								. self::_encodeVariables($cls)    .'});';
+		}
 
 
-    /**
-     * Encode several classes at once
-     *
-     * Returns JSON encoded classes, using {@link encodeClass()}.
-     *
-     * @param array $classNames
-     * @param string $package
-     * @return string
-     */
-    public static function encodeClasses(array $classNames, $package = '')
-    {
-        $result = '';
-        foreach ($classNames as $className) {
-            $result .= self::encodeClass($className, $package);
-        }
+		/**
+ 		* Encode several classes at once
+ 		*
+ 		* Returns JSON encoded classes, using {@link encodeClass()}.
+ 		*
+ 		* @param array $classNames
+ 		* @param string $package
+ 		* @return string
+ 		*/
+		public static function encodeClasses(array $classNames, $package = '')
+		{
+				$result = '';
+				foreach ($classNames as $className) {
+						$result .= self::encodeClass($className, $package);
+				}
 
-        return $result;
-    }
+				return $result;
+		}
 
 }
 
@@ -415,536 +415,536 @@
  */
 class Zend_Json_Decoder
 {
-    /**
-     * Parse tokens used to decode the JSON object. These are not
-     * for public consumption, they are just used internally to the
-     * class.
-     */
-    const EOF          = 0;
-    const DATUM        = 1;
-    const LBRACE       = 2;
-    const LBRACKET     = 3;
-    const RBRACE       = 4;
-    const RBRACKET     = 5;
-    const COMMA        = 6;
-    const COLON        = 7;
+		/**
+ 		* Parse tokens used to decode the JSON object. These are not
+ 		* for public consumption, they are just used internally to the
+ 		* class.
+ 		*/
+		const EOF          = 0;
+		const DATUM        = 1;
+		const LBRACE       = 2;
+		const LBRACKET     = 3;
+		const RBRACE       = 4;
+		const RBRACKET     = 5;
+		const COMMA        = 6;
+		const COLON        = 7;
 
-    /**
-     * Use to maintain a "pointer" to the source being decoded
-     *
-     * @var string
-     */
-    protected $_source;
+		/**
+ 		* Use to maintain a "pointer" to the source being decoded
+ 		*
+ 		* @var string
+ 		*/
+		protected $_source;
 
-    /**
-     * Caches the source length
-     *
-     * @var int
-     */
-    protected $_sourceLength;
+		/**
+ 		* Caches the source length
+ 		*
+ 		* @var int
+ 		*/
+		protected $_sourceLength;
 
-    /**
-     * The offset within the souce being decoded
-     *
-     * @var int
-     *
-     */
-    protected $_offset;
+		/**
+ 		* The offset within the souce being decoded
+ 		*
+ 		* @var int
+ 		*
+ 		*/
+		protected $_offset;
 
-    /**
-     * The current token being considered in the parser cycle
-     *
-     * @var int
-     */
-    protected $_token;
+		/**
+ 		* The current token being considered in the parser cycle
+ 		*
+ 		* @var int
+ 		*/
+		protected $_token;
 
-    /**
-     * Flag indicating how objects should be decoded
-     *
-     * @var int
-     * @access protected
-     */
-    protected $_decodeType;
+		/**
+ 		* Flag indicating how objects should be decoded
+ 		*
+ 		* @var int
+ 		* @access protected
+ 		*/
+		protected $_decodeType;
 
-    /**
-     * Constructor
-     *
-     * @param string $source String source to decode
-     * @param int $decodeType How objects should be decoded -- see
-     * {@link Zend_Json::TYPE_ARRAY} and {@link Zend_Json::TYPE_OBJECT} for
-     * valid values
-     * @return void
-     */
-    protected function __construct($source, $decodeType)
-    {
-        
-        // eliminate comments
-        $source = preg_replace(array(
+		/**
+ 		* Constructor
+ 		*
+ 		* @param string $source String source to decode
+ 		* @param int $decodeType How objects should be decoded -- see
+ 		* {@link Zend_Json::TYPE_ARRAY} and {@link Zend_Json::TYPE_OBJECT} for
+ 		* valid values
+ 		* @return void
+ 		*/
+		protected function __construct($source, $decodeType)
+		{
+				
+				// eliminate comments
+				$source = preg_replace(array(
 
-                  // eliminate single line comments in '// ...' form
-                  '#^\s*//(.+)$#m',
-    
-                  // eliminate multi-line comments in '/* ... */' form, at start of string
-                  '#^\s*/\*(.+)\*/#Us',
-    
-                  // eliminate multi-line comments in '/* ... */' form, at end of string
-                  '#/\*(.+)\*/\s*$#Us'
-    
-              ), '', $source);
-        
-        // Set defaults
-        $this->_source       = $source;
-        $this->_sourceLength = strlen($source);
-        $this->_token        = self::EOF;
-        $this->_offset       = 0;
+									// eliminate single line comments in '// ...' form
+									'#^\s*//(.+)$#m',
+		
+									// eliminate multi-line comments in '/* ... */' form, at start of string
+									'#^\s*/\*(.+)\*/#Us',
+		
+									// eliminate multi-line comments in '/* ... */' form, at end of string
+									'#/\*(.+)\*/\s*$#Us'
+		
+							), '', $source);
+				
+				// Set defaults
+				$this->_source       = $source;
+				$this->_sourceLength = strlen($source);
+				$this->_token        = self::EOF;
+				$this->_offset       = 0;
 
-        // Normalize and set $decodeType
-        if (!in_array($decodeType, array(Zend_Json::TYPE_ARRAY, Zend_Json::TYPE_OBJECT)))
-        {
-            $decodeType = Zend_Json::TYPE_ARRAY;
-        }
-        $this->_decodeType   = $decodeType;
+				// Normalize and set $decodeType
+				if (!in_array($decodeType, array(Zend_Json::TYPE_ARRAY, Zend_Json::TYPE_OBJECT)))
+				{
+						$decodeType = Zend_Json::TYPE_ARRAY;
+				}
+				$this->_decodeType   = $decodeType;
 
-        // Set pointer at first token
-        $this->_getNextToken();
-    }
+				// Set pointer at first token
+				$this->_getNextToken();
+		}
 
-    /**
-     * Decode a JSON source string
-     *
-     * Decodes a JSON encoded string. The value returned will be one of the
-     * following:
-     *        - integer
-     *        - float
-     *        - boolean
-     *        - null
-     *      - StdClass
-     *      - array
-     *         - array of one or more of the above types
-     *
-     * By default, decoded objects will be returned as associative arrays; to
-     * return a StdClass object instead, pass {@link Zend_Json::TYPE_OBJECT} to
-     * the $objectDecodeType parameter.
-     *
-     * Throws a Zend_Json_Exception if the source string is null.
-     *
-     * @static
-     * @access public
-     * @param string $source String to be decoded
-     * @param int $objectDecodeType How objects should be decoded; should be
-     * either or {@link Zend_Json::TYPE_ARRAY} or
-     * {@link Zend_Json::TYPE_OBJECT}; defaults to TYPE_ARRAY
-     * @return mixed
-     * @throws Zend_Json_Exception
-     */
-    public static function decode($source = null, $objectDecodeType = Zend_Json::TYPE_ARRAY)
-    {
-        if (null === $source) {
-            throw new Zend_Json_Exception('Must specify JSON encoded source for decoding');
-        } elseif (!is_string($source)) {
-            throw new Zend_Json_Exception('Can only decode JSON encoded strings');
-        }
+		/**
+ 		* Decode a JSON source string
+ 		*
+ 		* Decodes a JSON encoded string. The value returned will be one of the
+ 		* following:
+ 		*        - integer
+ 		*        - float
+ 		*        - boolean
+ 		*        - null
+ 		*      - StdClass
+ 		*      - array
+ 		*         - array of one or more of the above types
+ 		*
+ 		* By default, decoded objects will be returned as associative arrays; to
+ 		* return a StdClass object instead, pass {@link Zend_Json::TYPE_OBJECT} to
+ 		* the $objectDecodeType parameter.
+ 		*
+ 		* Throws a Zend_Json_Exception if the source string is null.
+ 		*
+ 		* @static
+ 		* @access public
+ 		* @param string $source String to be decoded
+ 		* @param int $objectDecodeType How objects should be decoded; should be
+ 		* either or {@link Zend_Json::TYPE_ARRAY} or
+ 		* {@link Zend_Json::TYPE_OBJECT}; defaults to TYPE_ARRAY
+ 		* @return mixed
+ 		* @throws Zend_Json_Exception
+ 		*/
+		public static function decode($source = null, $objectDecodeType = Zend_Json::TYPE_ARRAY)
+		{
+				if (null === $source) {
+						throw new Zend_Json_Exception('Must specify JSON encoded source for decoding');
+				} elseif (!is_string($source)) {
+						throw new Zend_Json_Exception('Can only decode JSON encoded strings');
+				}
 
-        $decoder = new self($source, $objectDecodeType);
+				$decoder = new self($source, $objectDecodeType);
 
-        return $decoder->_decodeValue();
-    }
+				return $decoder->_decodeValue();
+		}
 
 
-    /**
-     * Recursive driving rountine for supported toplevel tops
-     *
-     * @return mixed
-     */
-    protected function _decodeValue()
-    {
-        switch ($this->_token) {
-            case self::DATUM:
-                $result  = $this->_tokenValue;
-                $this->_getNextToken();
-                return($result);
-                break;
-            case self::LBRACE:
-                return($this->_decodeObject());
-                break;
-            case self::LBRACKET:
-                return($this->_decodeArray());
-                break;
-            default:
-                return null;
-                break;
-        }
-    }
+		/**
+ 		* Recursive driving rountine for supported toplevel tops
+ 		*
+ 		* @return mixed
+ 		*/
+		protected function _decodeValue()
+		{
+				switch ($this->_token) {
+						case self::DATUM:
+								$result  = $this->_tokenValue;
+								$this->_getNextToken();
+								return($result);
+								break;
+						case self::LBRACE:
+								return($this->_decodeObject());
+								break;
+						case self::LBRACKET:
+								return($this->_decodeArray());
+								break;
+						default:
+								return null;
+								break;
+				}
+		}
 
-    /**
-     * Decodes an object of the form:
-     *  { "attribute: value, "attribute2" : value,...}
-     *
-     * If ZJsonEnoder or ZJAjax was used to encode the original object
-     * then a special attribute called __className which specifies a class
-     * name that should wrap the data contained within the encoded source.
-     *
-     * Decodes to either an array or StdClass object, based on the value of
-     * {@link $_decodeType}. If invalid $_decodeType present, returns as an
-     * array.
-     *
-     * @return array|StdClass
-     */
-    protected function _decodeObject()
-    {
-        $members = array();
-        $tok = $this->_getNextToken();
+		/**
+ 		* Decodes an object of the form:
+ 		*  { "attribute: value, "attribute2" : value,...}
+ 		*
+ 		* If ZJsonEnoder or ZJAjax was used to encode the original object
+ 		* then a special attribute called __className which specifies a class
+ 		* name that should wrap the data contained within the encoded source.
+ 		*
+ 		* Decodes to either an array or StdClass object, based on the value of
+ 		* {@link $_decodeType}. If invalid $_decodeType present, returns as an
+ 		* array.
+ 		*
+ 		* @return array|StdClass
+ 		*/
+		protected function _decodeObject()
+		{
+				$members = array();
+				$tok = $this->_getNextToken();
 
-        while ($tok && $tok != self::RBRACE) {
-            if ($tok != self::DATUM || ! is_string($this->_tokenValue)) {
-                throw new Zend_Json_Exception('Missing key in object encoding: ' . $this->_source);
-            }
+				while ($tok && $tok != self::RBRACE) {
+						if ($tok != self::DATUM || ! is_string($this->_tokenValue)) {
+								throw new Zend_Json_Exception('Missing key in object encoding: ' . $this->_source);
+						}
 
-            $key = $this->_tokenValue;
-            $tok = $this->_getNextToken();
+						$key = $this->_tokenValue;
+						$tok = $this->_getNextToken();
 
-            if ($tok != self::COLON) {
-                throw new Zend_Json_Exception('Missing ":" in object encoding: ' . $this->_source);
-            }
+						if ($tok != self::COLON) {
+								throw new Zend_Json_Exception('Missing ":" in object encoding: ' . $this->_source);
+						}
 
-            $tok = $this->_getNextToken();
-            $members[$key] = $this->_decodeValue();
-            $tok = $this->_token;
+						$tok = $this->_getNextToken();
+						$members[$key] = $this->_decodeValue();
+						$tok = $this->_token;
 
-            if ($tok == self::RBRACE) {
-                break;
-            }
+						if ($tok == self::RBRACE) {
+								break;
+						}
 
-            if ($tok != self::COMMA) {
-                throw new Zend_Json_Exception('Missing "," in object encoding: ' . $this->_source);
-            }
+						if ($tok != self::COMMA) {
+								throw new Zend_Json_Exception('Missing "," in object encoding: ' . $this->_source);
+						}
 
-            $tok = $this->_getNextToken();
-        }
+						$tok = $this->_getNextToken();
+				}
 
-        switch ($this->_decodeType) {
-            case Zend_Json::TYPE_OBJECT:
-                // Create new StdClass and populate with $members
-                $result = new StdClass();
-                foreach ($members as $key => $value) {
-                    $result->$key = $value;
-                }
-                break;
-            case Zend_Json::TYPE_ARRAY:
-            default:
-                $result = $members;
-                break;
-        }
+				switch ($this->_decodeType) {
+						case Zend_Json::TYPE_OBJECT:
+								// Create new StdClass and populate with $members
+								$result = new StdClass();
+								foreach ($members as $key => $value) {
+										$result->$key = $value;
+								}
+								break;
+						case Zend_Json::TYPE_ARRAY:
+						default:
+								$result = $members;
+								break;
+				}
 
-        $this->_getNextToken();
-        return $result;
-    }
+				$this->_getNextToken();
+				return $result;
+		}
 
-    /**
-     * Decodes a JSON array format:
-     *    [element, element2,...,elementN]
-     *
-     * @return array
-     */
-    protected function _decodeArray()
-    {
-        $result = array();
-        $starttok = $tok = $this->_getNextToken(); // Move past the '['
-        $index  = 0;
+		/**
+ 		* Decodes a JSON array format:
+ 		*    [element, element2,...,elementN]
+ 		*
+ 		* @return array
+ 		*/
+		protected function _decodeArray()
+		{
+				$result = array();
+				$starttok = $tok = $this->_getNextToken(); // Move past the '['
+				$index  = 0;
 
-        while ($tok && $tok != self::RBRACKET) {
-            $result[$index++] = $this->_decodeValue();
+				while ($tok && $tok != self::RBRACKET) {
+						$result[$index++] = $this->_decodeValue();
 
-            $tok = $this->_token;
+						$tok = $this->_token;
 
-            if ($tok == self::RBRACKET || !$tok) {
-                break;
-            }
+						if ($tok == self::RBRACKET || !$tok) {
+								break;
+						}
 
-            if ($tok != self::COMMA) {
-                throw new Zend_Json_Exception('Missing "," in array encoding: ' . $this->_source);
-            }
+						if ($tok != self::COMMA) {
+								throw new Zend_Json_Exception('Missing "," in array encoding: ' . $this->_source);
+						}
 
-            $tok = $this->_getNextToken();
-        }
+						$tok = $this->_getNextToken();
+				}
 
-        $this->_getNextToken();
-        return($result);
-    }
+				$this->_getNextToken();
+				return($result);
+		}
 
 
-    /**
-     * Removes whitepsace characters from the source input
-     */
-    protected function _eatWhitespace()
-    {
-        if (preg_match(
-                '/([\t\b\f\n\r ])*/s',
-                $this->_source,
-                $matches,
-                PREG_OFFSET_CAPTURE,
-                $this->_offset)
-            && $matches[0][1] == $this->_offset)
-        {
-            $this->_offset += strlen($matches[0][0]);
-        }
-    }
+		/**
+ 		* Removes whitepsace characters from the source input
+ 		*/
+		protected function _eatWhitespace()
+		{
+				if (preg_match(
+								'/([\t\b\f\n\r ])*/s',
+								$this->_source,
+								$matches,
+								PREG_OFFSET_CAPTURE,
+								$this->_offset)
+						&& $matches[0][1] == $this->_offset)
+				{
+						$this->_offset += strlen($matches[0][0]);
+				}
+		}
 
 
-    /**
-     * Retrieves the next token from the source stream
-     *
-     * @return int Token constant value specified in class definition
-     */
-    protected function _getNextToken()
-    {
-        $this->_token      = self::EOF;
-        $this->_tokenValue = null;
-        $this->_eatWhitespace();
-        
-        if ($this->_offset >= $this->_sourceLength) {
-            return(self::EOF);
-        }
+		/**
+ 		* Retrieves the next token from the source stream
+ 		*
+ 		* @return int Token constant value specified in class definition
+ 		*/
+		protected function _getNextToken()
+		{
+				$this->_token      = self::EOF;
+				$this->_tokenValue = null;
+				$this->_eatWhitespace();
+				
+				if ($this->_offset >= $this->_sourceLength) {
+						return(self::EOF);
+				}
 
-        $str        = $this->_source;
-        $str_length = $this->_sourceLength;
-        $i          = $this->_offset;
-        $start      = $i;
-        
-        switch ($str{$i}) {
-            case '{':
-               $this->_token = self::LBRACE;
-               break;
-            case '}':
-                $this->_token = self::RBRACE;
-                break;
-            case '[':
-                $this->_token = self::LBRACKET;
-                break;
-            case ']':
-                $this->_token = self::RBRACKET;
-                break;
-            case ',':
-                $this->_token = self::COMMA;
-                break;
-            case ':':
-                $this->_token = self::COLON;
-                break;
-            case  '"':
-                $result = '';
-                do {
-                    $i++;
-                    if ($i >= $str_length) {
-                        break;
-                    }
+				$str        = $this->_source;
+				$str_length = $this->_sourceLength;
+				$i          = $this->_offset;
+				$start      = $i;
+				
+				switch ($str{$i}) {
+						case '{':
+ 							$this->_token = self::LBRACE;
+ 							break;
+						case '}':
+								$this->_token = self::RBRACE;
+								break;
+						case '[':
+								$this->_token = self::LBRACKET;
+								break;
+						case ']':
+								$this->_token = self::RBRACKET;
+								break;
+						case ',':
+								$this->_token = self::COMMA;
+								break;
+						case ':':
+								$this->_token = self::COLON;
+								break;
+						case  '"':
+								$result = '';
+								do {
+										$i++;
+										if ($i >= $str_length) {
+												break;
+										}
 
-                    $chr = $str{$i};
-                    if ($chr == '\\') {
-                        $i++;
-                        if ($i >= $str_length) {
-                            break;
-                        }
-                        $chr = $str{$i};
-                        switch ($chr) {
-                            case '"' :
-                                $result .= '"';
-                                break;
-                            case '\\':
-                                $result .= '\\';
-                                break;
-                            case '/' :
-                                $result .= '/';
-                                break;
-                            case 'b' :
-                                $result .= chr(8);
-                                break;
-                            case 'f' :
-                                $result .= chr(12);
-                                break;
-                            case 'n' :
-                                $result .= chr(10);
-                                break;
-                            case 'r' :
-                                $result .= chr(13);
-                                break;
-                            case 't' :
-                                $result .= chr(9);
-                                break;
-                            case '\'' :
-                                $result .= '\'';
-                                break;
-                            case 'u':
-                              $result .= self::decode_unicode_byte(substr($str, $i + 1, 4));
-                              $i += 4;
-                              break;
-                            default:
-                                throw new Zend_Json_Exception("Illegal escape "
-                                    .  "sequence '" . $chr . "'");
-                            }
-                    } elseif ($chr == '"') {
-                        break;
-                    } else {
-                        $result .= $chr;
-                    }
-                } while ($i < $str_length);
+										$chr = $str{$i};
+										if ($chr == '\\') {
+												$i++;
+												if ($i >= $str_length) {
+														break;
+												}
+												$chr = $str{$i};
+												switch ($chr) {
+														case '"' :
+																$result .= '"';
+																break;
+														case '\\':
+																$result .= '\\';
+																break;
+														case '/' :
+																$result .= '/';
+																break;
+														case 'b' :
+																$result .= chr(8);
+																break;
+														case 'f' :
+																$result .= chr(12);
+																break;
+														case 'n' :
+																$result .= chr(10);
+																break;
+														case 'r' :
+																$result .= chr(13);
+																break;
+														case 't' :
+																$result .= chr(9);
+																break;
+														case '\'' :
+																$result .= '\'';
+																break;
+														case 'u':
+															$result .= self::decode_unicode_byte(substr($str, $i + 1, 4));
+															$i += 4;
+															break;
+														default:
+																throw new Zend_Json_Exception("Illegal escape "
+																		.  "sequence '" . $chr . "'");
+														}
+										} elseif ($chr == '"') {
+												break;
+										} else {
+												$result .= $chr;
+										}
+								} while ($i < $str_length);
 
-                $this->_token = self::DATUM;
-                //$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
-                $this->_tokenValue = $result;
-                break;
-            case  "'":
-                $result = '';
-                do {
-                    $i++;
-                    if ($i >= $str_length) {
-                        break;
-                    }
+								$this->_token = self::DATUM;
+								//$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
+								$this->_tokenValue = $result;
+								break;
+						case  "'":
+								$result = '';
+								do {
+										$i++;
+										if ($i >= $str_length) {
+												break;
+										}
 
-                    $chr = $str{$i};
-                    if ($chr == '\\') {
-                        $i++;
-                        if ($i >= $str_length) {
-                            break;
-                        }
-                        $chr = $str{$i};
-                        switch ($chr) {
-                            case "'" :
-                                $result .= "'";
-                                break;
-                            case '\\':
-                                $result .= '\\';
-                                break;
-                            case '/' :
-                                $result .= '/';
-                                break;
-                            case 'b' :
-                                $result .= chr(8);
-                                break;
-                            case 'f' :
-                                $result .= chr(12);
-                                break;
-                            case 'n' :
-                                $result .= chr(10);
-                                break;
-                            case 'r' :
-                                $result .= chr(13);
-                                break;
-                            case 't' :
-                                $result .= chr(9);
-                                break;
-                            case '"' :
-                                $result .= '"';
-                                break;
-                            default:
-                                throw new Zend_Json_Exception("Illegal escape "
-                                    .  "sequence '" . $chr . "'");
-                            }
-                    } elseif ($chr == "'") {
-                        break;
-                    } else {
-                        $result .= $chr;
-                    }
-                } while ($i < $str_length);
+										$chr = $str{$i};
+										if ($chr == '\\') {
+												$i++;
+												if ($i >= $str_length) {
+														break;
+												}
+												$chr = $str{$i};
+												switch ($chr) {
+														case "'" :
+																$result .= "'";
+																break;
+														case '\\':
+																$result .= '\\';
+																break;
+														case '/' :
+																$result .= '/';
+																break;
+														case 'b' :
+																$result .= chr(8);
+																break;
+														case 'f' :
+																$result .= chr(12);
+																break;
+														case 'n' :
+																$result .= chr(10);
+																break;
+														case 'r' :
+																$result .= chr(13);
+																break;
+														case 't' :
+																$result .= chr(9);
+																break;
+														case '"' :
+																$result .= '"';
+																break;
+														default:
+																throw new Zend_Json_Exception("Illegal escape "
+																		.  "sequence '" . $chr . "'");
+														}
+										} elseif ($chr == "'") {
+												break;
+										} else {
+												$result .= $chr;
+										}
+								} while ($i < $str_length);
 
-                $this->_token = self::DATUM;
-                //$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
-                $this->_tokenValue = $result;
-                break;
-            case 't':
-                if (($i+ 3) < $str_length && substr($str, $start, 4) == "true") {
-                    $this->_token = self::DATUM;
-                }
-                $this->_tokenValue = true;
-                $i += 3;
-                break;
-            case 'f':
-                if (($i+ 4) < $str_length && substr($str, $start, 5) == "false") {
-                    $this->_token = self::DATUM;
-                }
-                $this->_tokenValue = false;
-                $i += 4;
-                break;
-            case 'n':
-                if (($i+ 3) < $str_length && substr($str, $start, 4) == "null") {
-                    $this->_token = self::DATUM;
-                }
-                $this->_tokenValue = NULL;
-                $i += 3;
-                break;
-              case ' ':
-                break;
-        }
+								$this->_token = self::DATUM;
+								//$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
+								$this->_tokenValue = $result;
+								break;
+						case 't':
+								if (($i+ 3) < $str_length && substr($str, $start, 4) == "true") {
+										$this->_token = self::DATUM;
+								}
+								$this->_tokenValue = true;
+								$i += 3;
+								break;
+						case 'f':
+								if (($i+ 4) < $str_length && substr($str, $start, 5) == "false") {
+										$this->_token = self::DATUM;
+								}
+								$this->_tokenValue = false;
+								$i += 4;
+								break;
+						case 'n':
+								if (($i+ 3) < $str_length && substr($str, $start, 4) == "null") {
+										$this->_token = self::DATUM;
+								}
+								$this->_tokenValue = NULL;
+								$i += 3;
+								break;
+							case ' ':
+								break;
+				}
 
-        if ($this->_token != self::EOF) {
-            $this->_offset = $i + 1; // Consume the last token character
-            return($this->_token);
-        }
+				if ($this->_token != self::EOF) {
+						$this->_offset = $i + 1; // Consume the last token character
+						return($this->_token);
+				}
 
-        $chr = $str{$i};
-        if ($chr == '-' || $chr == '.' || ($chr >= '0' && $chr <= '9')) {
-            if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s',
-                $str, $matches, PREG_OFFSET_CAPTURE, $start) && $matches[0][1] == $start) {
+				$chr = $str{$i};
+				if ($chr == '-' || $chr == '.' || ($chr >= '0' && $chr <= '9')) {
+						if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s',
+								$str, $matches, PREG_OFFSET_CAPTURE, $start) && $matches[0][1] == $start) {
 
-                $datum = $matches[0][0];
+								$datum = $matches[0][0];
 
-                if (is_numeric($datum)) {
-                    if (preg_match('/^0\d+$/', $datum)) {
-                        throw new Zend_Json_Exception("Octal notation not supported by JSON (value: $datum)");
-                    } else {
-                        $val  = intval($datum);
-                        $fVal = floatval($datum);
-                        $this->_tokenValue = ($val == $fVal ? $val : $fVal);
-                    }
-                } else {
-                    throw new Zend_Json_Exception("Illegal number format: $datum");
-                }
+								if (is_numeric($datum)) {
+										if (preg_match('/^0\d+$/', $datum)) {
+												throw new Zend_Json_Exception("Octal notation not supported by JSON (value: $datum)");
+										} else {
+												$val  = intval($datum);
+												$fVal = floatval($datum);
+												$this->_tokenValue = ($val == $fVal ? $val : $fVal);
+										}
+								} else {
+										throw new Zend_Json_Exception("Illegal number format: $datum");
+								}
 
-                $this->_token = self::DATUM;
-                $this->_offset = $start + strlen($datum);
-            }
-        } else {
-            throw new Zend_Json_Exception("Illegal Token at pos $i: $chr\nContext:\n--------------------------------------------------" . substr($str, $i) . "\n--------------------------------------------------");
-        }
+								$this->_token = self::DATUM;
+								$this->_offset = $start + strlen($datum);
+						}
+				} else {
+						throw new Zend_Json_Exception("Illegal Token at pos $i: $chr\nContext:\n--------------------------------------------------" . substr($str, $i) . "\n--------------------------------------------------");
+				}
 
-        return($this->_token);
-    }
-    
-    /**
-     * Handle a Unicode byte; local to Enano.
-     * @param string 4 character byte sequence
-     * @return string
-     */
-    
-    protected function decode_unicode_byte($byte)
-    {
-      if ( strlen($byte) != 4 )
-        throw new Zend_Json_Exception("Invalid Unicode sequence \\u$byte");
-        
-      $value = hexdec($byte);
+				return($this->_token);
+		}
+		
+		/**
+ 		* Handle a Unicode byte; local to Enano.
+ 		* @param string 4 character byte sequence
+ 		* @return string
+ 		*/
+		
+		protected function decode_unicode_byte($byte)
+		{
+			if ( strlen($byte) != 4 )
+				throw new Zend_Json_Exception("Invalid Unicode sequence \\u$byte");
+				
+			$value = hexdec($byte);
 
-      if ($value < 0x0080)
-      {
-        // 1 byte: 0xxxxxxx
-        $character = chr($value);
-      }
-      else if ($value < 0x0800)
-      {
-        // 2 bytes: 110xxxxx 10xxxxxx
-        $character =
-            chr((($value & 0x07c0) >> 6) | 0xc0)
-          . chr(($value & 0x3f) | 0x80);
-      }
-      else
-      {
-        // 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx
-        $character =
-            chr((($value & 0xf000) >> 12) | 0xe0)
-          . chr((($value & 0x0fc0) >> 6) | 0x80)
-          . chr(($value & 0x3f) | 0x80);
-      }
-      
-      return $character;
-    }
+			if ($value < 0x0080)
+			{
+				// 1 byte: 0xxxxxxx
+				$character = chr($value);
+			}
+			else if ($value < 0x0800)
+			{
+				// 2 bytes: 110xxxxx 10xxxxxx
+				$character =
+						chr((($value & 0x07c0) >> 6) | 0xc0)
+					. chr(($value & 0x3f) | 0x80);
+			}
+			else
+			{
+				// 3 bytes: 1110xxxx 10xxxxxx 10xxxxxx
+				$character =
+						chr((($value & 0xf000) >> 12) | 0xe0)
+					. chr((($value & 0x0fc0) >> 6) | 0x80)
+					. chr(($value & 0x3f) | 0x80);
+			}
+			
+			return $character;
+		}
 }
 
 /**
@@ -995,62 +995,62 @@
  */
 class Zend_Json
 {
-    /**
-     * How objects should be encoded -- arrays or as StdClass. TYPE_ARRAY is 1
-     * so that it is a boolean true value, allowing it to be used with
-     * ext/json's functions.
-     */
-    const TYPE_ARRAY  = 1;
-    const TYPE_OBJECT = 0;
+		/**
+ 		* How objects should be encoded -- arrays or as StdClass. TYPE_ARRAY is 1
+ 		* so that it is a boolean true value, allowing it to be used with
+ 		* ext/json's functions.
+ 		*/
+		const TYPE_ARRAY  = 1;
+		const TYPE_OBJECT = 0;
 
-    /**
-     * @var bool
-     */
-    public static $useBuiltinEncoderDecoder = true;
+		/**
+ 		* @var bool
+ 		*/
+		public static $useBuiltinEncoderDecoder = true;
 
-    /**
-     * Decodes the given $encodedValue string which is
-     * encoded in the JSON format
-     *
-     * Uses ext/json's json_decode if available.
-     *
-     * @param string $encodedValue Encoded in JSON format
-     * @param int $objectDecodeType Optional; flag indicating how to decode
-     * objects. See {@link ZJsonDecoder::decode()} for details.
-     * @return mixed
-     */
-    public static function decode($encodedValue, $objectDecodeType = Zend_Json::TYPE_ARRAY)
-    {
-        if (function_exists('json_decode') && self::$useBuiltinEncoderDecoder !== true) {
-            return json_decode($encodedValue, $objectDecodeType);
-        }
+		/**
+ 		* Decodes the given $encodedValue string which is
+ 		* encoded in the JSON format
+ 		*
+ 		* Uses ext/json's json_decode if available.
+ 		*
+ 		* @param string $encodedValue Encoded in JSON format
+ 		* @param int $objectDecodeType Optional; flag indicating how to decode
+ 		* objects. See {@link ZJsonDecoder::decode()} for details.
+ 		* @return mixed
+ 		*/
+		public static function decode($encodedValue, $objectDecodeType = Zend_Json::TYPE_ARRAY)
+		{
+				if (function_exists('json_decode') && self::$useBuiltinEncoderDecoder !== true) {
+						return json_decode($encodedValue, $objectDecodeType);
+				}
 
-        return Zend_Json_Decoder::decode($encodedValue, $objectDecodeType);
-    }
+				return Zend_Json_Decoder::decode($encodedValue, $objectDecodeType);
+		}
 
 
-    /**
-     * Encode the mixed $valueToEncode into the JSON format
-     *
-     * Encodes using ext/json's json_encode() if available.
-     *
-     * NOTE: Object should not contain cycles; the JSON format
-     * does not allow object reference.
-     *
-     * NOTE: Only public variables will be encoded
-     *
-     * @param mixed $valueToEncode
-     * @param boolean $cycleCheck Optional; whether or not to check for object recursion; off by default
-     * @return string JSON encoded object
-     */
-    public static function encode($valueToEncode, $cycleCheck = false)
-    {
-        if (function_exists('json_encode') && self::$useBuiltinEncoderDecoder !== true) {
-            return json_encode($valueToEncode);
-        }
+		/**
+ 		* Encode the mixed $valueToEncode into the JSON format
+ 		*
+ 		* Encodes using ext/json's json_encode() if available.
+ 		*
+ 		* NOTE: Object should not contain cycles; the JSON format
+ 		* does not allow object reference.
+ 		*
+ 		* NOTE: Only public variables will be encoded
+ 		*
+ 		* @param mixed $valueToEncode
+ 		* @param boolean $cycleCheck Optional; whether or not to check for object recursion; off by default
+ 		* @return string JSON encoded object
+ 		*/
+		public static function encode($valueToEncode, $cycleCheck = false)
+		{
+				if (function_exists('json_encode') && self::$useBuiltinEncoderDecoder !== true) {
+						return json_encode($valueToEncode);
+				}
 
-        return Zend_Json_Encoder::encode($valueToEncode, $cycleCheck);
-    }
+				return Zend_Json_Encoder::encode($valueToEncode, $cycleCheck);
+		}
 }
 
 ?>
--- a/includes/lang.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/lang.php	Sun Mar 28 23:10:46 2010 -0400
@@ -21,766 +21,766 @@
 
 class Language
 {
-  
-  /**
-   * The numerical ID of the loaded language.
-   * @var int
-   */
-  
-  var $lang_id;
-  
-  /**
-   * The ISO-639-3 code for the loaded language. This should be grabbed directly from the database.
-   * @var string
-   */
-  
-  var $lang_code;
+	
+	/**
+ 	* The numerical ID of the loaded language.
+ 	* @var int
+ 	*/
+	
+	var $lang_id;
+	
+	/**
+ 	* The ISO-639-3 code for the loaded language. This should be grabbed directly from the database.
+ 	* @var string
+ 	*/
+	
+	var $lang_code;
 
-  /**
-   * Used to track when a language was last changed, to allow browsers to cache language data
-   * @var int
-   */
-  
-  var $lang_timestamp;
-  
-  /**
-   * Will be an object that holds an instance of the class configured with the site's default language. Only instanciated when needed.
-   * @var object
-   */
-  
-  var $default;
-  
-  /**
-   * The list of loaded strings.
-   * @var array
-   * @access private
-   */
-  
-  var $strings = array();
-  
-  /**
-   * Switch for debug mode. If true, will show an asterisk after localized strings. This
-   * can be useful if you're localizing a component and need to see what's already done.
-   * @var bool
-   */
-  
-  var $debug = false;
-  
-  /**
-   * List of available filters to pass variables through.
-   * @var array
-   * @access private
-   */
-  
-  protected $filters = array();
-  
-  /**
-   * Constructor.
-   * @param int|string Language ID or code to load.
-   */
-  
-  function __construct($lang)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( defined('IN_ENANO_INSTALL') && ( !defined('ENANO_CONFIG_FETCHED') || ( defined('IN_ENANO_UPGRADE') && !defined('IN_ENANO_UPGRADE_POST') ) ) )
-    {
-      // special case for the Enano installer: it will load its own strings from a JSON file and just use this API for fetching
-      // and templatizing them.
-      // 1.1.4 fix: this was still being called after main API startup from installer payload
-      $this->lang_id   = 1;
-      $this->lang_code = $lang;
-      return true;
-    }
-    if ( is_string($lang) )
-    {
-      $sql_col = 'lang_code=\'' . $db->escape($lang) . '\'';
-    }
-    else if ( is_int($lang) )
-    {
-      $sql_col = 'lang_id=' . $lang . '';
-    }
-    else
-    {
-      $db->_die('lang.php - attempting to pass invalid value to constructor');
-    }
-    
-    $lang_default = ( $x = getConfig('default_language') ) ? intval($x) : '0';
-    
-    $q = $db->sql_query("SELECT lang_id, lang_code, last_changed, ( lang_id = $lang_default ) AS is_default FROM " . table_prefix . "language WHERE $sql_col OR lang_id = $lang_default ORDER BY is_default ASC LIMIT 1;");
-    
-    if ( !$q )
-      $db->_die('lang.php - main select query');
-    
-    if ( $db->numrows() < 1 )
-      $db->_die('lang.php - There are no languages installed');
-    
-    $row = $db->fetchrow();
-    
-    $this->lang_id   = intval( $row['lang_id'] );
-    $this->lang_code = $row['lang_code'];
-    $this->lang_timestamp = $row['last_changed'];
-    
-    $this->register_filter('htmlsafe', 'htmlspecialchars');
-    $this->register_filter('urlencode', 'urlencode');
-    $this->register_filter('rawurlencode', 'rawurlencode');
-    
-    $code = $plugins->setHook('lang_init');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-  }
-  
-  /**
-   * Fetches language strings from the database, or a cache file if it's available.
-   * @param bool If true (default), allows the cache to be used.
-   */
-  
-  function fetch($allow_cache = true)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Attempt to load the strings from a cache file
-    $loaded = false;
-    
-    if ( $allow_cache )
-    {
-      // Load the cache manager
-      global $cache;
-      
-      if ( $cached = $cache->fetch("lang_{$this->lang_id}") )
-      {
-        $this->merge($cached);
-        $loaded = true;
-      }
-    }
-    if ( !$loaded )
-    {
-      // No cache file - select and retrieve from the database
-      $q = $db->sql_unbuffered_query("SELECT string_category, string_name, string_content FROM " . table_prefix . "language_strings WHERE lang_id = {$this->lang_id};");
-      if ( !$q )
-        $db->_die('lang.php - selecting language string data');
-      if ( $row = $db->fetchrow() )
-      {
-        $strings = array();
-        do
-        {
-          $cat =& $row['string_category'];
-          if ( !is_array(@$strings[$cat]) )
-          {
-            $strings[$cat] = array();
-          }
-          $strings[$cat][ $row['string_name'] ] = $row['string_content'];
-        }
-        while ( $row = $db->fetchrow() );
-        // all done fetching
-        $this->merge($strings);
-        $this->regen_caches(false);
-      }
-      /*
-      else
-      {
-        if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
-          $db->_die('lang.php - No strings for language ' . $this->lang_code);
-      }
-      */
-    }
-  }
-  
-  /**
-   * Loads a file from the disk cache (treated as PHP) and merges it into RAM.
-   * @param string File to load
-   */
-  
-  function load_cache_file($file)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !file_exists($file) )
-      $db->_die('lang.php - requested cache file doesn\'t exist');
-    
-    @include($file);
-        
-    if ( !isset($lang_cache) || ( isset($lang_cache) && !is_array($lang_cache) ) )
-      $db->_die('lang.php - the cache file is invalid (didn\'t set $lang_cache as an array)');
-    
-    $this->merge($lang_cache);
-  }
-  
-  /**
-   * Loads a JSON language file and parses the strings into RAM. Will use the cache if possible, but stays far away from the database,
-   * which we assume doesn't exist yet.
-   */
-  
-  function load_file($file)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !file_exists($file) )
-    {
-      if ( defined('IN_ENANO_INSTALL') )
-      {
-        die('lang.php - requested JSON file (' . htmlspecialchars($file) . ') doesn\'t exist');
-      }
-      else
-      {
-        $db->_die('lang.php - requested JSON file doesn\'t exist');
-      }
-    }
-    
-    $contents = trim(@file_get_contents($file));
-    if ( empty($contents) )
-      $db->_die('lang.php - empty language file...');
-    
-    // Trim off all text before and after the starting and ending braces
-    $contents = preg_replace('/^([^{]+)\{/', '{', $contents);
-    $contents = preg_replace('/\}([^}]+)$/', '}', $contents);
-    $contents = trim($contents);
-    
-    if ( empty($contents) )
-      $db->_die('lang.php - no meat to the language file...');
-    
-    $checksum = md5($contents);
-    if ( file_exists("./cache/lang_json_{$checksum}.php") )
-    {
-      $this->load_cache_file("./cache/lang_json_{$checksum}.php");
-    }
-    else
-    {
-      // Correct syntax to be nice to the json parser
-    
-      // eliminate comments
-      $contents = preg_replace(array(
-              // eliminate single line comments in '// ...' form
-              '#^\s*//(.+)$#m',
-              // eliminate multi-line comments in '/* ... */' form, at start of string
-              '#^\s*/\*(.+)\*/#Us',
-              // eliminate multi-line comments in '/* ... */' form, at end of string
-              '#/\*(.+)\*/\s*$#Us'
-            ), '', $contents);
-      
-      $contents = preg_replace('/([,\{\[])([\s]*?)([a-z0-9_]+)([\s]*?):/', '\\1\\2"\\3" :', $contents);
-      
-      try
-      {
-        $langdata = enano_json_decode($contents);
-      }
-      catch(Zend_Json_Exception $e)
-      {
-        $db->_die('lang.php - Exception caught by JSON parser</p><pre>' . htmlspecialchars(print_r($e, true)) . '</pre><p>');
-        exit;
-      }
-    
-      if ( !is_array($langdata) )
-        $db->_die('lang.php - invalid language file');
-      
-      if ( !isset($langdata['categories']) || !isset($langdata['strings']) )
-        $db->_die('lang.php - language file does not contain the proper items');
-      
-      $this->merge($langdata['strings']);
-      
-      $lang_file = "./cache/lang_json_{$checksum}.php";
-      
-      $handle = @fopen($lang_file, 'w');
-      if ( !$handle )
-        // Couldn't open the file. Silently fail and let the strings come from RAM.
-        return false;
-        
-      // The file's open, that means we should be good.
-      fwrite($handle, '<?php
+	/**
+ 	* Used to track when a language was last changed, to allow browsers to cache language data
+ 	* @var int
+ 	*/
+	
+	var $lang_timestamp;
+	
+	/**
+ 	* Will be an object that holds an instance of the class configured with the site's default language. Only instanciated when needed.
+ 	* @var object
+ 	*/
+	
+	var $default;
+	
+	/**
+ 	* The list of loaded strings.
+ 	* @var array
+ 	* @access private
+ 	*/
+	
+	var $strings = array();
+	
+	/**
+ 	* Switch for debug mode. If true, will show an asterisk after localized strings. This
+ 	* can be useful if you're localizing a component and need to see what's already done.
+ 	* @var bool
+ 	*/
+	
+	var $debug = false;
+	
+	/**
+ 	* List of available filters to pass variables through.
+ 	* @var array
+ 	* @access private
+ 	*/
+	
+	protected $filters = array();
+	
+	/**
+ 	* Constructor.
+ 	* @param int|string Language ID or code to load.
+ 	*/
+	
+	function __construct($lang)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( defined('IN_ENANO_INSTALL') && ( !defined('ENANO_CONFIG_FETCHED') || ( defined('IN_ENANO_UPGRADE') && !defined('IN_ENANO_UPGRADE_POST') ) ) )
+		{
+			// special case for the Enano installer: it will load its own strings from a JSON file and just use this API for fetching
+			// and templatizing them.
+			// 1.1.4 fix: this was still being called after main API startup from installer payload
+			$this->lang_id   = 1;
+			$this->lang_code = $lang;
+			return true;
+		}
+		if ( is_string($lang) )
+		{
+			$sql_col = 'lang_code=\'' . $db->escape($lang) . '\'';
+		}
+		else if ( is_int($lang) )
+		{
+			$sql_col = 'lang_id=' . $lang . '';
+		}
+		else
+		{
+			$db->_die('lang.php - attempting to pass invalid value to constructor');
+		}
+		
+		$lang_default = ( $x = getConfig('default_language') ) ? intval($x) : '0';
+		
+		$q = $db->sql_query("SELECT lang_id, lang_code, last_changed, ( lang_id = $lang_default ) AS is_default FROM " . table_prefix . "language WHERE $sql_col OR lang_id = $lang_default ORDER BY is_default ASC LIMIT 1;");
+		
+		if ( !$q )
+			$db->_die('lang.php - main select query');
+		
+		if ( $db->numrows() < 1 )
+			$db->_die('lang.php - There are no languages installed');
+		
+		$row = $db->fetchrow();
+		
+		$this->lang_id   = intval( $row['lang_id'] );
+		$this->lang_code = $row['lang_code'];
+		$this->lang_timestamp = $row['last_changed'];
+		
+		$this->register_filter('htmlsafe', 'htmlspecialchars');
+		$this->register_filter('urlencode', 'urlencode');
+		$this->register_filter('rawurlencode', 'rawurlencode');
+		
+		$code = $plugins->setHook('lang_init');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+	}
+	
+	/**
+ 	* Fetches language strings from the database, or a cache file if it's available.
+ 	* @param bool If true (default), allows the cache to be used.
+ 	*/
+	
+	function fetch($allow_cache = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Attempt to load the strings from a cache file
+		$loaded = false;
+		
+		if ( $allow_cache )
+		{
+			// Load the cache manager
+			global $cache;
+			
+			if ( $cached = $cache->fetch("lang_{$this->lang_id}") )
+			{
+				$this->merge($cached);
+				$loaded = true;
+			}
+		}
+		if ( !$loaded )
+		{
+			// No cache file - select and retrieve from the database
+			$q = $db->sql_unbuffered_query("SELECT string_category, string_name, string_content FROM " . table_prefix . "language_strings WHERE lang_id = {$this->lang_id};");
+			if ( !$q )
+				$db->_die('lang.php - selecting language string data');
+			if ( $row = $db->fetchrow() )
+			{
+				$strings = array();
+				do
+				{
+					$cat =& $row['string_category'];
+					if ( !is_array(@$strings[$cat]) )
+					{
+						$strings[$cat] = array();
+					}
+					$strings[$cat][ $row['string_name'] ] = $row['string_content'];
+				}
+				while ( $row = $db->fetchrow() );
+				// all done fetching
+				$this->merge($strings);
+				$this->regen_caches(false);
+			}
+			/*
+			else
+			{
+				if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
+					$db->_die('lang.php - No strings for language ' . $this->lang_code);
+			}
+			*/
+		}
+	}
+	
+	/**
+ 	* Loads a file from the disk cache (treated as PHP) and merges it into RAM.
+ 	* @param string File to load
+ 	*/
+	
+	function load_cache_file($file)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !file_exists($file) )
+			$db->_die('lang.php - requested cache file doesn\'t exist');
+		
+		@include($file);
+				
+		if ( !isset($lang_cache) || ( isset($lang_cache) && !is_array($lang_cache) ) )
+			$db->_die('lang.php - the cache file is invalid (didn\'t set $lang_cache as an array)');
+		
+		$this->merge($lang_cache);
+	}
+	
+	/**
+ 	* Loads a JSON language file and parses the strings into RAM. Will use the cache if possible, but stays far away from the database,
+ 	* which we assume doesn't exist yet.
+ 	*/
+	
+	function load_file($file)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !file_exists($file) )
+		{
+			if ( defined('IN_ENANO_INSTALL') )
+			{
+				die('lang.php - requested JSON file (' . htmlspecialchars($file) . ') doesn\'t exist');
+			}
+			else
+			{
+				$db->_die('lang.php - requested JSON file doesn\'t exist');
+			}
+		}
+		
+		$contents = trim(@file_get_contents($file));
+		if ( empty($contents) )
+			$db->_die('lang.php - empty language file...');
+		
+		// Trim off all text before and after the starting and ending braces
+		$contents = preg_replace('/^([^{]+)\{/', '{', $contents);
+		$contents = preg_replace('/\}([^}]+)$/', '}', $contents);
+		$contents = trim($contents);
+		
+		if ( empty($contents) )
+			$db->_die('lang.php - no meat to the language file...');
+		
+		$checksum = md5($contents);
+		if ( file_exists("./cache/lang_json_{$checksum}.php") )
+		{
+			$this->load_cache_file("./cache/lang_json_{$checksum}.php");
+		}
+		else
+		{
+			// Correct syntax to be nice to the json parser
+		
+			// eliminate comments
+			$contents = preg_replace(array(
+							// eliminate single line comments in '// ...' form
+							'#^\s*//(.+)$#m',
+							// eliminate multi-line comments in '/* ... */' form, at start of string
+							'#^\s*/\*(.+)\*/#Us',
+							// eliminate multi-line comments in '/* ... */' form, at end of string
+							'#/\*(.+)\*/\s*$#Us'
+						), '', $contents);
+			
+			$contents = preg_replace('/([,\{\[])([\s]*?)([a-z0-9_]+)([\s]*?):/', '\\1\\2"\\3" :', $contents);
+			
+			try
+			{
+				$langdata = enano_json_decode($contents);
+			}
+			catch(Zend_Json_Exception $e)
+			{
+				$db->_die('lang.php - Exception caught by JSON parser</p><pre>' . htmlspecialchars(print_r($e, true)) . '</pre><p>');
+				exit;
+			}
+		
+			if ( !is_array($langdata) )
+				$db->_die('lang.php - invalid language file');
+			
+			if ( !isset($langdata['categories']) || !isset($langdata['strings']) )
+				$db->_die('lang.php - language file does not contain the proper items');
+			
+			$this->merge($langdata['strings']);
+			
+			$lang_file = "./cache/lang_json_{$checksum}.php";
+			
+			$handle = @fopen($lang_file, 'w');
+			if ( !$handle )
+				// Couldn't open the file. Silently fail and let the strings come from RAM.
+				return false;
+				
+			// The file's open, that means we should be good.
+			fwrite($handle, '<?php
 // This file was generated automatically by Enano. You should not edit this file because any changes you make
 // to it will not be visible in the ACP and all changes will be lost upon any changes to strings in the admin panel.
 
 $lang_cache = ');
-      
-      $exported = $this->var_export_string($this->strings);
-      if ( empty($exported) )
-        // Ehh, that's not good
-        $db->_die('lang.php - load_file(): var_export_string() failed');
-      
-      fwrite($handle, $exported . '; ?>');
-      
-      // Clean up
-      unset($exported, $langdata);
-      
-      // Done =)
-      fclose($handle);
-    }
-  }
-  
-  /**
-   * Merges a standard language assoc array ($arr[cat][stringid]) with the master in RAM.
-   * @param array
-   */
-  
-  function merge($strings)
-  {
-    // This is stupidly simple.
-    foreach ( $strings as $cat_id => $contents )
-    {
-      if ( !isset($this->strings[$cat_id]) || ( isset($this->strings[$cat_id]) && !is_array($this->strings[$cat_id]) ) )
-        $this->strings[$cat_id] = array();
-      foreach ( $contents as $string_id => $string )
-      {
-        $this->strings[$cat_id][$string_id] = $string;
-      }
-    }
-  }
-  
-  /**
-   * Imports a JSON-format language file into the database and merges with current strings.
-   * @param string Path to the JSON file to load
-   * @param bool If true, only imports new strings and skips those that already exist. Defaults to false (import all strings)
-   * @param bool Enable debugging output, makes the process over CLI more interesting
-   */
-  
-  function import($file, $skip_existing = false, $debug = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !file_exists($file) )
-      $db->_die('lang.php - can\'t import language file: string file doesn\'t exist');
-    
-    if ( $this->lang_id == 0 )
-      $db->_die('lang.php - BUG: trying to perform import when $lang->lang_id == 0');
-    
-    if ( $debug )
-      $br = ( isset($_SERVER['REQUEST_URI']) ) ? '<br />' : '';
-    
-    if ( $debug )
-      echo "Importing file: $file$br\n  Checking file...$br\n";
-    
-    $contents = trim(@file_get_contents($file));
-    
-    if ( empty($contents) )
-      $db->_die('lang.php - can\'t load the contents of the language file');
-    
-    if ( $debug )
-      echo "  Cleaning up JSON$br\n";
-    
-    // Trim off all text before and after the starting and ending braces
-    $contents = preg_replace('/^([^{]+)\{/', '{', $contents);
-    $contents = preg_replace('/\}([^}]+)$/', '}', $contents);
-    
-    // Correct syntax to be nice to the json parser
-    $contents = enano_clean_json($contents);
-    
-    if ( $debug )
-      echo "  Decoding JSON stream$br\n";
-    
-    try
-    {
-      $langdata = enano_json_decode($contents);
-    }
-    catch(Zend_Json_Exception $e)
-    {
-      $db->_die('lang.php - Exception caught by JSON parser</p><pre>' . htmlspecialchars(print_r($e, true)) . '</pre><p>');
-      exit;
-    }
-    
-    if ( !is_array($langdata) )
-    {
-      $db->_die('lang.php - invalid or non-well-formed language file');
-    }
-    
-    if ( $debug )
-      echo "  Starting string import$br\n";
-    
-    return $this->import_array($langdata, $skip_existing, $debug);
-  }
-  
-  /**
-   * Imports a JSON-format language file into the database and merges with current strings.
-   * @param string Path to plugin file
-   */
-  
-  function import_plugin($file)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !file_exists($file) )
-      $db->_die('lang.php - can\'t import language file: string file doesn\'t exist');
-    
-    if ( $this->lang_id == 0 )
-      $db->_die('lang.php - BUG: trying to perform import when $lang->lang_id == 0');
-    
-    $block = pluginLoader::parse_plugin_blocks($file, 'language');
-    if ( !is_array($block) )
-      return false;
-    if ( !isset($block[0]) )
-      return false;
-    
-    $contents =& $block[0]['value'];
-    
-    // Trim off all text before and after the starting and ending braces
-    $contents = enano_trim_json($contents);
-    
-    // Correct syntax to be nice to the json parser
-    $contents = enano_clean_json($contents);
-    
-    try
-    {
-      $langdata = enano_json_decode($contents);
-    }
-    catch(Zend_Json_Exception $e)
-    {
-      $db->_die('lang.php - Exception caught by JSON parser</p><pre>' . htmlspecialchars(print_r($e, true)) . '</pre><p>');
-      exit;
-    }
-    
-    if ( !is_array($langdata) )
-    {
-      $db->_die('lang.php - invalid or non-well-formed language file');
-    }
-    
-    // Does the plugin support the current language?
-    if ( isset($langdata[$this->lang_code]) )
-    {
-      // Yes, import that
-      return $this->import_array($langdata[$this->lang_code]);
-    }
-    
-    // Just import the first language we run across.
-    $supported_langs = array_keys($langdata);
-    
-    if ( !isset($supported_langs[0]) )
-    {
-      $db->_die('lang.php - plugin has an invalid or corrupt language block');
-    }
-    
-    $first_lang = $supported_langs[0];
-    
-    return $this->import_array($langdata[$first_lang], false, true);
-  }
-  
-  /**
-   * Performs the actual import of string data.
-   * @param array Parsed JSON object, should be in the form of an array
-   * @param bool If true, only imports new strings and skips those that already exist. Defaults to false.
-   * @param bool Enable debugging output
-   * @access private
-   */
-  
-  protected function import_array($langdata, $skip_existing = false, $debug = false) 
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !isset($langdata['categories']) || !isset($langdata['strings']) )
-      $db->_die('lang.php - language file does not contain the proper items');
-    
-    if ( $debug )
-      $br = ( isset($_SERVER['REQUEST_URI']) ) ? '<br />' : '';
-    
-    $insert_list = array();
-    $delete_list = array();
-    
-    foreach ( $langdata['categories'] as $category )
-    {
-      if ( isset($langdata['strings'][$category]) )
-      {
-        if ( $debug )
-        {
-          $desc = ( isset($langdata['strings']['meta'][$category]) ) ? $langdata['strings']['meta'][$category] : $this->get("meta_$category");
-          echo "  Indexing category: $category ({$desc})$br\n";
-        }
-        foreach ( $langdata['strings'][$category] as $string_name => $string_value )
-        {
-          // should we skip this string?
-          if ( isset($this->strings[$category]) && $skip_existing )
-          {
-            if ( isset($this->strings[$category][$string_name]) )
-            {
-              // already exists, skip
-              if ( $debug )
-                echo "    Skipping string (already exists): {$category}_{$string_name}$br\n";
-              continue;
-            }
-          }
-          $string_name = $db->escape($string_name);
-          $string_value = $db->escape($string_value);
-          $category_name = $db->escape($category);
-          $insert_list[] = "({$this->lang_id}, '$category_name', '$string_name', '$string_value')";
-          $delete_list[] = "( lang_id = {$this->lang_id} AND string_category = '$category_name' AND string_name = '$string_name' )";
-        }
-      }
-    }
-    
-    if ( $debug )
-    {
-      echo "  Running deletion of old strings...";
-      $start = microtime_float();
-    }
-    $delete_list = implode(" OR\n  ", $delete_list);
-    
-    if ( !empty($delete_list) )
-    {
-      $sql = "DELETE FROM " . table_prefix . "language_strings WHERE $delete_list;";
-      
-      // Free some memory
-      unset($delete_list);
-      
-      // Run the query
-      $q = $db->sql_query($sql);
-      if ( !$q )
-        $db->_die('lang.php - couldn\'t kill off them old strings');
-    }
-    
-    if ( $debug )
-    {
-      $time = round(microtime_float() - $start, 5);
-      echo "({$time}s)$br\n";
-    }
-    
-    if ( !empty($insert_list) )
-    {
-      if ( $debug )
-      {
-        echo "  Inserting strings...";
-        $start = microtime_float();
-      }
-      $insert_list = implode(",\n  ", $insert_list);
-      $sql = "INSERT INTO " . table_prefix . "language_strings(lang_id, string_category, string_name, string_content) VALUES\n  $insert_list;";
-      
-      // Free some memory
-      unset($insert_list);
-      
-      // Run the query
-      $q = $db->sql_query($sql);
-      if ( !$q )
-        $db->_die('lang.php - couldn\'t insert strings in import()');
-      
-      if ( $debug )
-      {
-        $time = round(microtime_float() - $start, 5);
-        echo "({$time}s)$br\n";
-      }
-    }
-    
-    // YAY! done!
-    // This will regenerate the cache file if possible.
-    if ( $debug )
-      echo "  Regenerating cache file$br\n";
-    $this->regen_caches();
-    
-    return true;
-  }
-  
-  /**
-   * Refetches the strings and writes out the cache file.
-   */
-  
-  function regen_caches($refetch = true)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Refresh the strings in RAM to the latest copies in the DB
-    if ( $refetch )
-      $this->fetch(false);
-    
-    // Load the cache manager
-    global $cache;
-    
-    // Store it
-    $cache->store("lang_{$this->lang_id}", $this->strings, -1);
-    
-    // Update timestamp in database
-    $q = $db->sql_query('UPDATE ' . table_prefix . 'language SET last_changed = ' . time() . ' WHERE lang_id = ' . $this->lang_id . ';');
-    if ( !$q )
-      $db->_die('lang.php - updating timestamp on language');
-    
-    return true;
-  }
-  
-  /**
-   * Calls var_export() on whatever, and returns the function's output.
-   * @param mixed Whatever you want var_exported. Usually an array.
-   * @return string
-   */
-  
-  static function var_export_string($val)
-  {
-    ob_start();
-    var_export($val);
-    $contents = ob_get_contents();
-    ob_end_clean();
-    return $contents;
-  }
-  
-  /**
-   * Registers a filter, a function that strings can be passed through to change the string somehow (e.g. htmlspecialchars)
-   * @param string Filter name. Lowercase alphanumeric (htmlsafe)
-   * @param callback Function to call.
-   * @return bool True on success, false if some error occurred
-   */
-  
-  public function register_filter($filter_name, $filter_function)
-  {
-    if ( !is_string($filter_function) && !is_array($filter_function) )
-    {
-      return false;
-    }
-    if ( ( is_string($filter_function) && !function_exists($filter_function) ) || ( is_array($filter_function) && !method_exists(@$filter_function[0], @$filter_function[1]) ) )
-    {
-      return false;
-    }
-    if ( !preg_match('/^[a-z0-9_]+$/', $filter_name) )
-    {
-      return false;
-    }
-    $this->filters[$filter_name] = $filter_function;
-    return true;
-  }
-  
-  /**
-   * Fetches a language string from the cache in RAM. If it isn't there, it will call fetch() again and then try. If it still can't find it, it will ask for the string
-   * in the default language. If even then the string can't be found, this function will return what was passed to it.
-   *
-   * This will also templatize strings. If a string contains variables in the format %foo%, you may specify the second parameter as an associative array in the format
-   * of 'foo' => 'foo substitute'.
-   *
-   * @param string ID of the string to fetch. This will always be in the format of category_stringid.
-   * @param array Optional. Associative array of substitutions.
-   * @return string
-   */
-  
-  function get($string_id, $substitutions = false)
-  {
-    if ( !is_array($substitutions) )
-      $substitutions = array();
-    // if this isn't a valid language string ID, just return the string unprocessed.
-    if ( !preg_match('/^([a-z0-9]+)((_[a-z0-9]+)+)$/', $string_id) )
-      return $string_id;
-    return $this->substitute($this->get_uncensored($string_id), $substitutions);
-  }
-  
-  /**
-   * The same as get(), but does not perform any substitution or filtering. Used in get() (of course) and in the admin panel, where
-   * strings are updated only if they were changed.
-   *
-   * @param string ID of the string to fetch. This will always be in the format of category_stringid.
-   * @param array Optional. Associative array of substitutions.
-   * @return string
-   */
-  
-  function get_uncensored($string_id, $substitutions = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Extract the category and string ID
-    $category = substr($string_id, 0, ( strpos($string_id, '_') ));
-    $string_name = substr($string_id, ( strpos($string_id, '_') + 1 ));
-    $found = false;
-    if ( isset($this->strings[$category]) && isset($this->strings[$category][$string_name]) )
-    {
-      $found = true;
-      $string = $this->strings[$category][$string_name];
-    }
-    if ( !$found )
-    {
-      // Ehh, the string wasn't found. Rerun fetch() and try again.
-      // Or if it's the installer, no use in refetching, so just fail.
-      if ( defined('IN_ENANO_INSTALL') || ( defined('ENANO_INSTALLED') && !@$db->_conn ) )
-      {
-        return $string_id;
-      }
-      $this->fetch();
-      profiler_log('Language(' . $this->lang_code . '): refetched due to missing string: ' . $string_id);
-      if ( isset($this->strings[$category]) && isset($this->strings[$category][$string_name]) )
-      {
-        $found = true;
-        $string = $this->strings[$category][$string_name];
-      }
-      if ( !$found )
-      {
-        // STILL not found. Check the default language.
-        $lang_default = ( $x = getConfig('default_language') ) ? intval($x) : $this->lang_id;
-        if ( $lang_default != $this->lang_id )
-        {
-          if ( !is_object($this->default) )
-            $this->default = new Language($lang_default);
-          return $this->default->get_uncensored($string_id);
-        }
-      }
-    }
-    if ( !$found )
-    {
-      // Alright, it's nowhere. Return the input, grumble grumble...
-      return $string_id;
-    }
-    // Found it!
-    return $string;
-  }
-  
-  /**
-   * Processes substitutions.
-   * @param string
-   * @param array
-   * @return string
-   */
-  
-  function substitute($string, $subs)
-  {
-    preg_match_all('/%this\.([a-z0-9_]+)((?:\|(?:[a-z0-9_]+))*)%/', $string, $matches);
-    if ( count($matches[0]) > 0 )
-    {
-      foreach ( $matches[1] as $i => $string_id )
-      {
-        $result = $this->get($string_id);
-        $string = str_replace($matches[0][$i], $this->process_filters($result, $matches[2][$i]), $string);
-      }
-    }
-    preg_match_all('/%config\.([a-z0-9_]+)((?:\|(?:[a-z0-9_]+))*)%/', $string, $matches);
-    if ( count($matches[0]) > 0 )
-    {
-      foreach ( $matches[1] as $i => $string_id )
-      {
-        $result = getConfig($string_id, '');
-        $string = str_replace($matches[0][$i], $this->process_filters($result, $matches[2][$i]), $string);
-      }
-    }
-    preg_match_all('/%([a-z0-9_]+)((?:\|(?:[a-z0-9_]+))*)%/', $string, $matches);
-    if ( count($matches[0]) > 0 )
-    {
-      foreach ( $matches[1] as $i => $string_id )
-      {
-        if ( isset($subs[$string_id]) )
-        {
-          $string = str_replace($matches[0][$i], $this->process_filters($subs[$string_id], $matches[2][$i]), $string);
-        }
-      }
-    }
-    return ( $this->debug ) ? "$string*" : $string;
-  }
-  
-  /**
-   * Processes filters to a language string.
-   * @param string Unprocessed string
-   * @param string Filter list (format: |filter1|filter2|filter3, initial pipe is important); can also be an array if you so desire
-   * @return string
-   */
-  
-  function process_filters($string, $filters)
-  {
-    if ( !empty($filters) )
-    {
-      $filters = trim($filters, '|');
-      $filters = explode('|', $filters);
-      foreach ( $filters as $filter )
-      {
-        if ( isset($this->filters[$filter]) )
-        {
-          $result = call_user_func($this->filters[$filter], $string);
-          if ( is_string($result) )
-          {
-            $string = $result;
-          }
-        }
-      }
-    }
-    return $string;
-  }
-  
+			
+			$exported = $this->var_export_string($this->strings);
+			if ( empty($exported) )
+				// Ehh, that's not good
+				$db->_die('lang.php - load_file(): var_export_string() failed');
+			
+			fwrite($handle, $exported . '; ?>');
+			
+			// Clean up
+			unset($exported, $langdata);
+			
+			// Done =)
+			fclose($handle);
+		}
+	}
+	
+	/**
+ 	* Merges a standard language assoc array ($arr[cat][stringid]) with the master in RAM.
+ 	* @param array
+ 	*/
+	
+	function merge($strings)
+	{
+		// This is stupidly simple.
+		foreach ( $strings as $cat_id => $contents )
+		{
+			if ( !isset($this->strings[$cat_id]) || ( isset($this->strings[$cat_id]) && !is_array($this->strings[$cat_id]) ) )
+				$this->strings[$cat_id] = array();
+			foreach ( $contents as $string_id => $string )
+			{
+				$this->strings[$cat_id][$string_id] = $string;
+			}
+		}
+	}
+	
+	/**
+ 	* Imports a JSON-format language file into the database and merges with current strings.
+ 	* @param string Path to the JSON file to load
+ 	* @param bool If true, only imports new strings and skips those that already exist. Defaults to false (import all strings)
+ 	* @param bool Enable debugging output, makes the process over CLI more interesting
+ 	*/
+	
+	function import($file, $skip_existing = false, $debug = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !file_exists($file) )
+			$db->_die('lang.php - can\'t import language file: string file doesn\'t exist');
+		
+		if ( $this->lang_id == 0 )
+			$db->_die('lang.php - BUG: trying to perform import when $lang->lang_id == 0');
+		
+		if ( $debug )
+			$br = ( isset($_SERVER['REQUEST_URI']) ) ? '<br />' : '';
+		
+		if ( $debug )
+			echo "Importing file: $file$br\n  Checking file...$br\n";
+		
+		$contents = trim(@file_get_contents($file));
+		
+		if ( empty($contents) )
+			$db->_die('lang.php - can\'t load the contents of the language file');
+		
+		if ( $debug )
+			echo "  Cleaning up JSON$br\n";
+		
+		// Trim off all text before and after the starting and ending braces
+		$contents = preg_replace('/^([^{]+)\{/', '{', $contents);
+		$contents = preg_replace('/\}([^}]+)$/', '}', $contents);
+		
+		// Correct syntax to be nice to the json parser
+		$contents = enano_clean_json($contents);
+		
+		if ( $debug )
+			echo "  Decoding JSON stream$br\n";
+		
+		try
+		{
+			$langdata = enano_json_decode($contents);
+		}
+		catch(Zend_Json_Exception $e)
+		{
+			$db->_die('lang.php - Exception caught by JSON parser</p><pre>' . htmlspecialchars(print_r($e, true)) . '</pre><p>');
+			exit;
+		}
+		
+		if ( !is_array($langdata) )
+		{
+			$db->_die('lang.php - invalid or non-well-formed language file');
+		}
+		
+		if ( $debug )
+			echo "  Starting string import$br\n";
+		
+		return $this->import_array($langdata, $skip_existing, $debug);
+	}
+	
+	/**
+ 	* Imports a JSON-format language file into the database and merges with current strings.
+ 	* @param string Path to plugin file
+ 	*/
+	
+	function import_plugin($file)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !file_exists($file) )
+			$db->_die('lang.php - can\'t import language file: string file doesn\'t exist');
+		
+		if ( $this->lang_id == 0 )
+			$db->_die('lang.php - BUG: trying to perform import when $lang->lang_id == 0');
+		
+		$block = pluginLoader::parse_plugin_blocks($file, 'language');
+		if ( !is_array($block) )
+			return false;
+		if ( !isset($block[0]) )
+			return false;
+		
+		$contents =& $block[0]['value'];
+		
+		// Trim off all text before and after the starting and ending braces
+		$contents = enano_trim_json($contents);
+		
+		// Correct syntax to be nice to the json parser
+		$contents = enano_clean_json($contents);
+		
+		try
+		{
+			$langdata = enano_json_decode($contents);
+		}
+		catch(Zend_Json_Exception $e)
+		{
+			$db->_die('lang.php - Exception caught by JSON parser</p><pre>' . htmlspecialchars(print_r($e, true)) . '</pre><p>');
+			exit;
+		}
+		
+		if ( !is_array($langdata) )
+		{
+			$db->_die('lang.php - invalid or non-well-formed language file');
+		}
+		
+		// Does the plugin support the current language?
+		if ( isset($langdata[$this->lang_code]) )
+		{
+			// Yes, import that
+			return $this->import_array($langdata[$this->lang_code]);
+		}
+		
+		// Just import the first language we run across.
+		$supported_langs = array_keys($langdata);
+		
+		if ( !isset($supported_langs[0]) )
+		{
+			$db->_die('lang.php - plugin has an invalid or corrupt language block');
+		}
+		
+		$first_lang = $supported_langs[0];
+		
+		return $this->import_array($langdata[$first_lang], false, true);
+	}
+	
+	/**
+ 	* Performs the actual import of string data.
+ 	* @param array Parsed JSON object, should be in the form of an array
+ 	* @param bool If true, only imports new strings and skips those that already exist. Defaults to false.
+ 	* @param bool Enable debugging output
+ 	* @access private
+ 	*/
+	
+	protected function import_array($langdata, $skip_existing = false, $debug = false) 
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !isset($langdata['categories']) || !isset($langdata['strings']) )
+			$db->_die('lang.php - language file does not contain the proper items');
+		
+		if ( $debug )
+			$br = ( isset($_SERVER['REQUEST_URI']) ) ? '<br />' : '';
+		
+		$insert_list = array();
+		$delete_list = array();
+		
+		foreach ( $langdata['categories'] as $category )
+		{
+			if ( isset($langdata['strings'][$category]) )
+			{
+				if ( $debug )
+				{
+					$desc = ( isset($langdata['strings']['meta'][$category]) ) ? $langdata['strings']['meta'][$category] : $this->get("meta_$category");
+					echo "  Indexing category: $category ({$desc})$br\n";
+				}
+				foreach ( $langdata['strings'][$category] as $string_name => $string_value )
+				{
+					// should we skip this string?
+					if ( isset($this->strings[$category]) && $skip_existing )
+					{
+						if ( isset($this->strings[$category][$string_name]) )
+						{
+							// already exists, skip
+							if ( $debug )
+								echo "    Skipping string (already exists): {$category}_{$string_name}$br\n";
+							continue;
+						}
+					}
+					$string_name = $db->escape($string_name);
+					$string_value = $db->escape($string_value);
+					$category_name = $db->escape($category);
+					$insert_list[] = "({$this->lang_id}, '$category_name', '$string_name', '$string_value')";
+					$delete_list[] = "( lang_id = {$this->lang_id} AND string_category = '$category_name' AND string_name = '$string_name' )";
+				}
+			}
+		}
+		
+		if ( $debug )
+		{
+			echo "  Running deletion of old strings...";
+			$start = microtime_float();
+		}
+		$delete_list = implode(" OR\n  ", $delete_list);
+		
+		if ( !empty($delete_list) )
+		{
+			$sql = "DELETE FROM " . table_prefix . "language_strings WHERE $delete_list;";
+			
+			// Free some memory
+			unset($delete_list);
+			
+			// Run the query
+			$q = $db->sql_query($sql);
+			if ( !$q )
+				$db->_die('lang.php - couldn\'t kill off them old strings');
+		}
+		
+		if ( $debug )
+		{
+			$time = round(microtime_float() - $start, 5);
+			echo "({$time}s)$br\n";
+		}
+		
+		if ( !empty($insert_list) )
+		{
+			if ( $debug )
+			{
+				echo "  Inserting strings...";
+				$start = microtime_float();
+			}
+			$insert_list = implode(",\n  ", $insert_list);
+			$sql = "INSERT INTO " . table_prefix . "language_strings(lang_id, string_category, string_name, string_content) VALUES\n  $insert_list;";
+			
+			// Free some memory
+			unset($insert_list);
+			
+			// Run the query
+			$q = $db->sql_query($sql);
+			if ( !$q )
+				$db->_die('lang.php - couldn\'t insert strings in import()');
+			
+			if ( $debug )
+			{
+				$time = round(microtime_float() - $start, 5);
+				echo "({$time}s)$br\n";
+			}
+		}
+		
+		// YAY! done!
+		// This will regenerate the cache file if possible.
+		if ( $debug )
+			echo "  Regenerating cache file$br\n";
+		$this->regen_caches();
+		
+		return true;
+	}
+	
+	/**
+ 	* Refetches the strings and writes out the cache file.
+ 	*/
+	
+	function regen_caches($refetch = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Refresh the strings in RAM to the latest copies in the DB
+		if ( $refetch )
+			$this->fetch(false);
+		
+		// Load the cache manager
+		global $cache;
+		
+		// Store it
+		$cache->store("lang_{$this->lang_id}", $this->strings, -1);
+		
+		// Update timestamp in database
+		$q = $db->sql_query('UPDATE ' . table_prefix . 'language SET last_changed = ' . time() . ' WHERE lang_id = ' . $this->lang_id . ';');
+		if ( !$q )
+			$db->_die('lang.php - updating timestamp on language');
+		
+		return true;
+	}
+	
+	/**
+ 	* Calls var_export() on whatever, and returns the function's output.
+ 	* @param mixed Whatever you want var_exported. Usually an array.
+ 	* @return string
+ 	*/
+	
+	static function var_export_string($val)
+	{
+		ob_start();
+		var_export($val);
+		$contents = ob_get_contents();
+		ob_end_clean();
+		return $contents;
+	}
+	
+	/**
+ 	* Registers a filter, a function that strings can be passed through to change the string somehow (e.g. htmlspecialchars)
+ 	* @param string Filter name. Lowercase alphanumeric (htmlsafe)
+ 	* @param callback Function to call.
+ 	* @return bool True on success, false if some error occurred
+ 	*/
+	
+	public function register_filter($filter_name, $filter_function)
+	{
+		if ( !is_string($filter_function) && !is_array($filter_function) )
+		{
+			return false;
+		}
+		if ( ( is_string($filter_function) && !function_exists($filter_function) ) || ( is_array($filter_function) && !method_exists(@$filter_function[0], @$filter_function[1]) ) )
+		{
+			return false;
+		}
+		if ( !preg_match('/^[a-z0-9_]+$/', $filter_name) )
+		{
+			return false;
+		}
+		$this->filters[$filter_name] = $filter_function;
+		return true;
+	}
+	
+	/**
+ 	* Fetches a language string from the cache in RAM. If it isn't there, it will call fetch() again and then try. If it still can't find it, it will ask for the string
+ 	* in the default language. If even then the string can't be found, this function will return what was passed to it.
+ 	*
+ 	* This will also templatize strings. If a string contains variables in the format %foo%, you may specify the second parameter as an associative array in the format
+ 	* of 'foo' => 'foo substitute'.
+ 	*
+ 	* @param string ID of the string to fetch. This will always be in the format of category_stringid.
+ 	* @param array Optional. Associative array of substitutions.
+ 	* @return string
+ 	*/
+	
+	function get($string_id, $substitutions = false)
+	{
+		if ( !is_array($substitutions) )
+			$substitutions = array();
+		// if this isn't a valid language string ID, just return the string unprocessed.
+		if ( !preg_match('/^([a-z0-9]+)((_[a-z0-9]+)+)$/', $string_id) )
+			return $string_id;
+		return $this->substitute($this->get_uncensored($string_id), $substitutions);
+	}
+	
+	/**
+ 	* The same as get(), but does not perform any substitution or filtering. Used in get() (of course) and in the admin panel, where
+ 	* strings are updated only if they were changed.
+ 	*
+ 	* @param string ID of the string to fetch. This will always be in the format of category_stringid.
+ 	* @param array Optional. Associative array of substitutions.
+ 	* @return string
+ 	*/
+	
+	function get_uncensored($string_id, $substitutions = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Extract the category and string ID
+		$category = substr($string_id, 0, ( strpos($string_id, '_') ));
+		$string_name = substr($string_id, ( strpos($string_id, '_') + 1 ));
+		$found = false;
+		if ( isset($this->strings[$category]) && isset($this->strings[$category][$string_name]) )
+		{
+			$found = true;
+			$string = $this->strings[$category][$string_name];
+		}
+		if ( !$found )
+		{
+			// Ehh, the string wasn't found. Rerun fetch() and try again.
+			// Or if it's the installer, no use in refetching, so just fail.
+			if ( defined('IN_ENANO_INSTALL') || ( defined('ENANO_INSTALLED') && !@$db->_conn ) )
+			{
+				return $string_id;
+			}
+			$this->fetch();
+			profiler_log('Language(' . $this->lang_code . '): refetched due to missing string: ' . $string_id);
+			if ( isset($this->strings[$category]) && isset($this->strings[$category][$string_name]) )
+			{
+				$found = true;
+				$string = $this->strings[$category][$string_name];
+			}
+			if ( !$found )
+			{
+				// STILL not found. Check the default language.
+				$lang_default = ( $x = getConfig('default_language') ) ? intval($x) : $this->lang_id;
+				if ( $lang_default != $this->lang_id )
+				{
+					if ( !is_object($this->default) )
+						$this->default = new Language($lang_default);
+					return $this->default->get_uncensored($string_id);
+				}
+			}
+		}
+		if ( !$found )
+		{
+			// Alright, it's nowhere. Return the input, grumble grumble...
+			return $string_id;
+		}
+		// Found it!
+		return $string;
+	}
+	
+	/**
+ 	* Processes substitutions.
+ 	* @param string
+ 	* @param array
+ 	* @return string
+ 	*/
+	
+	function substitute($string, $subs)
+	{
+		preg_match_all('/%this\.([a-z0-9_]+)((?:\|(?:[a-z0-9_]+))*)%/', $string, $matches);
+		if ( count($matches[0]) > 0 )
+		{
+			foreach ( $matches[1] as $i => $string_id )
+			{
+				$result = $this->get($string_id);
+				$string = str_replace($matches[0][$i], $this->process_filters($result, $matches[2][$i]), $string);
+			}
+		}
+		preg_match_all('/%config\.([a-z0-9_]+)((?:\|(?:[a-z0-9_]+))*)%/', $string, $matches);
+		if ( count($matches[0]) > 0 )
+		{
+			foreach ( $matches[1] as $i => $string_id )
+			{
+				$result = getConfig($string_id, '');
+				$string = str_replace($matches[0][$i], $this->process_filters($result, $matches[2][$i]), $string);
+			}
+		}
+		preg_match_all('/%([a-z0-9_]+)((?:\|(?:[a-z0-9_]+))*)%/', $string, $matches);
+		if ( count($matches[0]) > 0 )
+		{
+			foreach ( $matches[1] as $i => $string_id )
+			{
+				if ( isset($subs[$string_id]) )
+				{
+					$string = str_replace($matches[0][$i], $this->process_filters($subs[$string_id], $matches[2][$i]), $string);
+				}
+			}
+		}
+		return ( $this->debug ) ? "$string*" : $string;
+	}
+	
+	/**
+ 	* Processes filters to a language string.
+ 	* @param string Unprocessed string
+ 	* @param string Filter list (format: |filter1|filter2|filter3, initial pipe is important); can also be an array if you so desire
+ 	* @return string
+ 	*/
+	
+	function process_filters($string, $filters)
+	{
+		if ( !empty($filters) )
+		{
+			$filters = trim($filters, '|');
+			$filters = explode('|', $filters);
+			foreach ( $filters as $filter )
+			{
+				if ( isset($this->filters[$filter]) )
+				{
+					$result = call_user_func($this->filters[$filter], $string);
+					if ( is_string($result) )
+					{
+						$string = $result;
+					}
+				}
+			}
+		}
+		return $string;
+	}
+	
 } // class Language
 
 ?>
--- a/includes/log.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/log.php	Sun Mar 28 23:10:46 2010 -0400
@@ -22,469 +22,469 @@
 
 class LogDisplay
 {
-  /**
-   * Criteria for the search.
-   * Structure:
-   <code>
-   array(
-       array( 'user', 'Dan' ),
-       array( 'within', 86400 ),
-       array( 'page', 'Main_Page' )
-     )
-   </code>
-   * @var array
-   */
-  
-  var $criteria = array();
-  
-  /**
-   * Adds a criterion for the log display.
-   * @param string Criterion type - user, page, or within
-   * @param string Value - username, page ID, or (int) within # seconds or (string) number + suffix (suffix: d = day, w = week, m = month, y = year) ex: "1w"
-   */
-  
-  public function add_criterion($criterion, $value)
-  {
-    switch ( $criterion )
-    {
-      case 'user':
-      case 'page':
-      case 'action':
-        $this->criteria[] = array($criterion, $value);
-        break;
-      case 'minor':
-        $this->criteria[] = array($criterion, intval($value));
-        break;
-      case 'within':
-        if ( is_int($value) )
-        {
-          $this->criteria[] = array($criterion, $value);
-        }
-        else if ( is_string($value) )
-        {
-          $lastchar = substr($value, -1);
-          $amt = intval($value);
-          switch($lastchar)
-          {
-            case 'd':
-              $amt = $amt * 86400;
-              break;
-            case 'w':
-              $amt = $amt * 604800;
-              break;
-            case 'm':
-              $amt = $amt * 2592000;
-              break;
-            case 'y':
-              $amt = $amt * 31536000;
-              break;
-          }
-          $this->criteria[] = array($criterion, $amt);
-        }
-        else
-        {
-          throw new Exception('Invalid value type for within');
-        }
-        break;
-      default:
-        throw new Exception('Unknown criterion type');
-        break;
-    }
-  }
-  
-  /**
-   * Build the necessary SQL query.
-   * @param int Optional: offset, defaults to 0
-   * @param int Optional: page size, defaults to 0; 0 = don't limit
-   */
-  
-  public function build_sql($offset = 0, $page_size = 0, $just_page_count = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $where_extra = '';
-    $where_bits = array(
-        'user' => array(),
-        'page' => array(),
-        'action' => array()
-      );
-    foreach ( $this->criteria as $criterion )
-    {
-      list($type, $value) = $criterion;
-      switch($type)
-      {
-        case 'user':
-          $where_bits['user'][] = "author = '" . $db->escape(str_replace('_', ' ', $value)) . "'";
-          break;
-        case 'action':
-          if ( $value === 'protect' )
-          {
-            $where_bits['action'][] = "action = 'prot'";
-            $where_bits['action'][] = "action = 'unprot'";
-            $where_bits['action'][] = "action = 'semiprot'";
-          }
-          else
-          {
-            $where_bits['action'][] = "action = '" . $db->escape($value) . "'";
-          }
-          break;
-        case 'page':
-          list($page_id, $namespace) = RenderMan::strToPageId($value);
-          $where_bits['page'][] = "page_id = '" . $db->escape($page_id) . "' AND namespace = '" . $db->escape($namespace) . "'";
-          break;
-        case 'within':
-          $threshold = time() - $value;
-          $where_extra .= "\n    AND time_id > $threshold";
-          break;
-        case 'minor':
-          if ( $value == 1 )
-            $where_extra .= "\n    AND ( minor_edit = 1 OR action != 'edit' )";
-          else
-            $where_extra .= "\n    AND minor_edit != 1";
-          break;
-      }
-    }
-    if ( !empty($where_bits['user']) )
-    {
-      $where_extra .= "\n    AND ( " . implode(" OR ", $where_bits['user']) . " )";
-    }
-    if ( !empty($where_bits['page']) )
-    {
-      $where_extra .= "\n    AND ( (" . implode(") OR (", $where_bits['page']) . ") )";
-    }
-    if ( !empty($where_bits['action']) )
-    {
-      $where_extra .= "\n    AND ( (" . implode(") OR (", $where_bits['action']) . ") )";
-    }
-    if ( ENANO_DBLAYER == 'PGSQL' )
-      $limit = ( $page_size > 0 ) ? "\n  LIMIT $page_size OFFSET $offset" : '';
-    else
-      $limit = ( $page_size > 0 ) ? "\n  LIMIT $offset, $page_size" : '';
-    $columns = ( $just_page_count ) ? 'COUNT(*)' : 'log_id, action, page_id, namespace, CHAR_LENGTH(page_text) AS revision_size, author, author_uid, u.username, time_id, edit_summary, minor_edit';
-    $sql = 'SELECT ' . $columns . ' FROM ' . table_prefix . "logs AS l\n"
-         . "  LEFT JOIN " . table_prefix . "users AS u\n"
-         . "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
-         . "  WHERE log_type = 'page' AND is_draft != 1$where_extra\n"
-         . "  GROUP BY log_id, action, page_id, namespace, page_text, author, author_uid, username, time_id, edit_summary, minor_edit\n"
-         . "  ORDER BY time_id DESC $limit;";
-    
-    return $sql;
-  }
-  
-  /**
-   * Get data!
-   * @param int Offset, defaults to 0
-   * @param int Page size, if 0 (default) returns entire table (danger Will Robinson!)
-   * @return array
-   */
-  
-  public function get_data($offset = 0, $page_size = 0)
-  {
-    global $db, $session, $paths, $session, $plugins; // Common objects
-    $sql = $this->build_sql($offset, $page_size);
-    if ( !$db->sql_query($sql) )
-      $db->_die();
-    
-    $return = array();
-    $deplist = array();
-    $idlist = array();
-    while ( $row = $db->fetchrow() )
-    {
-      $return[ $row['log_id'] ] = $row;
-      if ( $row['action'] === 'edit' )
-      {
-        // This is a page revision; its parent needs to be found
-        $pagekey = serialize(array($row['page_id'], $row['namespace']));
-        $deplist[$pagekey] = "( page_id = '" . $db->escape($row['page_id']) . "' AND namespace = '" . $db->escape($row['namespace']) . "' AND log_id < {$row['log_id']} )";
-        // if we already have a revision from this page in the dataset, we've found its parent
-        if ( isset($idlist[$pagekey]) )
-        {
-          $child =& $return[ $idlist[$pagekey] ];
-          $child['parent_size'] = $row['revision_size'];
-          $child['parent_revid'] = $row['log_id'];
-          $child['parent_time'] = $row['time_id'];
-          unset($child);
-        }
-        $idlist[$pagekey] = $row['log_id'];
-      }
-    }
-    
-    // Second query fetches all parent revision data
-    // (maybe we have no edits?? check deplist)
-    
-    if ( !empty($deplist) )
-    {
-      // FIXME: inefficient. damn GROUP BY for not obeying ORDER BY, it means we can't group and instead have to select
-      // all previous revisions of page X and discard all but the first one we find.
-      $where_extra = implode("\n    OR ", $deplist);
-      $sql = 'SELECT log_id, page_id, namespace, CHAR_LENGTH(page_text) AS revision_size, time_id FROM ' . table_prefix . "logs\n"
-           . "  WHERE log_type = 'page' AND action = 'edit'\n  AND ( $where_extra )\n"
-           // . "  GROUP BY page_id, namespace\n"
-           . "  ORDER BY log_id DESC;";
-      if ( !$db->sql_query($sql) )
-        $db->_die();
-      
-      while ( $row = $db->fetchrow() )
-      {
-        $pagekey = serialize(array($row['page_id'], $row['namespace']));
-        if ( isset($idlist[$pagekey]) )
-        {
-          $child =& $return[ $idlist[$pagekey] ];
-          $child['parent_size'] = $row['revision_size'];
-          $child['parent_revid'] = $row['log_id'];
-          $child['parent_time'] = $row['time_id'];
-          unset($child, $idlist[$pagekey]);
-        }
-      }
-    }
-    
-    // final iteration goes through all edits and if there's not info on the parent, sets to 0. It also calculates size change.
-    foreach ( $return as &$row )
-    {
-      if ( $row['action'] === 'edit' && !isset($row['parent_revid']) )
-      {
-        $row['parent_revid'] = 0;
-        $row['parent_size'] = 0;
-      }
-      if ( $row['action'] === 'edit' )
-      {
-        $row['size_delta'] = $row['revision_size'] - $row['parent_size'];
-      }
-    }
-    
-    return array_values($return);
-  }
-  
-  /**
-   * Get the number of rows that will be in the result set.
-   * @return int
-   */
-  
-  public function get_row_count()
-  {
-    global $db, $session, $paths, $session, $plugins; // Common objects
-    
-    if ( !$db->sql_query( $this->build_sql(0, 0, true) ) )
-      $db->_die();
-    
-    list($count) = $db->fetchrow_num();
-    return $count;
-  }
-  
-  /**
-   * Returns the list of criteria
-   * @return array
-   */
-  
-  public function get_criteria()
-  {
-    return $this->criteria;
-  }
-  
-  /**
-   * Formats a result row into pretty HTML.
-   * @param array dataset from LogDisplay::get_data()
-   * @param bool If true (default), shows action buttons.
-   * @param bool If true (default), shows page title; good for integrated displays
-   * @static
-   * @return string
-   */
-  
-  public static function render_row($row, $show_buttons = true, $show_pagetitle = true)
-  {
-    global $db, $session, $paths, $session, $plugins; // Common objects
-    global $lang;
-    
-    $html = '';
-    
-    $pagekey = ( isset($paths->nslist[$row['namespace']]) ) ? $paths->nslist[$row['namespace']] . $row['page_id'] : $row['namespace'] . ':' . $row['page_id'];
-    $pagekey = sanitize_page_id($pagekey);
-    
-    // diff button
-    if ( $show_buttons )
-    {
-      if ( $row['action'] == 'edit' && !empty($row['parent_revid']) )
-      {
-        $html .= '(';
-        $ispage = isPage($pagekey);
-        
-        if ( $ispage )
-          $html .= '<a href="' . makeUrlNS($row['namespace'], $row['page_id'], "do=diff&diff1={$row['parent_revid']}&diff2={$row['log_id']}", true) . '">';
-        
-        $html .= $lang->get('pagetools_rc_btn_diff');
-        
-        if ( $ispage )
-          $html .= '</a>';
-        
-        if ( $ispage )
-          $html .= ', <a href="' . makeUrlNS($row['namespace'], $row['page_id'], "oldid={$row['log_id']}", true) . '">';
-        
-        $html .= $lang->get('pagetools_rc_btn_view');
-        
-        if ( $ispage )
-          $html .= '</a>';
-        
-        if ( $row['parent_revid'] > 0 && isPage($pagekey) )
-        {
-          $html .= ', <a href="' . makeUrlNS($row['namespace'], $row['page_id'], false, true) . '#do:edit;rev:' . $row['parent_revid'] . '">' . $lang->get('pagetools_rc_btn_undo') . '</a>';
-        }
-        $html .= ') ';
-      }
-      else if ( $row['action'] != 'edit' && ( isPage($pagekey) || $row['action'] == 'delete' ) )
-      {
-        $html .= '(';
-        $html .= '<a href="' . makeUrlNS($row['namespace'], $row['page_id'], "do=rollback&id={$row['log_id']}", true). '">' . $lang->get('pagetools_rc_btn_undo') . '</a>';
-        $html .= ') ';
-      }
-      
-      // hist button
-      $html .= '(';
-      if ( isPage($pagekey) )
-      {
-        $html .= '<a href="' . makeUrlNS($row['namespace'], $row['page_id'], "do=history", true) . '">';
-      }
-      $html .= $lang->get('pagetools_rc_btn_hist');
-      if ( isPage($pagekey) )
-      {
-        $html .= '</a>';
-      }
-      $html .= ') . . ';
-    }
-    
-    if ( $show_pagetitle )
-    {
-      // new page?
-      if ( $row['action'] == 'edit' && empty($row['parent_revid']) )
-      {
-        $html .= '<b>N</b> ';
-      }
-      // minor edit?
-      if ( $row['action'] == 'edit' && $row['minor_edit'] )
-      {
-        $html .= '<b>m</b> ';
-      }
-      
-      // link to the page
-      $cls = ( isPage($pagekey) ) ? '' : ' class="wikilink-nonexistent"';
-      $html .= '<a href="' . makeUrlNS($row['namespace'], $row['page_id']) . '"' . $cls . '>' . htmlspecialchars(get_page_title_ns($row['page_id'], $row['namespace'])) . '</a>; ';
-    }
-    
-    // date
-    $today = time() - ( time() % 86400 );
-    $date = MemberlistFormatter::format_date($row['time_id']) . ' ';
-    $date .= date('h:i:s', $row['time_id']);
-    $html .= "$date . . ";
-    
-    // size counter
-    if ( $row['action'] == 'edit' )
-    {
-      $css = self::get_css($row['size_delta']);
-      $size_change = number_format($row['size_delta']);
-      if ( substr($size_change, 0, 1) != '-' )
-        $size_change = "+$size_change";
-      
-      $html .= "<span style=\"$css\">({$size_change})</span>";
-      $html .= ' . . ';
-    }
-    
-    // link to userpage
-    $real_username = $row['author_uid'] > 1 && !empty($row['username']) ? $row['username'] : $row['author'];
-    $cls = ( isPage($paths->nslist['User'] . $real_username) ) ? '' : ' class="wikilink-nonexistent"';
-    $rank_info = $session->get_user_rank($row['author_uid']);
-    $html .= '<a style="' . $rank_info['rank_style'] . '" href="' . makeUrlNS('User', sanitize_page_id($real_username), false, true) . '"' . $cls . '>' . htmlspecialchars($real_username) . '</a> ';
-    $html .= '(';
-    $html .= '<a href="' . makeUrlNS('Special', 'PrivateMessages/Compose/To/' . sanitize_page_id($real_username), false, true) . '">';
-    $html .= $lang->get('pagetools_rc_btn_pm');
-    $html .= '</a>, ';
-    $html .= '<a href="' . makeUrlNS('User', sanitize_page_id($real_username), false, true) . '#do:comments">';
-    $html .= $lang->get('pagetools_rc_btn_usertalk');
-    $html .= '</a>';
-    $html .= ') . . ';
-    
-    // Edit summary
-    
-    if ( $row['action'] == 'edit' )
-    {
-      $html .= '<i>(';
-      if ( empty($row['edit_summary']) )
-      {
-        $html .= '<span style="color: #808080;">' . $lang->get('history_summary_none_given') . '</span>';
-      }
-      else
-      {
-        $html .= RenderMan::parse_internal_links(htmlspecialchars($row['edit_summary']));
-      }
-      $html .= ')</i>';
-    }
-    else
-    {
-      switch($row['action'])
-      {
-        default:
-          $html .= $row['action'];
-          break;
-        case 'rename':
-          $html .= $lang->get('log_action_rename', array('old_name' => htmlspecialchars($row['edit_summary'])));
-          break;
-        case 'create':
-          $html .= $lang->get('log_action_create');
-          break;
-        case 'votereset':
-          $html .= $lang->get('log_action_votereset', array('num_votes' => $row['edit_summary'], 'plural' => ( intval($row['edit_summary']) == 1 ? '' : $lang->get('meta_plural'))));
-          break;
-        case 'prot':
-        case 'unprot':
-        case 'semiprot':
-        case 'delete':
-        case 'reupload':
-          $stringmap = array(
-            'prot' => 'log_action_protect_full',
-            'unprot' => 'log_action_protect_none',
-            'semiprot' => 'log_action_protect_semi',
-            'delete' => 'log_action_delete',
-            'reupload' => 'log_action_reupload'
-          );
-        
-        if ( $row['edit_summary'] === '__REVERSION__' )
-          $reason = '<span style="color: #808080;">' . $lang->get('log_msg_reversion') . '</span>';
-        else if ( $row['action'] == 'reupload' && $row['edit_summary'] === '__ROLLBACK__' )
-          $reason = '<span style="color: #808080;">' . $lang->get('log_msg_file_restored') . '</span>';
-        else
-          $reason = ( !empty($row['edit_summary']) ) ? htmlspecialchars($row['edit_summary']) : '<span style="color: #808080;">' . $lang->get('log_msg_no_reason_provided') . '</span>';
-        
-        $html .= $lang->get($stringmap[$row['action']], array('reason' => $reason));
-      }
-    }
-    
-    return $html;
-  }
-  
-  /**
-   * Return CSS blurb for size delta
-   * @return string
-   * @static
-   * @access private
-   */
-  
-  private static function get_css($change_size)
-  {
-    // Hardly changed at all? Return a gray
-    if ( $change_size <= 5 && $change_size >= -5 )
-      return 'color: #808080;';
-    // determine saturation based on size of change (1-500 bytes)
-    $change_abs = abs($change_size);
-    $index = 0x70 * ( $change_abs / 500 );
-    if ( $index > 0x70 )
-      $index = 0x70;
-    $index = $index + 0x40;
-    $index = dechex($index);
-    if ( strlen($index) < 2 )
-      $index = "0$index";
-    $css = ( $change_size > 0 ) ? "color: #00{$index}00;" : "color: #{$index}0000;";
-    if ( $change_abs > 500 )
-      $css .= ' font-weight: bold;';
-    return $css;
-  }
+	/**
+ 	* Criteria for the search.
+ 	* Structure:
+ 	<code>
+ 	array(
+ 			array( 'user', 'Dan' ),
+ 			array( 'within', 86400 ),
+ 			array( 'page', 'Main_Page' )
+ 		)
+ 	</code>
+ 	* @var array
+ 	*/
+	
+	var $criteria = array();
+	
+	/**
+ 	* Adds a criterion for the log display.
+ 	* @param string Criterion type - user, page, or within
+ 	* @param string Value - username, page ID, or (int) within # seconds or (string) number + suffix (suffix: d = day, w = week, m = month, y = year) ex: "1w"
+ 	*/
+	
+	public function add_criterion($criterion, $value)
+	{
+		switch ( $criterion )
+		{
+			case 'user':
+			case 'page':
+			case 'action':
+				$this->criteria[] = array($criterion, $value);
+				break;
+			case 'minor':
+				$this->criteria[] = array($criterion, intval($value));
+				break;
+			case 'within':
+				if ( is_int($value) )
+				{
+					$this->criteria[] = array($criterion, $value);
+				}
+				else if ( is_string($value) )
+				{
+					$lastchar = substr($value, -1);
+					$amt = intval($value);
+					switch($lastchar)
+					{
+						case 'd':
+							$amt = $amt * 86400;
+							break;
+						case 'w':
+							$amt = $amt * 604800;
+							break;
+						case 'm':
+							$amt = $amt * 2592000;
+							break;
+						case 'y':
+							$amt = $amt * 31536000;
+							break;
+					}
+					$this->criteria[] = array($criterion, $amt);
+				}
+				else
+				{
+					throw new Exception('Invalid value type for within');
+				}
+				break;
+			default:
+				throw new Exception('Unknown criterion type');
+				break;
+		}
+	}
+	
+	/**
+ 	* Build the necessary SQL query.
+ 	* @param int Optional: offset, defaults to 0
+ 	* @param int Optional: page size, defaults to 0; 0 = don't limit
+ 	*/
+	
+	public function build_sql($offset = 0, $page_size = 0, $just_page_count = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$where_extra = '';
+		$where_bits = array(
+				'user' => array(),
+				'page' => array(),
+				'action' => array()
+			);
+		foreach ( $this->criteria as $criterion )
+		{
+			list($type, $value) = $criterion;
+			switch($type)
+			{
+				case 'user':
+					$where_bits['user'][] = "author = '" . $db->escape(str_replace('_', ' ', $value)) . "'";
+					break;
+				case 'action':
+					if ( $value === 'protect' )
+					{
+						$where_bits['action'][] = "action = 'prot'";
+						$where_bits['action'][] = "action = 'unprot'";
+						$where_bits['action'][] = "action = 'semiprot'";
+					}
+					else
+					{
+						$where_bits['action'][] = "action = '" . $db->escape($value) . "'";
+					}
+					break;
+				case 'page':
+					list($page_id, $namespace) = RenderMan::strToPageId($value);
+					$where_bits['page'][] = "page_id = '" . $db->escape($page_id) . "' AND namespace = '" . $db->escape($namespace) . "'";
+					break;
+				case 'within':
+					$threshold = time() - $value;
+					$where_extra .= "\n    AND time_id > $threshold";
+					break;
+				case 'minor':
+					if ( $value == 1 )
+						$where_extra .= "\n    AND ( minor_edit = 1 OR action != 'edit' )";
+					else
+						$where_extra .= "\n    AND minor_edit != 1";
+					break;
+			}
+		}
+		if ( !empty($where_bits['user']) )
+		{
+			$where_extra .= "\n    AND ( " . implode(" OR ", $where_bits['user']) . " )";
+		}
+		if ( !empty($where_bits['page']) )
+		{
+			$where_extra .= "\n    AND ( (" . implode(") OR (", $where_bits['page']) . ") )";
+		}
+		if ( !empty($where_bits['action']) )
+		{
+			$where_extra .= "\n    AND ( (" . implode(") OR (", $where_bits['action']) . ") )";
+		}
+		if ( ENANO_DBLAYER == 'PGSQL' )
+			$limit = ( $page_size > 0 ) ? "\n  LIMIT $page_size OFFSET $offset" : '';
+		else
+			$limit = ( $page_size > 0 ) ? "\n  LIMIT $offset, $page_size" : '';
+		$columns = ( $just_page_count ) ? 'COUNT(*)' : 'log_id, action, page_id, namespace, CHAR_LENGTH(page_text) AS revision_size, author, author_uid, u.username, time_id, edit_summary, minor_edit';
+		$sql = 'SELECT ' . $columns . ' FROM ' . table_prefix . "logs AS l\n"
+ 				. "  LEFT JOIN " . table_prefix . "users AS u\n"
+ 				. "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
+ 				. "  WHERE log_type = 'page' AND is_draft != 1$where_extra\n"
+ 				. "  GROUP BY log_id, action, page_id, namespace, page_text, author, author_uid, username, time_id, edit_summary, minor_edit\n"
+ 				. "  ORDER BY time_id DESC $limit;";
+		
+		return $sql;
+	}
+	
+	/**
+ 	* Get data!
+ 	* @param int Offset, defaults to 0
+ 	* @param int Page size, if 0 (default) returns entire table (danger Will Robinson!)
+ 	* @return array
+ 	*/
+	
+	public function get_data($offset = 0, $page_size = 0)
+	{
+		global $db, $session, $paths, $session, $plugins; // Common objects
+		$sql = $this->build_sql($offset, $page_size);
+		if ( !$db->sql_query($sql) )
+			$db->_die();
+		
+		$return = array();
+		$deplist = array();
+		$idlist = array();
+		while ( $row = $db->fetchrow() )
+		{
+			$return[ $row['log_id'] ] = $row;
+			if ( $row['action'] === 'edit' )
+			{
+				// This is a page revision; its parent needs to be found
+				$pagekey = serialize(array($row['page_id'], $row['namespace']));
+				$deplist[$pagekey] = "( page_id = '" . $db->escape($row['page_id']) . "' AND namespace = '" . $db->escape($row['namespace']) . "' AND log_id < {$row['log_id']} )";
+				// if we already have a revision from this page in the dataset, we've found its parent
+				if ( isset($idlist[$pagekey]) )
+				{
+					$child =& $return[ $idlist[$pagekey] ];
+					$child['parent_size'] = $row['revision_size'];
+					$child['parent_revid'] = $row['log_id'];
+					$child['parent_time'] = $row['time_id'];
+					unset($child);
+				}
+				$idlist[$pagekey] = $row['log_id'];
+			}
+		}
+		
+		// Second query fetches all parent revision data
+		// (maybe we have no edits?? check deplist)
+		
+		if ( !empty($deplist) )
+		{
+			// FIXME: inefficient. damn GROUP BY for not obeying ORDER BY, it means we can't group and instead have to select
+			// all previous revisions of page X and discard all but the first one we find.
+			$where_extra = implode("\n    OR ", $deplist);
+			$sql = 'SELECT log_id, page_id, namespace, CHAR_LENGTH(page_text) AS revision_size, time_id FROM ' . table_prefix . "logs\n"
+ 					. "  WHERE log_type = 'page' AND action = 'edit'\n  AND ( $where_extra )\n"
+ 					// . "  GROUP BY page_id, namespace\n"
+ 					. "  ORDER BY log_id DESC;";
+			if ( !$db->sql_query($sql) )
+				$db->_die();
+			
+			while ( $row = $db->fetchrow() )
+			{
+				$pagekey = serialize(array($row['page_id'], $row['namespace']));
+				if ( isset($idlist[$pagekey]) )
+				{
+					$child =& $return[ $idlist[$pagekey] ];
+					$child['parent_size'] = $row['revision_size'];
+					$child['parent_revid'] = $row['log_id'];
+					$child['parent_time'] = $row['time_id'];
+					unset($child, $idlist[$pagekey]);
+				}
+			}
+		}
+		
+		// final iteration goes through all edits and if there's not info on the parent, sets to 0. It also calculates size change.
+		foreach ( $return as &$row )
+		{
+			if ( $row['action'] === 'edit' && !isset($row['parent_revid']) )
+			{
+				$row['parent_revid'] = 0;
+				$row['parent_size'] = 0;
+			}
+			if ( $row['action'] === 'edit' )
+			{
+				$row['size_delta'] = $row['revision_size'] - $row['parent_size'];
+			}
+		}
+		
+		return array_values($return);
+	}
+	
+	/**
+ 	* Get the number of rows that will be in the result set.
+ 	* @return int
+ 	*/
+	
+	public function get_row_count()
+	{
+		global $db, $session, $paths, $session, $plugins; // Common objects
+		
+		if ( !$db->sql_query( $this->build_sql(0, 0, true) ) )
+			$db->_die();
+		
+		list($count) = $db->fetchrow_num();
+		return $count;
+	}
+	
+	/**
+ 	* Returns the list of criteria
+ 	* @return array
+ 	*/
+	
+	public function get_criteria()
+	{
+		return $this->criteria;
+	}
+	
+	/**
+ 	* Formats a result row into pretty HTML.
+ 	* @param array dataset from LogDisplay::get_data()
+ 	* @param bool If true (default), shows action buttons.
+ 	* @param bool If true (default), shows page title; good for integrated displays
+ 	* @static
+ 	* @return string
+ 	*/
+	
+	public static function render_row($row, $show_buttons = true, $show_pagetitle = true)
+	{
+		global $db, $session, $paths, $session, $plugins; // Common objects
+		global $lang;
+		
+		$html = '';
+		
+		$pagekey = ( isset($paths->nslist[$row['namespace']]) ) ? $paths->nslist[$row['namespace']] . $row['page_id'] : $row['namespace'] . ':' . $row['page_id'];
+		$pagekey = sanitize_page_id($pagekey);
+		
+		// diff button
+		if ( $show_buttons )
+		{
+			if ( $row['action'] == 'edit' && !empty($row['parent_revid']) )
+			{
+				$html .= '(';
+				$ispage = isPage($pagekey);
+				
+				if ( $ispage )
+					$html .= '<a href="' . makeUrlNS($row['namespace'], $row['page_id'], "do=diff&diff1={$row['parent_revid']}&diff2={$row['log_id']}", true) . '">';
+				
+				$html .= $lang->get('pagetools_rc_btn_diff');
+				
+				if ( $ispage )
+					$html .= '</a>';
+				
+				if ( $ispage )
+					$html .= ', <a href="' . makeUrlNS($row['namespace'], $row['page_id'], "oldid={$row['log_id']}", true) . '">';
+				
+				$html .= $lang->get('pagetools_rc_btn_view');
+				
+				if ( $ispage )
+					$html .= '</a>';
+				
+				if ( $row['parent_revid'] > 0 && isPage($pagekey) )
+				{
+					$html .= ', <a href="' . makeUrlNS($row['namespace'], $row['page_id'], false, true) . '#do:edit;rev:' . $row['parent_revid'] . '">' . $lang->get('pagetools_rc_btn_undo') . '</a>';
+				}
+				$html .= ') ';
+			}
+			else if ( $row['action'] != 'edit' && ( isPage($pagekey) || $row['action'] == 'delete' ) )
+			{
+				$html .= '(';
+				$html .= '<a href="' . makeUrlNS($row['namespace'], $row['page_id'], "do=rollback&id={$row['log_id']}", true). '">' . $lang->get('pagetools_rc_btn_undo') . '</a>';
+				$html .= ') ';
+			}
+			
+			// hist button
+			$html .= '(';
+			if ( isPage($pagekey) )
+			{
+				$html .= '<a href="' . makeUrlNS($row['namespace'], $row['page_id'], "do=history", true) . '">';
+			}
+			$html .= $lang->get('pagetools_rc_btn_hist');
+			if ( isPage($pagekey) )
+			{
+				$html .= '</a>';
+			}
+			$html .= ') . . ';
+		}
+		
+		if ( $show_pagetitle )
+		{
+			// new page?
+			if ( $row['action'] == 'edit' && empty($row['parent_revid']) )
+			{
+				$html .= '<b>N</b> ';
+			}
+			// minor edit?
+			if ( $row['action'] == 'edit' && $row['minor_edit'] )
+			{
+				$html .= '<b>m</b> ';
+			}
+			
+			// link to the page
+			$cls = ( isPage($pagekey) ) ? '' : ' class="wikilink-nonexistent"';
+			$html .= '<a href="' . makeUrlNS($row['namespace'], $row['page_id']) . '"' . $cls . '>' . htmlspecialchars(get_page_title_ns($row['page_id'], $row['namespace'])) . '</a>; ';
+		}
+		
+		// date
+		$today = time() - ( time() % 86400 );
+		$date = MemberlistFormatter::format_date($row['time_id']) . ' ';
+		$date .= date('h:i:s', $row['time_id']);
+		$html .= "$date . . ";
+		
+		// size counter
+		if ( $row['action'] == 'edit' )
+		{
+			$css = self::get_css($row['size_delta']);
+			$size_change = number_format($row['size_delta']);
+			if ( substr($size_change, 0, 1) != '-' )
+				$size_change = "+$size_change";
+			
+			$html .= "<span style=\"$css\">({$size_change})</span>";
+			$html .= ' . . ';
+		}
+		
+		// link to userpage
+		$real_username = $row['author_uid'] > 1 && !empty($row['username']) ? $row['username'] : $row['author'];
+		$cls = ( isPage($paths->nslist['User'] . $real_username) ) ? '' : ' class="wikilink-nonexistent"';
+		$rank_info = $session->get_user_rank($row['author_uid']);
+		$html .= '<a style="' . $rank_info['rank_style'] . '" href="' . makeUrlNS('User', sanitize_page_id($real_username), false, true) . '"' . $cls . '>' . htmlspecialchars($real_username) . '</a> ';
+		$html .= '(';
+		$html .= '<a href="' . makeUrlNS('Special', 'PrivateMessages/Compose/To/' . sanitize_page_id($real_username), false, true) . '">';
+		$html .= $lang->get('pagetools_rc_btn_pm');
+		$html .= '</a>, ';
+		$html .= '<a href="' . makeUrlNS('User', sanitize_page_id($real_username), false, true) . '#do:comments">';
+		$html .= $lang->get('pagetools_rc_btn_usertalk');
+		$html .= '</a>';
+		$html .= ') . . ';
+		
+		// Edit summary
+		
+		if ( $row['action'] == 'edit' )
+		{
+			$html .= '<i>(';
+			if ( empty($row['edit_summary']) )
+			{
+				$html .= '<span style="color: #808080;">' . $lang->get('history_summary_none_given') . '</span>';
+			}
+			else
+			{
+				$html .= RenderMan::parse_internal_links(htmlspecialchars($row['edit_summary']));
+			}
+			$html .= ')</i>';
+		}
+		else
+		{
+			switch($row['action'])
+			{
+				default:
+					$html .= $row['action'];
+					break;
+				case 'rename':
+					$html .= $lang->get('log_action_rename', array('old_name' => htmlspecialchars($row['edit_summary'])));
+					break;
+				case 'create':
+					$html .= $lang->get('log_action_create');
+					break;
+				case 'votereset':
+					$html .= $lang->get('log_action_votereset', array('num_votes' => $row['edit_summary'], 'plural' => ( intval($row['edit_summary']) == 1 ? '' : $lang->get('meta_plural'))));
+					break;
+				case 'prot':
+				case 'unprot':
+				case 'semiprot':
+				case 'delete':
+				case 'reupload':
+					$stringmap = array(
+						'prot' => 'log_action_protect_full',
+						'unprot' => 'log_action_protect_none',
+						'semiprot' => 'log_action_protect_semi',
+						'delete' => 'log_action_delete',
+						'reupload' => 'log_action_reupload'
+					);
+				
+				if ( $row['edit_summary'] === '__REVERSION__' )
+					$reason = '<span style="color: #808080;">' . $lang->get('log_msg_reversion') . '</span>';
+				else if ( $row['action'] == 'reupload' && $row['edit_summary'] === '__ROLLBACK__' )
+					$reason = '<span style="color: #808080;">' . $lang->get('log_msg_file_restored') . '</span>';
+				else
+					$reason = ( !empty($row['edit_summary']) ) ? htmlspecialchars($row['edit_summary']) : '<span style="color: #808080;">' . $lang->get('log_msg_no_reason_provided') . '</span>';
+				
+				$html .= $lang->get($stringmap[$row['action']], array('reason' => $reason));
+			}
+		}
+		
+		return $html;
+	}
+	
+	/**
+ 	* Return CSS blurb for size delta
+ 	* @return string
+ 	* @static
+ 	* @access private
+ 	*/
+	
+	private static function get_css($change_size)
+	{
+		// Hardly changed at all? Return a gray
+		if ( $change_size <= 5 && $change_size >= -5 )
+			return 'color: #808080;';
+		// determine saturation based on size of change (1-500 bytes)
+		$change_abs = abs($change_size);
+		$index = 0x70 * ( $change_abs / 500 );
+		if ( $index > 0x70 )
+			$index = 0x70;
+		$index = $index + 0x40;
+		$index = dechex($index);
+		if ( strlen($index) < 2 )
+			$index = "0$index";
+		$css = ( $change_size > 0 ) ? "color: #00{$index}00;" : "color: #{$index}0000;";
+		if ( $change_abs > 500 )
+			$css .= ' font-weight: bold;';
+		return $css;
+	}
 }
  
 ?>
--- a/includes/math.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/math.php	Sun Mar 28 23:10:46 2010 -0400
@@ -18,70 +18,70 @@
 
 class EnanoMath_GMP
 {
-  var $api = 'GMP';
-  
-  /**
-   * Initializes a number to a GMP integer.
-   * @param string String representation of the number
-   * @param int Base the number is currently in, defaults to 10
-   * @return resource
-   */
-  
-  function init($int, $base = 10)
-  {
-    return ( is_resource($int) ) ? $int : gmp_init($int, $base);
-  }
-  
-  /**
-   * Converts a number from a GMP integer to a string
-   * @param resource
-   * @param int Base, default is 10
-   * @return string
-   */
-  
-  function str($int, $base = 10)
-  {
-    return ( is_string($int) ) ? $int : gmp_strval($int, $base);
-  }
-  
-  /**
-   * Converts a number between bases.
-   * @param resource BigInt to convert
-   * @param int Base to convert from
-   * @param int Base to convert to
-   */
-   
-  function basecon($int, $from, $to)
-  {
-    return $this->init(gmp_strval(gmp_init($this->str($int), $from), $to));
-  }
-  
-  /**
-   * Generates a random integer.
-   * @param int Length
-   * @return resource
-   */
-  
-  function random($len)
-  {
-    return gmp_random($len / 8);
-  }
-  
-  /**
-   * Powmod operation (calculates (a ^ b) mod m)
-   * @param resource a
-   * @param resource b
-   * @param resource m
-   * @return resource
-   */
-  
-  function powmod($a, $b, $m)
-  {
-    $a = $this->init($a);
-    $b = $this->init($b);
-    $m = $this->init($m);
-    return ( function_exists('gmp_powm') ) ? gmp_powm($a, $b, $m) : gmp_mod(gmp_pow($a, $b), $m);
-  }
+	var $api = 'GMP';
+	
+	/**
+ 	* Initializes a number to a GMP integer.
+ 	* @param string String representation of the number
+ 	* @param int Base the number is currently in, defaults to 10
+ 	* @return resource
+ 	*/
+	
+	function init($int, $base = 10)
+	{
+		return ( is_resource($int) ) ? $int : gmp_init($int, $base);
+	}
+	
+	/**
+ 	* Converts a number from a GMP integer to a string
+ 	* @param resource
+ 	* @param int Base, default is 10
+ 	* @return string
+ 	*/
+	
+	function str($int, $base = 10)
+	{
+		return ( is_string($int) ) ? $int : gmp_strval($int, $base);
+	}
+	
+	/**
+ 	* Converts a number between bases.
+ 	* @param resource BigInt to convert
+ 	* @param int Base to convert from
+ 	* @param int Base to convert to
+ 	*/
+ 	
+	function basecon($int, $from, $to)
+	{
+		return $this->init(gmp_strval(gmp_init($this->str($int), $from), $to));
+	}
+	
+	/**
+ 	* Generates a random integer.
+ 	* @param int Length
+ 	* @return resource
+ 	*/
+	
+	function random($len)
+	{
+		return gmp_random($len / 8);
+	}
+	
+	/**
+ 	* Powmod operation (calculates (a ^ b) mod m)
+ 	* @param resource a
+ 	* @param resource b
+ 	* @param resource m
+ 	* @return resource
+ 	*/
+	
+	function powmod($a, $b, $m)
+	{
+		$a = $this->init($a);
+		$b = $this->init($b);
+		$m = $this->init($m);
+		return ( function_exists('gmp_powm') ) ? gmp_powm($a, $b, $m) : gmp_mod(gmp_pow($a, $b), $m);
+	}
 }
 
 /**
@@ -90,70 +90,70 @@
 
 class EnanoMath_BigInt
 {
-  var $api = 'big_int';
-  
-  /**
-   * Initializes a number to a BigInt integer.
-   * @param string String representation of the number
-   * @param int Base the number is in, defaults to 10
-   * @return resource
-   */
-  
-  function init($int, $base = 10)
-  {
-    return (is_resource($int)) ? $int : bi_from_str($int, $base);
-  }
-  
-  /**
-   * Converts a number from a BigInt integer to a string
-   * @param resource
-   * @param int Base, default is 10
-   * @return string
-   */
-  
-  function str($int, $base = 10)
-  {
-    return ( is_string($int) ) ? $int : bi_to_str($int, $base);
-  }
-  
-  /**
-   * Generates a random integer
-   * @param int Length (bits)
-   * @return resource
-   */
-  
-  function random($len)
-  {
-    return bi_rand($len);
-  }
-  
-  /**
-   * Converts a number between bases.
-   * @param resource BigInt to convert
-   * @param int Base to convert from
-   * @param int Base to convert to
-   */
-  
-  function basecon($int, $from, $to)
-  {
-    return bi_base_convert($this->str($int, $from), $from, $to);
-  }
-  
-  /**
-   * Powmod operation (calculates (a ^ b) mod m)
-   * @param resource a
-   * @param resource b
-   * @param resource m
-   * @return resource
-   */
-  
-  function powmod($a, $b, $m)
-  {
-    $a = $this->init($a);
-    $b = $this->init($b);
-    $m = $this->init($m);
-    return bi_powmod($a, $b, $m);
-  }
+	var $api = 'big_int';
+	
+	/**
+ 	* Initializes a number to a BigInt integer.
+ 	* @param string String representation of the number
+ 	* @param int Base the number is in, defaults to 10
+ 	* @return resource
+ 	*/
+	
+	function init($int, $base = 10)
+	{
+		return (is_resource($int)) ? $int : bi_from_str($int, $base);
+	}
+	
+	/**
+ 	* Converts a number from a BigInt integer to a string
+ 	* @param resource
+ 	* @param int Base, default is 10
+ 	* @return string
+ 	*/
+	
+	function str($int, $base = 10)
+	{
+		return ( is_string($int) ) ? $int : bi_to_str($int, $base);
+	}
+	
+	/**
+ 	* Generates a random integer
+ 	* @param int Length (bits)
+ 	* @return resource
+ 	*/
+	
+	function random($len)
+	{
+		return bi_rand($len);
+	}
+	
+	/**
+ 	* Converts a number between bases.
+ 	* @param resource BigInt to convert
+ 	* @param int Base to convert from
+ 	* @param int Base to convert to
+ 	*/
+	
+	function basecon($int, $from, $to)
+	{
+		return bi_base_convert($this->str($int, $from), $from, $to);
+	}
+	
+	/**
+ 	* Powmod operation (calculates (a ^ b) mod m)
+ 	* @param resource a
+ 	* @param resource b
+ 	* @param resource m
+ 	* @return resource
+ 	*/
+	
+	function powmod($a, $b, $m)
+	{
+		$a = $this->init($a);
+		$b = $this->init($b);
+		$m = $this->init($m);
+		return bi_powmod($a, $b, $m);
+	}
 }
 
 /**
@@ -162,129 +162,129 @@
 
 class EnanoMath_BCMath
 {
-  var $api = 'BCMath';
-  
-  /**
-   * Initializes a number to a BCMath integer.
-   * @param string String representation of the number
-   * @param int Base the number is in, defaults to 10
-   * @return resource
-   */
-  
-  function init($int, $base = 10)
-  {
-    return $this->basecon($int, $base, 10);
-  }
-  
-  /**
-   * Converts a number from a BCMath integer to a string
-   * @param resource
-   * @param int Base, default is 10
-   * @return string
-   */
-   
-  function str($res)
-  {
-    return ( is_string($res) ) ? $res : strval($this->basecon($res, 10, $base));
-  }
-  
-  /**
-   * Generates a random integer
-   * @param int Length in bits
-   * @return resource
-   */
-  
-  function random($len)
-  {
-    $len = 4 * $len;
-    $chars = '0123456789abcdef';
-    $ret = '';
-    for ( $i = 0; $i < $len; $i++ )
-    {
-      $chid = mt_rand ( 0, strlen($chars) - 1 );
-      $chr = $chars{$chid};
-      $ret .= $chr;
-    }
-    return $this->basecon($this->init($ret), 16, 10);
-  }
-  
-  /**
-   * Converts a number between bases.
-   * @param resource BigInt to convert
-   * @param int Base to convert from
-   * @param int Base to convert to
-   */
-  
-  function basecon($int, $from, $to)
-  {
-    if ( $from != 10 )
-      $int = $this->_bcmath_base2dec($int, $from);
-    if ( $to != 10 )
-      $int = $this->_bcmath_dec2base($int, $to);
-    return $int;
-  }
-  
-  /**
-   * Powmod operation (calculates (a ^ b) mod m)
-   * @param resource a
-   * @param resource b
-   * @param resource m
-   * @return resource
-   */
-  
-  function powmod($a, $b, $m)
-  {
-    $a = $this->init($a);
-    $b = $this->init($b);
-    $m = $this->init($m);
-    return ( function_exists('bcpowmod') ) ? bcpowmod($a, $b, $m) : bcmod( bcpow($a, $b), $m );
-  }
-  
-  // from us.php.net/bc:
-  // convert a decimal value to any other base value
-  function _bcmath_dec2base($dec,$base,$digits=FALSE) {
-      if($base<2 or $base>256) die("Invalid Base: ".$base);
-      bcscale(0);
-      $value="";
-      if(!$digits) $digits=$this->_bcmath_digits($base);
-      while($dec>$base-1) {
-          $rest=bcmod($dec,$base);
-          $dec=bcdiv($dec,$base);
-          $value=$digits[$rest].$value;
-      }
-      $value=$digits[intval($dec)].$value;
-      return (string) $value;
-  }
-  
-  // convert another base value to its decimal value
-  function _bcmath_base2dec($value,$base,$digits=FALSE) {
-      if($base<2 or $base>256) die("Invalid Base: ".$base);
-      bcscale(0);
-      if($base<37) $value=strtolower($value);
-      if(!$digits) $digits=$this->_bcmath_digits($base);
-      $size=strlen($value);
-      $dec="0";
-      for($loop=0;$loop<$size;$loop++) {
-          $element=strpos($digits,$value[$loop]);
-          $power=bcpow($base,$size-$loop-1);
-          $dec=bcadd($dec,bcmul($element,$power));
-      }
-      return (string) $dec;
-  }
-  
-  function _bcmath_digits($base) {
-      if($base>64) {
-          $digits="";
-          for($loop=0;$loop<256;$loop++) {
-              $digits.=chr($loop);
-          }
-      } else {
-          $digits ="0123456789abcdefghijklmnopqrstuvwxyz";
-          $digits.="ABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
-      }
-      $digits=substr($digits,0,$base);
-      return (string) $digits;
-  }
+	var $api = 'BCMath';
+	
+	/**
+ 	* Initializes a number to a BCMath integer.
+ 	* @param string String representation of the number
+ 	* @param int Base the number is in, defaults to 10
+ 	* @return resource
+ 	*/
+	
+	function init($int, $base = 10)
+	{
+		return $this->basecon($int, $base, 10);
+	}
+	
+	/**
+ 	* Converts a number from a BCMath integer to a string
+ 	* @param resource
+ 	* @param int Base, default is 10
+ 	* @return string
+ 	*/
+ 	
+	function str($res)
+	{
+		return ( is_string($res) ) ? $res : strval($this->basecon($res, 10, $base));
+	}
+	
+	/**
+ 	* Generates a random integer
+ 	* @param int Length in bits
+ 	* @return resource
+ 	*/
+	
+	function random($len)
+	{
+		$len = 4 * $len;
+		$chars = '0123456789abcdef';
+		$ret = '';
+		for ( $i = 0; $i < $len; $i++ )
+		{
+			$chid = mt_rand ( 0, strlen($chars) - 1 );
+			$chr = $chars{$chid};
+			$ret .= $chr;
+		}
+		return $this->basecon($this->init($ret), 16, 10);
+	}
+	
+	/**
+ 	* Converts a number between bases.
+ 	* @param resource BigInt to convert
+ 	* @param int Base to convert from
+ 	* @param int Base to convert to
+ 	*/
+	
+	function basecon($int, $from, $to)
+	{
+		if ( $from != 10 )
+			$int = $this->_bcmath_base2dec($int, $from);
+		if ( $to != 10 )
+			$int = $this->_bcmath_dec2base($int, $to);
+		return $int;
+	}
+	
+	/**
+ 	* Powmod operation (calculates (a ^ b) mod m)
+ 	* @param resource a
+ 	* @param resource b
+ 	* @param resource m
+ 	* @return resource
+ 	*/
+	
+	function powmod($a, $b, $m)
+	{
+		$a = $this->init($a);
+		$b = $this->init($b);
+		$m = $this->init($m);
+		return ( function_exists('bcpowmod') ) ? bcpowmod($a, $b, $m) : bcmod( bcpow($a, $b), $m );
+	}
+	
+	// from us.php.net/bc:
+	// convert a decimal value to any other base value
+	function _bcmath_dec2base($dec,$base,$digits=FALSE) {
+			if($base<2 or $base>256) die("Invalid Base: ".$base);
+			bcscale(0);
+			$value="";
+			if(!$digits) $digits=$this->_bcmath_digits($base);
+			while($dec>$base-1) {
+					$rest=bcmod($dec,$base);
+					$dec=bcdiv($dec,$base);
+					$value=$digits[$rest].$value;
+			}
+			$value=$digits[intval($dec)].$value;
+			return (string) $value;
+	}
+	
+	// convert another base value to its decimal value
+	function _bcmath_base2dec($value,$base,$digits=FALSE) {
+			if($base<2 or $base>256) die("Invalid Base: ".$base);
+			bcscale(0);
+			if($base<37) $value=strtolower($value);
+			if(!$digits) $digits=$this->_bcmath_digits($base);
+			$size=strlen($value);
+			$dec="0";
+			for($loop=0;$loop<$size;$loop++) {
+					$element=strpos($digits,$value[$loop]);
+					$power=bcpow($base,$size-$loop-1);
+					$dec=bcadd($dec,bcmul($element,$power));
+			}
+			return (string) $dec;
+	}
+	
+	function _bcmath_digits($base) {
+			if($base>64) {
+					$digits="";
+					for($loop=0;$loop<256;$loop++) {
+							$digits.=chr($loop);
+					}
+			} else {
+					$digits ="0123456789abcdefghijklmnopqrstuvwxyz";
+					$digits.="ABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
+			}
+			$digits=substr($digits,0,$base);
+			return (string) $digits;
+	}
 }
 
 /**
@@ -294,14 +294,14 @@
 
 function enanomath_create()
 {
-  if ( function_exists('gmp_init') )
-    return new EnanoMath_GMP();
-  else if ( function_exists('bi_from_str') )
-    return new EnanoMath_BigInt();
-  else if ( function_exists('bcadd') )
-    return new EnanoMath_BCMath();
-  else
-    throw new Exception('dh_err_not_supported');
+	if ( function_exists('gmp_init') )
+		return new EnanoMath_GMP();
+	else if ( function_exists('bi_from_str') )
+		return new EnanoMath_BigInt();
+	else if ( function_exists('bcadd') )
+		return new EnanoMath_BCMath();
+	else
+		throw new Exception('dh_err_not_supported');
 }
 
 ?>
--- a/includes/namespaces/api.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/namespaces/api.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,21 +13,21 @@
 
 class Namespace_API extends Namespace_Default
 {
-  function send()
-  {
-    global $output, $session;
-    $uri = scriptPath . '/' . $this->page_id;
-    if ( $output->naked )
-    {
-      $sep = ( strstr($uri, '?') ) ? '&' : '?';
-      $uri .= "{$sep}noheaders";
-    }
-    if ( $session->sid_super )
-    {
-      $sep = ( strstr($uri, '?') ) ? '&' : '?';
-      $uri .= "{$sep}auth={$session->sid_super}";
-    }
-    redirect( $uri, '', '', 0 );
-  }
+	function send()
+	{
+		global $output, $session;
+		$uri = scriptPath . '/' . $this->page_id;
+		if ( $output->naked )
+		{
+			$sep = ( strstr($uri, '?') ) ? '&' : '?';
+			$uri .= "{$sep}noheaders";
+		}
+		if ( $session->sid_super )
+		{
+			$sep = ( strstr($uri, '?') ) ? '&' : '?';
+			$uri .= "{$sep}auth={$session->sid_super}";
+		}
+		redirect( $uri, '', '', 0 );
+	}
 }
 
--- a/includes/namespaces/default.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/namespaces/default.php	Sun Mar 28 23:10:46 2010 -0400
@@ -21,1013 +21,1013 @@
 
 class Namespace_Default
 {
-  /**
-   * Page ID
-   * @var string
-   */
-  
-  public $page_id;
-  
-  /**
-   * Namespace
-   * @var string
-   */
-  
-  public $namespace;
-  
-  /**
-   * Local copy of the page text
-   */
-  
-  public $text_cache;
-  
-  /**
-   * Revision ID to send. If 0, the latest revision.
-   * @var int
-   */
-  
-  public $revision_id = 0;
-  
-  /**
-   * Tracks whether the page exists
-   * @var bool
-   */
-  
-  public $exists = false;
-  
-  /**
-   * Page title
-   * @var string
-   */
-  
-  public $title = '';
-  
-  /**
-   * PathManager info array ("cdata") for this page. (The one with urlname, name, namespace, delvotes, delvote_ips, protected, visible, etc.)
-   * @var array
-   */
-  
-  public $cdata = array();
-  
-  /**
-   * ACL calculation instance for this page.
-   * @var object(Session_ACLPageInfo)
-   */
-  
-  public $perms = false;
-  
-  /**
-   * Protection calculation
-   * @var bool
-   */
-  
-  public $page_protected = false;
-  
-  /**
-   * Wiki mode calculation
-   * @var bool
-   */
-  
-  public $wiki_mode = false;
-  
-  /**
-   * Page conditions. These represent the final decision as to whether an action is allowed or not. They are set to true if ACLs permit AND if
-   * the action "makes sense." (e.g., you can't vote to delete a non-wikimode page.)
-   * @var array
-   */
-  
-  public $conds = array();
-  
-  /**
-   * Constructor.
-   */
-  
-  public function __construct($page_id, $namespace, $revision_id = 0)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $this->page_id = sanitize_page_id($page_id);
-    $this->namespace = $namespace;
-    $this->revision_id = intval($revision_id);
-    
-    // grab the cdata
-    $this->build_cdata();
-    
-    $this->page_protected = $this->cdata['really_protected'] ? true : false;
-    switch($this->cdata['wiki_mode'])
-    {
-      case 0: $this->wiki_mode = false; break;
-      case 1: $this->wiki_mode = true; break;
-      default: case 2: $this->wiki_mode = getConfig('wiki_mode') == 1; break;
-    }
-  }
-  
-  /**
-   * Build the page's cdata.
-   */
-  
-  public function build_cdata()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    static $cdata_cache = array();
-    $pathskey = $paths->get_pathskey($this->page_id, $this->namespace);
-    if ( isset($cdata_cache[$pathskey]) )
-    {
-      $this->cdata = $cdata_cache[$pathskey];
-      $this->exists = $cdata_cache[$pathskey]['page_exists'];
-      $this->title = $cdata_cache[$pathskey]['name'];
-      return null;
-    }
-    
-    $this->exists = false;
-    $ns_char = substr($paths->nslist['Special'], -1);
-    $page_name = $this->namespace == 'Article' ? dirtify_page_id($this->page_id) : "{$this->namespace}{$ns_char}" . dirtify_page_id($this->page_id);
-    $page_name = str_replace('_', ' ', $page_name);
-    $this->title = $page_name;
-    
-    $this->cdata = array(
-      'name' => $page_name,
-      'urlname' => $this->page_id,
-      'namespace' => $this->namespace,
-      'special' => 0,
-      'visible' => 0,
-      'comments_on' => 1,
-      'protected' => 0,
-      'delvotes' => 0,
-      'delvote_ips' => '',
-      'wiki_mode' => 2,
-      'page_exists' => false,
-      'page_format' => getConfig('default_page_format', 'wikitext')
-    );
-    
-    if ( $data_from_db = Namespace_Default::get_cdata_from_db($this->page_id, $this->namespace) )
-    {
-      $this->exists = true;
-      $this->cdata = $data_from_db;
-      $this->cdata['page_exists'] = true;
-      $this->title = $this->cdata['name'];
-    }
-        
-    $this->cdata = Namespace_Default::bake_cdata($this->cdata);
-    
-    $cdata_cache[$pathskey] = $this->cdata;
-  }
-  
-  /**
-   * Pulls the page's actual text from the database.
-   */
-  
-  function fetch_text()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !empty($this->text_cache) )
-    {
-      return $this->text_cache;
-    }
-    
-    if ( $this->revision_id > 0 && is_int($this->revision_id) )
-    {
-    
-      $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
-      if ( !$q )
-      {
-        $this->send_error('Error during SQL query.', true);
-      }
-      if ( $db->numrows() < 1 )
-      {
-        // Compatibility fix for old pages with dots in the page ID
-        if ( strstr($this->page_id, '.2e') )
-        {
-          $db->free_result();
-          $page_id = str_replace('.2e', '.', $this->page_id);
-          $q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
-          if ( !$q )
-          {
-            $this->send_error('Error during SQL query.', true);
-          }
-          if ( $db->numrows() < 1 )
-          {
-            $this->page_exists = false;
-            return 'err_no_text_rows';
-          }
-        }
-        else
-        {
-          $this->page_exists = false;
-          return 'err_no_text_rows';
-        }
-      }
-      else
-      {
-        $row = $db->fetchrow();
-      }
-      
-      $db->free_result();
-      
-    }
-    else
-    {
-      $q = $db->sql_query('SELECT t.page_text, t.char_tag, l.time_id FROM '.table_prefix."page_text AS t\n"
-                        . "  LEFT JOIN " . table_prefix . "logs AS l\n"
-                        . "    ON ( l.page_id = t.page_id AND l.namespace = t.namespace )\n"
-                        . "  WHERE t.page_id='$this->page_id' AND t.namespace='$this->namespace'\n"
-                        . "  ORDER BY l.time_id DESC LIMIT 1;");
-      if ( !$q )
-      {
-        $this->send_error('Error during SQL query.', true);
-      }
-      if ( $db->numrows() < 1 )
-      {
-        // Compatibility fix for old pages with dots in the page ID
-        if ( strstr($this->page_id, '.2e') )
-        {
-          $db->free_result();
-          $page_id = str_replace('.2e', '.', $this->page_id);
-          $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\';');
-          if ( !$q )
-          {
-            $this->send_error('Error during SQL query.', true);
-          }
-          if ( $db->numrows() < 1 )
-          {
-            $this->page_exists = false;
-            return 'err_no_text_rows';
-          }
-        }
-        else
-        {
-          $this->page_exists = false;
-          return 'err_no_text_rows';
-        }
-      }
-      
-      $row = $db->fetchrow();
-      $db->free_result();
-      
-    }
-    
-    if ( !empty($row['char_tag']) )
-    {
-      // This page text entry uses the old text-escaping format
-      $from = array(
-          "{APOS:{$row['char_tag']}}",
-          "{QUOT:{$row['char_tag']}}",
-          "{SLASH:{$row['char_tag']}}"
-        );
-      $to = array("'", '"',  '\\');
-      $row['page_text'] = str_replace($from, $to, $row['page_text']);
-    }
-    
-    $this->text_cache = $row['page_text'];
-    
-    if ( isset($row['time_id']) )
-    {
-      $this->revision_time = intval($row['time_id']);
-    }
-    
-    return $row['page_text'];
-  }
-  
-  /**
-   * Send the page.
-   */
-  
-  public function send()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $output;
-    
-    $output->add_before_footer($this->display_categories());
-    
-    if ( $this->exists )
-      $this->send_from_db();
-    else
-    {
-      // This is the DEPRECATED way to extend namespaces. It's left in only for compatibility with older plugins.
-      ob_start();
-      $code = $plugins->setHook('page_not_found');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      $c = ob_get_contents();
-      if ( !empty($c) )
-      {
-        ob_end_clean();
-        echo $c;
-      }
-      else
-      {
-        $output->header();
-        $this->error_404();
-        $output->footer();
-      }
-    }
-  }
-  
-  /**
-   * Get a redirect, if there is one.
-   * @return mixed Array: Page ID and namespace, associative; bool: false (no redirect)
-   */
-  
-  public function get_redirect()
-  {
-    $text = $this->fetch_text();
-    if ( preg_match('/^#redirect \[\[([^\]]+?)\]\]/i', $text, $match ) )
-    {
-      list($page_id, $namespace) = RenderMan::strToPageID($match[1]);
-      return array(
-          'page_id' => $page_id,
-          'namespace' => $namespace
-        );
-    }
-    return false;
-  }
-   
-  /**
-   * The "real" send-the-page function. The reason for this is so other namespaces can re-use the code
-   * to fetch the page from the DB while being able to install their own wrappers.
-   */
-  
-  public function send_from_db($incl_inner_headers = true, $send_headers = true)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $output;
-    
-    $text = $this->fetch_text();
-    
-    profiler_log("Namespace [$this->namespace, $this->page_id]: pulled text from DB");
-    
-    $text = preg_replace('/([\s]*)__NOBREADCRUMBS__([\s]*)/', '', $text);
-    $text = preg_replace('/([\s]*)__NOTOC__([\s]*)/', '', $text);
-    $text = preg_replace('/^#redirect \[\[.+?\]\]\s*/i', '', $text);
-    
-    if ( $send_headers )
-    {
-      $output->set_title($this->title);
-      $output->header();
-    }
-    $this->do_breadcrumbs();
-    
-    if ( $incl_inner_headers )
-    {
-      if ( !$this->perms )
-        $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
-      
-      if ( $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0)
-      {
-        $delvote_ips = unserialize($this->cdata['delvote_ips']);
-        $hr = htmlspecialchars(implode(', ', $delvote_ips['u']));
-        
-        $string_id = ( $this->cdata['delvotes'] == 1 ) ? 'delvote_lbl_votes_one' : 'delvote_lbl_votes_plural';
-        $string = $lang->get($string_id, array('num_users' => $this->cdata['delvotes']));
-        
-        echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;" id="mdgDeleteVoteNoticeBox">
-                <b>' . $lang->get('etc_lbl_notice') . '</b> ' . $string . '<br />
-                <b>' . $lang->get('delvote_lbl_users_that_voted') . '</b> ' . $hr . '<br />
-                <a href="'.makeUrl($paths->page, 'do=deletepage').'" onclick="ajaxDeletePage(); return false;">' . $lang->get('delvote_btn_deletepage') . '</a>  |  <a href="'.makeUrl($paths->page, 'do=resetvotes').'" onclick="ajaxResetDelVotes(); return false;">' . $lang->get('delvote_btn_resetvotes') . '</a>
-              </div>';
-      }
-    }
-    
-    if ( $this->revision_id )
-    {
-      echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;">
-              <b>' . $lang->get('page_msg_archived_title') . '</b><br />
-              ' . $lang->get('page_msg_archived_body', array(
-                  'archive_date' => enano_date(ED_DATE, $this->revision_time),
-                  'archive_time' => enano_date(ED_TIME, $this->revision_time),
-                  'current_link' => makeUrlNS($this->namespace, $this->page_id),
-                  'restore_link' => makeUrlNS($this->namespace, $this->page_id, 'do=edit&amp;revid='.$this->revision_id),
-                  'restore_onclick' => 'ajaxEditor(\''.$this->revision_id.'\'); return false;',
-                )) . '
-            </div>';
-      $q = $db->sql_query('SELECT page_format FROM ' . table_prefix . "logs WHERE log_id = {$this->revision_id};");
-      if ( !$q )
-        $db->_die();
-      
-      list($page_format) = $db->fetchrow_num();
-      $db->free_result();
-    }
-    else
-    {
-      $page_format = $this->cdata['page_format'];
-    }
-    
-    $code = $plugins->setHook('pageprocess_render_head');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    $prof_contentevent = profiler_log("Namespace [$this->namespace, $this->page_id]: headers and preprocessing done - about to send content");
-    
-    if ( $incl_inner_headers )
-    {
-      if ( $page_format === 'wikitext' )
-      {
-        $text = '?>' . RenderMan::render($text);
-      }
-      else
-      {
-        // Page format is XHTML. This means we want to disable functionality that MCE takes care of, while still retaining
-        // the ability to wikilink, the ability to use images, etc. Basically, RENDER_INLINEONLY disables all behavior in
-        // the rendering engine/Text_Wiki that conflicts with MCE.
-        $text = '?>' . RenderMan::render($text, RENDER_INLINE);
-      }
-    }
-    else
-    {
-      $text = '?>' . $text;
-      $text = preg_replace('/<nowiki>(.*?)<\/nowiki>/s', '\\1', $text);
-    }
-    
-    eval ( $text );
-    
-    profiler_log("Namespace [$this->namespace, $this->page_id]: content sent", true, $prof_contentevent);
-    
-    $code = $plugins->setHook('pageprocess_render_tail');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    if ( $incl_inner_headers )
-    {
-      display_page_footers();
-    }
-    
-    profiler_log("Namespace [$this->namespace, $this->page_id]: sent footers");
-    
-    if ( $send_headers )
-      $output->footer();
-  }
-  
-  /**
-   * Echoes out breadcrumb data, if appropriate.
-   * @access private
-   */
-  
-  function do_breadcrumbs()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if ( strpos($this->text_cache, '__NOBREADCRUMBS__') !== false )
-      return false;
-    
-    $mode = getConfig('breadcrumb_mode');
-    
-    if ( $mode == 'never' )
-      // Breadcrumbs are disabled
-      return true;
-      
-    // Minimum depth for breadcrumb display
-    $threshold = ( $mode == 'always' ) ? 0 : 1;
-    
-    $breadcrumb_data = explode('/', $this->page_id);
-    if ( count($breadcrumb_data) > $threshold )
-    {
-      // If we're not on a subpage of the main page, add "Home" to the list
-      $show_home = false;
-      if ( $mode == 'always' )
-      {
-        $show_home = true;
-      }
-      echo '<!-- Start breadcrumbs -->
-            <div class="breadcrumbs">
-              ';
-      if ( $show_home )
-      {
-        // Display the "home" link first.
-        $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
-        if ( $pathskey !== get_main_page() )
-          echo '<a href="' . makeUrl(get_main_page(), false, true) . '">';
-        echo $lang->get('onpage_btn_breadcrumbs_home');
-        if ( $pathskey !== get_main_page() )
-          echo '</a>';
-      }
-      foreach ( $breadcrumb_data as $i => $crumb )
-      {
-        $cumulative = implode('/', array_slice($breadcrumb_data, 0, ( $i + 1 )));
-        if ( $show_home && $cumulative === get_main_page() )
-          continue;
-        if ( $show_home || $i > 0 )
-          echo ' &raquo; ';
-        $title = ( isPage($cumulative) ) ? get_page_title($cumulative) : get_page_title($crumb);
-        if ( $i + 1 == count($breadcrumb_data) )
-        {
-          echo htmlspecialchars($title);
-        }
-        else
-        {
-          $exists = ( isPage($cumulative) ) ? '' : ' class="wikilink-nonexistent"';
-          echo '<a href="' . makeUrl($cumulative, false, true) . '"' . $exists . '>' . htmlspecialchars($title) . '</a>';
-        }
-      }
-      echo '</div>
-            <!-- End breadcrumbs -->
-            ';
-    }
-  }
-  
-  public function error_404()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang, $output;
-    
-    $userpage = $this->namespace == 'User';
-    
-    @header('HTTP/1.1 404 Not Found');
-    
-    $msg = ( $pp = $paths->sysmsg('Page_not_found') ) ? $pp : '{STANDARD404}';
-    
-    $standard_404 = '';
-    
-    if ( $userpage )
-    {
-      $standard_404 .= '<h3>' . $lang->get('page_msg_404_title_userpage') . '</h3>
-             <p>' . $lang->get('page_msg_404_body_userpage');
-    }
-    else
-    {
-      $standard_404 .= '<h3>' . $lang->get('page_msg_404_title') . '</h3>
-             <p>' . $lang->get('page_msg_404_body');
-    }
-    if ( $session->get_permissions('create_page') )
-    {
-      $standard_404 .= ' ' . $lang->get('page_msg_404_create', array(
-          'create_flags' => 'href="'.makeUrlNS($this->namespace, $this->page_id, 'do=edit', true).'" onclick="ajaxEditor(); return false;"',
-          'mainpage_link' => makeUrl(get_main_page(), false, true)
-        ));
-    }
-    else
-    {
-      $standard_404 .= ' ' . $lang->get('page_msg_404_gohome', array(
-          'mainpage_link' => makeUrl(get_main_page(), false, true)
-        ));
-    }
-    $standard_404 .= '</p>';
-    if ( $session->get_permissions('history_rollback') )
-    {
-      $e = $db->sql_query('SELECT * FROM ' . table_prefix . 'logs WHERE action=\'delete\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' ORDER BY time_id DESC;');
-      if ( !$e )
-      {
-        $db->_die('The deletion log could not be selected.');
-      }
-      if ( $db->numrows() > 0 )
-      {
-        $r = $db->fetchrow();
-        $standard_404 .= '<p>' . $lang->get('page_msg_404_was_deleted', array(
-                  'delete_time' => enano_date(ED_DATE | ED_TIME, $r['time_id']),
-                  'delete_reason' => htmlspecialchars($r['edit_summary']),
-                  'rollback_flags' => 'href="'.makeUrl($paths->page, 'do=rollback&amp;id='.$r['log_id']).'" onclick="ajaxRollback(\''.$r['log_id'].'\'); return false;"'
-                ))
-              . '</p>';
-        if ( $session->user_level >= USER_LEVEL_ADMIN )
-        {
-          $standard_404 .= '<p>' . $lang->get('page_msg_404_admin_opts', array(
-                    'detag_link' => makeUrl($paths->page, 'do=detag', true)
-                  ))
-                . '</p>';
-        }
-      }
-      $db->free_result();
-    }
-    $standard_404 .= '<p>
-            ' . $lang->get('page_msg_404_http_response') . '
-          </p>';
-          
-    $parser = $template->makeParserText($msg);
-    $parser->assign_vars(array(
-        'STANDARD404' => $standard_404
-      ));
-    
-    $msg = RenderMan::render($parser->run());
-    eval( '?>' . $msg );
-  }
-  
-  /**
-   * Display the categories a page is in. If the current page is a category, its contents will also be printed.
-   */
-  
-  function display_categories()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $html = '';
-    
-    if ( $this->namespace == 'Category' )
-    {
-      // Show member pages and subcategories
-      $q = $db->sql_query('SELECT p.urlname, p.namespace, p.name, p.namespace=\'Category\' AS is_category FROM '.table_prefix.'categories AS c
-                             LEFT JOIN '.table_prefix.'pages AS p
-                               ON ( p.urlname = c.page_id AND p.namespace = c.namespace )
-                             WHERE c.category_id=\'' . $db->escape($this->page_id) . '\'
-                             ORDER BY is_category DESC, p.name ASC;');
-      if ( !$q )
-      {
-        $db->_die();
-      }
-      $html .= '<h3>' . $lang->get('onpage_cat_heading_subcategories') . '</h3>';
-      $html .= '<div class="tblholder">';
-      $html .= '<table border="0" cellspacing="1" cellpadding="4">';
-      $html .= '<tr>';
-      $ticker = 0;
-      $counter = 0;
-      $switched = false;
-      $class  = 'row1';
-      while ( $row = $db->fetchrow($q) )
-      {
-        if ( $row['is_category'] == 0 && !$switched )
-        {
-          if ( $counter > 0 )
-          {
-            // Fill-in
-            while ( $ticker < 3 )
-            {
-              $ticker++;
-              $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
-            }
-          }
-          else
-          {
-            $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
-          }
-          $html .= '</tr></table></div>' . "\n\n";
-          $html .= '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
-          $html .= '<div class="tblholder">';
-          $html .= '<table border="0" cellspacing="1" cellpadding="4">';
-          $html .= '<tr>';
-          $counter = 0;
-          $ticker = -1;
-          $switched = true;
-        }
-        $counter++;
-        $ticker++;
-        if ( $ticker == 3 )
-        {
-          $html .= '</tr><tr>';
-          $ticker = 0;
-          $class = ( $class == 'row3' ) ? 'row1' : 'row3';
-        }
-        $html .= "<td class=\"{$class}\" style=\"width: 33.3%;\">"; // " to workaround stupid jEdit bug
-        
-        $link = makeUrlNS($row['namespace'], sanitize_page_id($row['urlname']));
-        $html .= '<a href="' . $link . '"';
-        $key = $paths->nslist[$row['namespace']] . sanitize_page_id($row['urlname']);
-        if ( !isPage( $key ) )
-        {
-          $html .= ' class="wikilink-nonexistent"';
-        }
-        $html .= '>';
-        $title = get_page_title_ns($row['urlname'], $row['namespace']);
-        $html .= htmlspecialchars($title);
-        $html .= '</a>';
-        
-        $html .= "</td>";
-      }
-      if ( !$switched )
-      {
-        if ( $counter > 0 )
-        {
-          // Fill-in
-          while ( $ticker < 2 )
-          {
-            $ticker++;
-            $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
-          }
-        }
-        else
-        {
-          $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
-        }
-        $html .= '</tr></table></div>' . "\n\n";
-        $html .= '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
-        $html .= '<div class="tblholder">';
-        $html .= '<table border="0" cellspacing="1" cellpadding="4">';
-        $html .= '<tr>';
-        $counter = 0;
-        $ticker = 0;
-        $switched = true;
-      }
-      if ( $counter > 0 )
-      {
-        // Fill-in
-        while ( $ticker < 2 )
-        {
-          $ticker++;
-          $html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
-        }
-      }
-      else
-      {
-        $html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_pages') . '</td>';
-      }
-      $html .= '</tr></table></div>' . "\n\n";
-    }
-    
-    if ( $this->namespace != 'Special' && $this->namespace != 'Admin' )
-    {
-      $html .= '<div class="mdg-comment" style="margin: 10px 0 0 0;" id="category_box_wrapper">';
-      $html .= '<div style="float: right;">';
-      $html .= '(<a href="#" onclick="ajaxCatToTag(); return false;">' . $lang->get('tags_catbox_link') . '</a>)';
-      $html .= '</div>';
-      $html .= '<div id="mdgCatBox">' . $lang->get('catedit_catbox_lbl_categories') . ' ';
-      
-      $q = $db->sql_query('SELECT category_id FROM ' . table_prefix . "categories WHERE page_id = '$this->page_id' AND namespace = '$this->namespace';");
-      if ( !$q )
-        $db->_die();
-      
-      if ( $row = $db->fetchrow() )
-      {
-        $list = array();
-        do
-        {
-          $cid = sanitize_page_id($row['category_id']);
-          $title = get_page_title_ns($cid, 'Category');
-          $link = makeUrlNS('Category', $cid);
-          $list[] = '<a href="' . $link . '">' . htmlspecialchars($title) . '</a>';
-        }
-        while ( $row = $db->fetchrow($q) );
-        $html .= implode(', ', $list);
-      }
-      else
-      {
-        $html .= $lang->get('catedit_catbox_lbl_uncategorized');
-      }
-      
-      $can_edit = ( $session->get_permissions('edit_cat') && ( !$paths->page_protected || $session->get_permissions('even_when_protected') ) );
-      if ( $can_edit )
-      {
-        $edit_link = '<a href="' . makeUrl($paths->page, 'do=catedit', true) . '" onclick="ajaxCatEdit(); return false;">' . $lang->get('catedit_catbox_link_edit') . '</a>';
-        $html .= ' [ ' . $edit_link . ' ]';
-      }
-      
-      $html .= '</div></div>';
-    }
-    return $html;
-  }
-  
-  /**
-   * Pull in switches as to whether a specific toolbar button should be used or not. This sets things up according to the current page being displayed.
-   * @return array Associative
-   */
-  
-  function set_conds()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !$this->perms )
-      $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
-    
-    if ( !$this->perms )
-    {
-      // We're trying to send a page WAY too early (session hasn't been started yet), such as for a redirect. Send a default set of conds because
-      // there's NO way to get permissions to determine anything otherwise. Yes, starting $session here might be dangerous.
-      $this->conds = array(
-          'article' => true,
-          'comments' => false,
-          'edit' => false,
-          'viewsource' => false,
-          'history' => false,
-          'rename' => false,
-          'delvote' => false,
-          'resetvotes' => false,
-          'delete' => false,
-          'printable' => false,
-          'protect' => false,
-          'setwikimode' => false,
-          'clearlogs' => false,
-          'password' => false,
-          'acledit' => false,
-          'adminpage' => false
-        );
-      return $this->conds;
-    }
-    
-    // die('have perms: <pre>' . print_r($this->perms, true) . "\n---------------------------------\nBacktrace:\n" . enano_debug_print_backtrace(true));
-    
-    $enforce_protection = ( $this->page_protected && ( ( $session->check_acl_scope('even_when_protected', $this->namespace) && !$this->perms->get_permissions('even_when_protected') ) || !$session->check_acl_scope('even_when_protected', $this->namespace) ) );
-    
-    $conds = array();
-    
-    // Article: always show
-    $conds['article'] = true;
-    
-    // Discussion: Show if comments are enabled on the site, and if comments are on for this page.
-    $conds['comments'] = $this->perms->get_permissions('read') && getConfig('enable_comments', '1')=='1' && $this->cdata['comments_on'] == 1;
-    
-    // Edit: Show if we have permission to edit the page, and if we don't have protection in effect
-    $conds['edit'] = $this->perms->get_permissions('read') && $session->check_acl_scope('edit_page', $this->namespace) && $this->perms->get_permissions('edit_page') && !$enforce_protection;
-    
-    // View source: Show if we have permission to view source and either ACLs prohibit editing or protection is in effect
-    $conds['viewsource'] = $session->check_acl_scope('view_source', $this->namespace) && $this->perms->get_permissions('view_source') && ( !$this->perms->get_permissions('edit_page') || $enforce_protection ) && $this->namespace != 'API';
-    
-    // History: Show if we have permission to see history and if the page exists
-    $conds['history'] = $session->check_acl_scope('history_view', $this->namespace) && $this->exists && $this->perms->get_permissions('history_view');
-    
-    // Rename: Show if the page exists, if we have permission to rename, and if protection isn't in effect
-    $conds['rename'] = $session->check_acl_scope('rename', $this->namespace) && $this->exists && $this->perms->get_permissions('rename') && !$enforce_protection;
-    
-    // Vote-to-delete: Show if we have Wiki Mode on, if we have permission to vote for deletion, and if the page exists (can't vote to delete a nonexistent page)
-    $conds['delvote'] = $this->wiki_mode && $session->check_acl_scope('vote_delete', $this->namespace) && $this->perms->get_permissions('vote_delete') && $this->exists;
-    
-    // Reset votes: Show if we have Wiki Mode on, if we have permission to reset votes, if the page exists, and if there's at least one vote
-    $conds['resetvotes'] = $session->check_acl_scope('vote_reset', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0;
-    
-    // Delete page: Show if the page exists and if we have permission to delete it
-    $conds['delete'] = $session->check_acl_scope('delete_page', $this->namespace) && $this->exists && $this->perms->get_permissions('delete_page');
-    
-    // Printable view: Show if the page exists
-    $conds['printable'] = $this->exists;
-    
-    // Protect: Show if we have Wiki Mode on, if the page exists, and if we have permission to protect the page.
-    $conds['protect'] = $session->check_acl_scope('protect', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('protect');
-    
-    // Set Wiki Mode: Show if the page exists and if we have permission to set wiki mode
-    $conds['setwikimode'] = $session->check_acl_scope('set_wiki_mode', $this->namespace) && $this->exists && $this->perms->get_permissions('set_wiki_mode');
-    
-    // Clear logs: Show if we have permission to clear logs
-    $conds['clearlogs'] = $session->check_acl_scope('clear_logs', $this->namespace) && $this->perms->get_permissions('clear_logs');
-    
-    // Set password: a little bit complicated. If there's a password, check for password_reset; else, check for password_set.
-    $conds['password'] = empty($this->cdata['password']) ?
-                           $session->check_acl_scope('password_set', $this->namespace) && $this->perms->get_permissions('password_set') :
-                           $session->check_acl_scope('password_reset', $this->namespace) && $this->perms->get_permissions('password_reset');
-    
-    // Edit ACLs: Show if this is a non-Enano page that's calling the Enano API and (a) if we have permissions to edit ACLs or (b) we're an admin AND ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL is on
-    $conds['acledit'] = $this->namespace != 'API' && $session->check_acl_scope('edit_acl', $this->namespace) && ( $this->perms->get_permissions('edit_acl') || ( defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL') &&  $session->user_level >= USER_LEVEL_ADMIN ) );
-    
-    // Admin page: Show if the page exists and if we're an admin
-    $conds['adminpage'] = $session->user_level >= USER_LEVEL_ADMIN && $this->exists;
-    
-    // Allow plugins to change stuff
-    $code = $plugins->setHook('page_conds_set');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    $this->conds = $conds;
-  }
-  
-  /**
-   * Return page conditions
-   * @return array
-   */
-  
-  public function get_conds()
-  {
-    if ( empty($this->conds) )
-      $this->set_conds();
-    
-    return $this->conds;
-  }
-  
-  /**
-   * Just tell us if the current page exists or not.
-   * @return bool
-   */
-   
-  public function exists()
-  {
-    return $this->exists;
-  }
-  
-  /**
-   * Return cdata
-   * @return array
-   */
-  
-  public function get_cdata()
-  {
-    return $this->cdata;
-  }
-  
-  /**
-   * Bake, or finalize the processing of, a cdata array.
-   * @static
-   * @access public
-   */
-  
-  public static function bake_cdata($cdata)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // urlname_nons is the actual page_id.
-    $cdata['urlname_nons'] = $cdata['urlname'];
-    if ( isset($paths->nslist[ $cdata['namespace'] ]) )
-    {
-      $cdata['urlname'] = $paths->nslist[ $cdata['namespace'] ] . $cdata['urlname'];
-    }
-    else
-    {
-      $ns_char = substr($paths->nslist['Special'], -1);
-      $cdata['urlname'] = $cdata['namespace'] . $ns_char . $cdata['urlname'];
-    }
-    
-    // add missing keys
-    $defaults = array(
-      'special' => 0,
-      'visible' => 0,
-      'comments_on' => 1,
-      'protected' => 0,
-      'delvotes' => 0,
-      'delvote_ips' => serialize(array()),
-      'wiki_mode' => 2,
-      'page_format' => getConfig('default_page_format', 'wikitext')
-    );
-    foreach ( $defaults as $key => $value )
-    {
-      if ( !isset($cdata[$key]) )
-        $cdata[$key] = $value;
-    }
-    
-    // fix up deletion votes
-    if ( empty($cdata['delvotes']) )
-      $cdata['delvotes'] = 0;
-    
-    // fix up deletion vote IP list
-    if ( empty($cdata['delvote_ips']) )
-      $cdata['delvote_ips'] = serialize(array());
-    
-    // calculate wiki mode
-    $cdata['really_wiki_mode'] = ( $cdata['wiki_mode'] == 1 || ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode', 0) == 1 ) );
-    
-    // calculate protection
-    $cdata['really_protected'] = ( $cdata['protected'] > 0 );
-    if ( $cdata['protected'] == 2 )
-    {
-      $cdata['really_protected'] = !$session->user_logged_in || ( $session->user_logged_in && $session->reg_time + 86400*4 > time() );
-    }
-    
-    return $cdata;
-  }
-  
-  /**
-   * Grabs raw (unbaked) cdata from the database, caching if possible.
-   * @param string Page ID
-   * @param string Namespace.
-   * @static
-   */
-  
-  public static function get_cdata_from_db($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    static $cache = array();
-    
-    $pathskey = $paths->get_pathskey($page_id, $namespace);
-    if ( isset($cache[$pathskey]) )
-      return $cache[$pathskey];
-    
-    $page_id_db = $db->escape($page_id);
-    $namespace_db = $db->escape($namespace);
-    
-    $q = $db->sql_query('SELECT p.*'
-                      . '    FROM ' . table_prefix . "pages AS p\n"
-                      . "  WHERE p.urlname = '$page_id_db' AND p.namespace = '$namespace_db'\n"
-                      . "    GROUP BY p.urlname, p.name, p.namespace, p.page_order, p.special, p.visible, p.protected, p.wiki_mode, p.comments_on, p.delvotes, p.delvote_ips, p.page_format, p.password;");
-    
-    if ( !$q )
-      $db->_die();
-    
-    if ( $db->numrows() < 1 )
-    {
-      $db->free_result();
-      $cache[$pathskey] = false;
-      return false;
-    }
-    
-    $row = $db->fetchrow();
-    
-    // Get comment counts
-    // FIXME: Apparently there's a bit of recursion in here. Fetching permissions depends on this cdata function.
-    // Perhaps we should eliminate session's dependency on cdata? (What is it used for?)
-    $q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';");
-    // yay parallel assignment
-    $row['comments_approved'] = $row['comments_unapproved'] = $row['comments_spam'] = 0;
-    while ( $commentrow = $db->fetchrow() )
-      switch($commentrow['approved'])
-      {
-        case COMMENT_APPROVED:
-        default:
-          $row['comments_approved']++;
-          break;
-        case COMMENT_UNAPPROVED:
-          $row['comments_unapproved']++;
-          break;
-        case COMMENT_SPAM:
-          $row['comments_spam']++;
-          break;
-      }
-    
-    $cache[$pathskey] = $row;
-    return $row;
-  }
+	/**
+ 	* Page ID
+ 	* @var string
+ 	*/
+	
+	public $page_id;
+	
+	/**
+ 	* Namespace
+ 	* @var string
+ 	*/
+	
+	public $namespace;
+	
+	/**
+ 	* Local copy of the page text
+ 	*/
+	
+	public $text_cache;
+	
+	/**
+ 	* Revision ID to send. If 0, the latest revision.
+ 	* @var int
+ 	*/
+	
+	public $revision_id = 0;
+	
+	/**
+ 	* Tracks whether the page exists
+ 	* @var bool
+ 	*/
+	
+	public $exists = false;
+	
+	/**
+ 	* Page title
+ 	* @var string
+ 	*/
+	
+	public $title = '';
+	
+	/**
+ 	* PathManager info array ("cdata") for this page. (The one with urlname, name, namespace, delvotes, delvote_ips, protected, visible, etc.)
+ 	* @var array
+ 	*/
+	
+	public $cdata = array();
+	
+	/**
+ 	* ACL calculation instance for this page.
+ 	* @var object(Session_ACLPageInfo)
+ 	*/
+	
+	public $perms = false;
+	
+	/**
+ 	* Protection calculation
+ 	* @var bool
+ 	*/
+	
+	public $page_protected = false;
+	
+	/**
+ 	* Wiki mode calculation
+ 	* @var bool
+ 	*/
+	
+	public $wiki_mode = false;
+	
+	/**
+ 	* Page conditions. These represent the final decision as to whether an action is allowed or not. They are set to true if ACLs permit AND if
+ 	* the action "makes sense." (e.g., you can't vote to delete a non-wikimode page.)
+ 	* @var array
+ 	*/
+	
+	public $conds = array();
+	
+	/**
+ 	* Constructor.
+ 	*/
+	
+	public function __construct($page_id, $namespace, $revision_id = 0)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$this->page_id = sanitize_page_id($page_id);
+		$this->namespace = $namespace;
+		$this->revision_id = intval($revision_id);
+		
+		// grab the cdata
+		$this->build_cdata();
+		
+		$this->page_protected = $this->cdata['really_protected'] ? true : false;
+		switch($this->cdata['wiki_mode'])
+		{
+			case 0: $this->wiki_mode = false; break;
+			case 1: $this->wiki_mode = true; break;
+			default: case 2: $this->wiki_mode = getConfig('wiki_mode') == 1; break;
+		}
+	}
+	
+	/**
+ 	* Build the page's cdata.
+ 	*/
+	
+	public function build_cdata()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		static $cdata_cache = array();
+		$pathskey = $paths->get_pathskey($this->page_id, $this->namespace);
+		if ( isset($cdata_cache[$pathskey]) )
+		{
+			$this->cdata = $cdata_cache[$pathskey];
+			$this->exists = $cdata_cache[$pathskey]['page_exists'];
+			$this->title = $cdata_cache[$pathskey]['name'];
+			return null;
+		}
+		
+		$this->exists = false;
+		$ns_char = substr($paths->nslist['Special'], -1);
+		$page_name = $this->namespace == 'Article' ? dirtify_page_id($this->page_id) : "{$this->namespace}{$ns_char}" . dirtify_page_id($this->page_id);
+		$page_name = str_replace('_', ' ', $page_name);
+		$this->title = $page_name;
+		
+		$this->cdata = array(
+			'name' => $page_name,
+			'urlname' => $this->page_id,
+			'namespace' => $this->namespace,
+			'special' => 0,
+			'visible' => 0,
+			'comments_on' => 1,
+			'protected' => 0,
+			'delvotes' => 0,
+			'delvote_ips' => '',
+			'wiki_mode' => 2,
+			'page_exists' => false,
+			'page_format' => getConfig('default_page_format', 'wikitext')
+		);
+		
+		if ( $data_from_db = Namespace_Default::get_cdata_from_db($this->page_id, $this->namespace) )
+		{
+			$this->exists = true;
+			$this->cdata = $data_from_db;
+			$this->cdata['page_exists'] = true;
+			$this->title = $this->cdata['name'];
+		}
+				
+		$this->cdata = Namespace_Default::bake_cdata($this->cdata);
+		
+		$cdata_cache[$pathskey] = $this->cdata;
+	}
+	
+	/**
+ 	* Pulls the page's actual text from the database.
+ 	*/
+	
+	function fetch_text()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !empty($this->text_cache) )
+		{
+			return $this->text_cache;
+		}
+		
+		if ( $this->revision_id > 0 && is_int($this->revision_id) )
+		{
+		
+			$q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
+			if ( !$q )
+			{
+				$this->send_error('Error during SQL query.', true);
+			}
+			if ( $db->numrows() < 1 )
+			{
+				// Compatibility fix for old pages with dots in the page ID
+				if ( strstr($this->page_id, '.2e') )
+				{
+					$db->free_result();
+					$page_id = str_replace('.2e', '.', $this->page_id);
+					$q = $db->sql_query('SELECT page_text, char_tag, time_id FROM '.table_prefix.'logs WHERE log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\' AND log_id=' . $this->revision_id . ';');
+					if ( !$q )
+					{
+						$this->send_error('Error during SQL query.', true);
+					}
+					if ( $db->numrows() < 1 )
+					{
+						$this->page_exists = false;
+						return 'err_no_text_rows';
+					}
+				}
+				else
+				{
+					$this->page_exists = false;
+					return 'err_no_text_rows';
+				}
+			}
+			else
+			{
+				$row = $db->fetchrow();
+			}
+			
+			$db->free_result();
+			
+		}
+		else
+		{
+			$q = $db->sql_query('SELECT t.page_text, t.char_tag, l.time_id FROM '.table_prefix."page_text AS t\n"
+												. "  LEFT JOIN " . table_prefix . "logs AS l\n"
+												. "    ON ( l.page_id = t.page_id AND l.namespace = t.namespace )\n"
+												. "  WHERE t.page_id='$this->page_id' AND t.namespace='$this->namespace'\n"
+												. "  ORDER BY l.time_id DESC LIMIT 1;");
+			if ( !$q )
+			{
+				$this->send_error('Error during SQL query.', true);
+			}
+			if ( $db->numrows() < 1 )
+			{
+				// Compatibility fix for old pages with dots in the page ID
+				if ( strstr($this->page_id, '.2e') )
+				{
+					$db->free_result();
+					$page_id = str_replace('.2e', '.', $this->page_id);
+					$q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $this->namespace . '\';');
+					if ( !$q )
+					{
+						$this->send_error('Error during SQL query.', true);
+					}
+					if ( $db->numrows() < 1 )
+					{
+						$this->page_exists = false;
+						return 'err_no_text_rows';
+					}
+				}
+				else
+				{
+					$this->page_exists = false;
+					return 'err_no_text_rows';
+				}
+			}
+			
+			$row = $db->fetchrow();
+			$db->free_result();
+			
+		}
+		
+		if ( !empty($row['char_tag']) )
+		{
+			// This page text entry uses the old text-escaping format
+			$from = array(
+					"{APOS:{$row['char_tag']}}",
+					"{QUOT:{$row['char_tag']}}",
+					"{SLASH:{$row['char_tag']}}"
+				);
+			$to = array("'", '"',  '\\');
+			$row['page_text'] = str_replace($from, $to, $row['page_text']);
+		}
+		
+		$this->text_cache = $row['page_text'];
+		
+		if ( isset($row['time_id']) )
+		{
+			$this->revision_time = intval($row['time_id']);
+		}
+		
+		return $row['page_text'];
+	}
+	
+	/**
+ 	* Send the page.
+ 	*/
+	
+	public function send()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $output;
+		
+		$output->add_before_footer($this->display_categories());
+		
+		if ( $this->exists )
+			$this->send_from_db();
+		else
+		{
+			// This is the DEPRECATED way to extend namespaces. It's left in only for compatibility with older plugins.
+			ob_start();
+			$code = $plugins->setHook('page_not_found');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			$c = ob_get_contents();
+			if ( !empty($c) )
+			{
+				ob_end_clean();
+				echo $c;
+			}
+			else
+			{
+				$output->header();
+				$this->error_404();
+				$output->footer();
+			}
+		}
+	}
+	
+	/**
+ 	* Get a redirect, if there is one.
+ 	* @return mixed Array: Page ID and namespace, associative; bool: false (no redirect)
+ 	*/
+	
+	public function get_redirect()
+	{
+		$text = $this->fetch_text();
+		if ( preg_match('/^#redirect \[\[([^\]]+?)\]\]/i', $text, $match ) )
+		{
+			list($page_id, $namespace) = RenderMan::strToPageID($match[1]);
+			return array(
+					'page_id' => $page_id,
+					'namespace' => $namespace
+				);
+		}
+		return false;
+	}
+ 	
+	/**
+ 	* The "real" send-the-page function. The reason for this is so other namespaces can re-use the code
+ 	* to fetch the page from the DB while being able to install their own wrappers.
+ 	*/
+	
+	public function send_from_db($incl_inner_headers = true, $send_headers = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $output;
+		
+		$text = $this->fetch_text();
+		
+		profiler_log("Namespace [$this->namespace, $this->page_id]: pulled text from DB");
+		
+		$text = preg_replace('/([\s]*)__NOBREADCRUMBS__([\s]*)/', '', $text);
+		$text = preg_replace('/([\s]*)__NOTOC__([\s]*)/', '', $text);
+		$text = preg_replace('/^#redirect \[\[.+?\]\]\s*/i', '', $text);
+		
+		if ( $send_headers )
+		{
+			$output->set_title($this->title);
+			$output->header();
+		}
+		$this->do_breadcrumbs();
+		
+		if ( $incl_inner_headers )
+		{
+			if ( !$this->perms )
+				$this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
+			
+			if ( $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0)
+			{
+				$delvote_ips = unserialize($this->cdata['delvote_ips']);
+				$hr = htmlspecialchars(implode(', ', $delvote_ips['u']));
+				
+				$string_id = ( $this->cdata['delvotes'] == 1 ) ? 'delvote_lbl_votes_one' : 'delvote_lbl_votes_plural';
+				$string = $lang->get($string_id, array('num_users' => $this->cdata['delvotes']));
+				
+				echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;" id="mdgDeleteVoteNoticeBox">
+								<b>' . $lang->get('etc_lbl_notice') . '</b> ' . $string . '<br />
+								<b>' . $lang->get('delvote_lbl_users_that_voted') . '</b> ' . $hr . '<br />
+								<a href="'.makeUrl($paths->page, 'do=deletepage').'" onclick="ajaxDeletePage(); return false;">' . $lang->get('delvote_btn_deletepage') . '</a>  |  <a href="'.makeUrl($paths->page, 'do=resetvotes').'" onclick="ajaxResetDelVotes(); return false;">' . $lang->get('delvote_btn_resetvotes') . '</a>
+							</div>';
+			}
+		}
+		
+		if ( $this->revision_id )
+		{
+			echo '<div class="info-box" style="margin-left: 0; margin-top: 5px;">
+							<b>' . $lang->get('page_msg_archived_title') . '</b><br />
+							' . $lang->get('page_msg_archived_body', array(
+									'archive_date' => enano_date(ED_DATE, $this->revision_time),
+									'archive_time' => enano_date(ED_TIME, $this->revision_time),
+									'current_link' => makeUrlNS($this->namespace, $this->page_id),
+									'restore_link' => makeUrlNS($this->namespace, $this->page_id, 'do=edit&amp;revid='.$this->revision_id),
+									'restore_onclick' => 'ajaxEditor(\''.$this->revision_id.'\'); return false;',
+								)) . '
+						</div>';
+			$q = $db->sql_query('SELECT page_format FROM ' . table_prefix . "logs WHERE log_id = {$this->revision_id};");
+			if ( !$q )
+				$db->_die();
+			
+			list($page_format) = $db->fetchrow_num();
+			$db->free_result();
+		}
+		else
+		{
+			$page_format = $this->cdata['page_format'];
+		}
+		
+		$code = $plugins->setHook('pageprocess_render_head');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		$prof_contentevent = profiler_log("Namespace [$this->namespace, $this->page_id]: headers and preprocessing done - about to send content");
+		
+		if ( $incl_inner_headers )
+		{
+			if ( $page_format === 'wikitext' )
+			{
+				$text = '?>' . RenderMan::render($text);
+			}
+			else
+			{
+				// Page format is XHTML. This means we want to disable functionality that MCE takes care of, while still retaining
+				// the ability to wikilink, the ability to use images, etc. Basically, RENDER_INLINEONLY disables all behavior in
+				// the rendering engine/Text_Wiki that conflicts with MCE.
+				$text = '?>' . RenderMan::render($text, RENDER_INLINE);
+			}
+		}
+		else
+		{
+			$text = '?>' . $text;
+			$text = preg_replace('/<nowiki>(.*?)<\/nowiki>/s', '\\1', $text);
+		}
+		
+		eval ( $text );
+		
+		profiler_log("Namespace [$this->namespace, $this->page_id]: content sent", true, $prof_contentevent);
+		
+		$code = $plugins->setHook('pageprocess_render_tail');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		if ( $incl_inner_headers )
+		{
+			display_page_footers();
+		}
+		
+		profiler_log("Namespace [$this->namespace, $this->page_id]: sent footers");
+		
+		if ( $send_headers )
+			$output->footer();
+	}
+	
+	/**
+ 	* Echoes out breadcrumb data, if appropriate.
+ 	* @access private
+ 	*/
+	
+	function do_breadcrumbs()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if ( strpos($this->text_cache, '__NOBREADCRUMBS__') !== false )
+			return false;
+		
+		$mode = getConfig('breadcrumb_mode');
+		
+		if ( $mode == 'never' )
+			// Breadcrumbs are disabled
+			return true;
+			
+		// Minimum depth for breadcrumb display
+		$threshold = ( $mode == 'always' ) ? 0 : 1;
+		
+		$breadcrumb_data = explode('/', $this->page_id);
+		if ( count($breadcrumb_data) > $threshold )
+		{
+			// If we're not on a subpage of the main page, add "Home" to the list
+			$show_home = false;
+			if ( $mode == 'always' )
+			{
+				$show_home = true;
+			}
+			echo '<!-- Start breadcrumbs -->
+						<div class="breadcrumbs">
+							';
+			if ( $show_home )
+			{
+				// Display the "home" link first.
+				$pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
+				if ( $pathskey !== get_main_page() )
+					echo '<a href="' . makeUrl(get_main_page(), false, true) . '">';
+				echo $lang->get('onpage_btn_breadcrumbs_home');
+				if ( $pathskey !== get_main_page() )
+					echo '</a>';
+			}
+			foreach ( $breadcrumb_data as $i => $crumb )
+			{
+				$cumulative = implode('/', array_slice($breadcrumb_data, 0, ( $i + 1 )));
+				if ( $show_home && $cumulative === get_main_page() )
+					continue;
+				if ( $show_home || $i > 0 )
+					echo ' &raquo; ';
+				$title = ( isPage($cumulative) ) ? get_page_title($cumulative) : get_page_title($crumb);
+				if ( $i + 1 == count($breadcrumb_data) )
+				{
+					echo htmlspecialchars($title);
+				}
+				else
+				{
+					$exists = ( isPage($cumulative) ) ? '' : ' class="wikilink-nonexistent"';
+					echo '<a href="' . makeUrl($cumulative, false, true) . '"' . $exists . '>' . htmlspecialchars($title) . '</a>';
+				}
+			}
+			echo '</div>
+						<!-- End breadcrumbs -->
+						';
+		}
+	}
+	
+	public function error_404()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang, $output;
+		
+		$userpage = $this->namespace == 'User';
+		
+		@header('HTTP/1.1 404 Not Found');
+		
+		$msg = ( $pp = $paths->sysmsg('Page_not_found') ) ? $pp : '{STANDARD404}';
+		
+		$standard_404 = '';
+		
+		if ( $userpage )
+		{
+			$standard_404 .= '<h3>' . $lang->get('page_msg_404_title_userpage') . '</h3>
+ 						<p>' . $lang->get('page_msg_404_body_userpage');
+		}
+		else
+		{
+			$standard_404 .= '<h3>' . $lang->get('page_msg_404_title') . '</h3>
+ 						<p>' . $lang->get('page_msg_404_body');
+		}
+		if ( $session->get_permissions('create_page') )
+		{
+			$standard_404 .= ' ' . $lang->get('page_msg_404_create', array(
+					'create_flags' => 'href="'.makeUrlNS($this->namespace, $this->page_id, 'do=edit', true).'" onclick="ajaxEditor(); return false;"',
+					'mainpage_link' => makeUrl(get_main_page(), false, true)
+				));
+		}
+		else
+		{
+			$standard_404 .= ' ' . $lang->get('page_msg_404_gohome', array(
+					'mainpage_link' => makeUrl(get_main_page(), false, true)
+				));
+		}
+		$standard_404 .= '</p>';
+		if ( $session->get_permissions('history_rollback') )
+		{
+			$e = $db->sql_query('SELECT * FROM ' . table_prefix . 'logs WHERE action=\'delete\' AND page_id=\'' . $this->page_id . '\' AND namespace=\'' . $this->namespace . '\' ORDER BY time_id DESC;');
+			if ( !$e )
+			{
+				$db->_die('The deletion log could not be selected.');
+			}
+			if ( $db->numrows() > 0 )
+			{
+				$r = $db->fetchrow();
+				$standard_404 .= '<p>' . $lang->get('page_msg_404_was_deleted', array(
+									'delete_time' => enano_date(ED_DATE | ED_TIME, $r['time_id']),
+									'delete_reason' => htmlspecialchars($r['edit_summary']),
+									'rollback_flags' => 'href="'.makeUrl($paths->page, 'do=rollback&amp;id='.$r['log_id']).'" onclick="ajaxRollback(\''.$r['log_id'].'\'); return false;"'
+								))
+							. '</p>';
+				if ( $session->user_level >= USER_LEVEL_ADMIN )
+				{
+					$standard_404 .= '<p>' . $lang->get('page_msg_404_admin_opts', array(
+										'detag_link' => makeUrl($paths->page, 'do=detag', true)
+									))
+								. '</p>';
+				}
+			}
+			$db->free_result();
+		}
+		$standard_404 .= '<p>
+						' . $lang->get('page_msg_404_http_response') . '
+					</p>';
+					
+		$parser = $template->makeParserText($msg);
+		$parser->assign_vars(array(
+				'STANDARD404' => $standard_404
+			));
+		
+		$msg = RenderMan::render($parser->run());
+		eval( '?>' . $msg );
+	}
+	
+	/**
+ 	* Display the categories a page is in. If the current page is a category, its contents will also be printed.
+ 	*/
+	
+	function display_categories()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$html = '';
+		
+		if ( $this->namespace == 'Category' )
+		{
+			// Show member pages and subcategories
+			$q = $db->sql_query('SELECT p.urlname, p.namespace, p.name, p.namespace=\'Category\' AS is_category FROM '.table_prefix.'categories AS c
+ 														LEFT JOIN '.table_prefix.'pages AS p
+ 															ON ( p.urlname = c.page_id AND p.namespace = c.namespace )
+ 														WHERE c.category_id=\'' . $db->escape($this->page_id) . '\'
+ 														ORDER BY is_category DESC, p.name ASC;');
+			if ( !$q )
+			{
+				$db->_die();
+			}
+			$html .= '<h3>' . $lang->get('onpage_cat_heading_subcategories') . '</h3>';
+			$html .= '<div class="tblholder">';
+			$html .= '<table border="0" cellspacing="1" cellpadding="4">';
+			$html .= '<tr>';
+			$ticker = 0;
+			$counter = 0;
+			$switched = false;
+			$class  = 'row1';
+			while ( $row = $db->fetchrow($q) )
+			{
+				if ( $row['is_category'] == 0 && !$switched )
+				{
+					if ( $counter > 0 )
+					{
+						// Fill-in
+						while ( $ticker < 3 )
+						{
+							$ticker++;
+							$html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
+						}
+					}
+					else
+					{
+						$html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
+					}
+					$html .= '</tr></table></div>' . "\n\n";
+					$html .= '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
+					$html .= '<div class="tblholder">';
+					$html .= '<table border="0" cellspacing="1" cellpadding="4">';
+					$html .= '<tr>';
+					$counter = 0;
+					$ticker = -1;
+					$switched = true;
+				}
+				$counter++;
+				$ticker++;
+				if ( $ticker == 3 )
+				{
+					$html .= '</tr><tr>';
+					$ticker = 0;
+					$class = ( $class == 'row3' ) ? 'row1' : 'row3';
+				}
+				$html .= "<td class=\"{$class}\" style=\"width: 33.3%;\">"; // " to workaround stupid jEdit bug
+				
+				$link = makeUrlNS($row['namespace'], sanitize_page_id($row['urlname']));
+				$html .= '<a href="' . $link . '"';
+				$key = $paths->nslist[$row['namespace']] . sanitize_page_id($row['urlname']);
+				if ( !isPage( $key ) )
+				{
+					$html .= ' class="wikilink-nonexistent"';
+				}
+				$html .= '>';
+				$title = get_page_title_ns($row['urlname'], $row['namespace']);
+				$html .= htmlspecialchars($title);
+				$html .= '</a>';
+				
+				$html .= "</td>";
+			}
+			if ( !$switched )
+			{
+				if ( $counter > 0 )
+				{
+					// Fill-in
+					while ( $ticker < 2 )
+					{
+						$ticker++;
+						$html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
+					}
+				}
+				else
+				{
+					$html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_subcategories') . '</td>';
+				}
+				$html .= '</tr></table></div>' . "\n\n";
+				$html .= '<h3>' . $lang->get('onpage_cat_heading_pages') . '</h3>';
+				$html .= '<div class="tblholder">';
+				$html .= '<table border="0" cellspacing="1" cellpadding="4">';
+				$html .= '<tr>';
+				$counter = 0;
+				$ticker = 0;
+				$switched = true;
+			}
+			if ( $counter > 0 )
+			{
+				// Fill-in
+				while ( $ticker < 2 )
+				{
+					$ticker++;
+					$html .= '<td class="' . $class . '" style="width: 33.3%;"></td>';
+				}
+			}
+			else
+			{
+				$html .= '<td class="' . $class . '">' . $lang->get('onpage_cat_msg_no_pages') . '</td>';
+			}
+			$html .= '</tr></table></div>' . "\n\n";
+		}
+		
+		if ( $this->namespace != 'Special' && $this->namespace != 'Admin' )
+		{
+			$html .= '<div class="mdg-comment" style="margin: 10px 0 0 0;" id="category_box_wrapper">';
+			$html .= '<div style="float: right;">';
+			$html .= '(<a href="#" onclick="ajaxCatToTag(); return false;">' . $lang->get('tags_catbox_link') . '</a>)';
+			$html .= '</div>';
+			$html .= '<div id="mdgCatBox">' . $lang->get('catedit_catbox_lbl_categories') . ' ';
+			
+			$q = $db->sql_query('SELECT category_id FROM ' . table_prefix . "categories WHERE page_id = '$this->page_id' AND namespace = '$this->namespace';");
+			if ( !$q )
+				$db->_die();
+			
+			if ( $row = $db->fetchrow() )
+			{
+				$list = array();
+				do
+				{
+					$cid = sanitize_page_id($row['category_id']);
+					$title = get_page_title_ns($cid, 'Category');
+					$link = makeUrlNS('Category', $cid);
+					$list[] = '<a href="' . $link . '">' . htmlspecialchars($title) . '</a>';
+				}
+				while ( $row = $db->fetchrow($q) );
+				$html .= implode(', ', $list);
+			}
+			else
+			{
+				$html .= $lang->get('catedit_catbox_lbl_uncategorized');
+			}
+			
+			$can_edit = ( $session->get_permissions('edit_cat') && ( !$paths->page_protected || $session->get_permissions('even_when_protected') ) );
+			if ( $can_edit )
+			{
+				$edit_link = '<a href="' . makeUrl($paths->page, 'do=catedit', true) . '" onclick="ajaxCatEdit(); return false;">' . $lang->get('catedit_catbox_link_edit') . '</a>';
+				$html .= ' [ ' . $edit_link . ' ]';
+			}
+			
+			$html .= '</div></div>';
+		}
+		return $html;
+	}
+	
+	/**
+ 	* Pull in switches as to whether a specific toolbar button should be used or not. This sets things up according to the current page being displayed.
+ 	* @return array Associative
+ 	*/
+	
+	function set_conds()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !$this->perms )
+			$this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
+		
+		if ( !$this->perms )
+		{
+			// We're trying to send a page WAY too early (session hasn't been started yet), such as for a redirect. Send a default set of conds because
+			// there's NO way to get permissions to determine anything otherwise. Yes, starting $session here might be dangerous.
+			$this->conds = array(
+					'article' => true,
+					'comments' => false,
+					'edit' => false,
+					'viewsource' => false,
+					'history' => false,
+					'rename' => false,
+					'delvote' => false,
+					'resetvotes' => false,
+					'delete' => false,
+					'printable' => false,
+					'protect' => false,
+					'setwikimode' => false,
+					'clearlogs' => false,
+					'password' => false,
+					'acledit' => false,
+					'adminpage' => false
+				);
+			return $this->conds;
+		}
+		
+		// die('have perms: <pre>' . print_r($this->perms, true) . "\n---------------------------------\nBacktrace:\n" . enano_debug_print_backtrace(true));
+		
+		$enforce_protection = ( $this->page_protected && ( ( $session->check_acl_scope('even_when_protected', $this->namespace) && !$this->perms->get_permissions('even_when_protected') ) || !$session->check_acl_scope('even_when_protected', $this->namespace) ) );
+		
+		$conds = array();
+		
+		// Article: always show
+		$conds['article'] = true;
+		
+		// Discussion: Show if comments are enabled on the site, and if comments are on for this page.
+		$conds['comments'] = $this->perms->get_permissions('read') && getConfig('enable_comments', '1')=='1' && $this->cdata['comments_on'] == 1;
+		
+		// Edit: Show if we have permission to edit the page, and if we don't have protection in effect
+		$conds['edit'] = $this->perms->get_permissions('read') && $session->check_acl_scope('edit_page', $this->namespace) && $this->perms->get_permissions('edit_page') && !$enforce_protection;
+		
+		// View source: Show if we have permission to view source and either ACLs prohibit editing or protection is in effect
+		$conds['viewsource'] = $session->check_acl_scope('view_source', $this->namespace) && $this->perms->get_permissions('view_source') && ( !$this->perms->get_permissions('edit_page') || $enforce_protection ) && $this->namespace != 'API';
+		
+		// History: Show if we have permission to see history and if the page exists
+		$conds['history'] = $session->check_acl_scope('history_view', $this->namespace) && $this->exists && $this->perms->get_permissions('history_view');
+		
+		// Rename: Show if the page exists, if we have permission to rename, and if protection isn't in effect
+		$conds['rename'] = $session->check_acl_scope('rename', $this->namespace) && $this->exists && $this->perms->get_permissions('rename') && !$enforce_protection;
+		
+		// Vote-to-delete: Show if we have Wiki Mode on, if we have permission to vote for deletion, and if the page exists (can't vote to delete a nonexistent page)
+		$conds['delvote'] = $this->wiki_mode && $session->check_acl_scope('vote_delete', $this->namespace) && $this->perms->get_permissions('vote_delete') && $this->exists;
+		
+		// Reset votes: Show if we have Wiki Mode on, if we have permission to reset votes, if the page exists, and if there's at least one vote
+		$conds['resetvotes'] = $session->check_acl_scope('vote_reset', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('vote_reset') && $this->cdata['delvotes'] > 0;
+		
+		// Delete page: Show if the page exists and if we have permission to delete it
+		$conds['delete'] = $session->check_acl_scope('delete_page', $this->namespace) && $this->exists && $this->perms->get_permissions('delete_page');
+		
+		// Printable view: Show if the page exists
+		$conds['printable'] = $this->exists;
+		
+		// Protect: Show if we have Wiki Mode on, if the page exists, and if we have permission to protect the page.
+		$conds['protect'] = $session->check_acl_scope('protect', $this->namespace) && $this->wiki_mode && $this->exists && $this->perms->get_permissions('protect');
+		
+		// Set Wiki Mode: Show if the page exists and if we have permission to set wiki mode
+		$conds['setwikimode'] = $session->check_acl_scope('set_wiki_mode', $this->namespace) && $this->exists && $this->perms->get_permissions('set_wiki_mode');
+		
+		// Clear logs: Show if we have permission to clear logs
+		$conds['clearlogs'] = $session->check_acl_scope('clear_logs', $this->namespace) && $this->perms->get_permissions('clear_logs');
+		
+		// Set password: a little bit complicated. If there's a password, check for password_reset; else, check for password_set.
+		$conds['password'] = empty($this->cdata['password']) ?
+ 													$session->check_acl_scope('password_set', $this->namespace) && $this->perms->get_permissions('password_set') :
+ 													$session->check_acl_scope('password_reset', $this->namespace) && $this->perms->get_permissions('password_reset');
+		
+		// Edit ACLs: Show if this is a non-Enano page that's calling the Enano API and (a) if we have permissions to edit ACLs or (b) we're an admin AND ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL is on
+		$conds['acledit'] = $this->namespace != 'API' && $session->check_acl_scope('edit_acl', $this->namespace) && ( $this->perms->get_permissions('edit_acl') || ( defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL') &&  $session->user_level >= USER_LEVEL_ADMIN ) );
+		
+		// Admin page: Show if the page exists and if we're an admin
+		$conds['adminpage'] = $session->user_level >= USER_LEVEL_ADMIN && $this->exists;
+		
+		// Allow plugins to change stuff
+		$code = $plugins->setHook('page_conds_set');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		$this->conds = $conds;
+	}
+	
+	/**
+ 	* Return page conditions
+ 	* @return array
+ 	*/
+	
+	public function get_conds()
+	{
+		if ( empty($this->conds) )
+			$this->set_conds();
+		
+		return $this->conds;
+	}
+	
+	/**
+ 	* Just tell us if the current page exists or not.
+ 	* @return bool
+ 	*/
+ 	
+	public function exists()
+	{
+		return $this->exists;
+	}
+	
+	/**
+ 	* Return cdata
+ 	* @return array
+ 	*/
+	
+	public function get_cdata()
+	{
+		return $this->cdata;
+	}
+	
+	/**
+ 	* Bake, or finalize the processing of, a cdata array.
+ 	* @static
+ 	* @access public
+ 	*/
+	
+	public static function bake_cdata($cdata)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// urlname_nons is the actual page_id.
+		$cdata['urlname_nons'] = $cdata['urlname'];
+		if ( isset($paths->nslist[ $cdata['namespace'] ]) )
+		{
+			$cdata['urlname'] = $paths->nslist[ $cdata['namespace'] ] . $cdata['urlname'];
+		}
+		else
+		{
+			$ns_char = substr($paths->nslist['Special'], -1);
+			$cdata['urlname'] = $cdata['namespace'] . $ns_char . $cdata['urlname'];
+		}
+		
+		// add missing keys
+		$defaults = array(
+			'special' => 0,
+			'visible' => 0,
+			'comments_on' => 1,
+			'protected' => 0,
+			'delvotes' => 0,
+			'delvote_ips' => serialize(array()),
+			'wiki_mode' => 2,
+			'page_format' => getConfig('default_page_format', 'wikitext')
+		);
+		foreach ( $defaults as $key => $value )
+		{
+			if ( !isset($cdata[$key]) )
+				$cdata[$key] = $value;
+		}
+		
+		// fix up deletion votes
+		if ( empty($cdata['delvotes']) )
+			$cdata['delvotes'] = 0;
+		
+		// fix up deletion vote IP list
+		if ( empty($cdata['delvote_ips']) )
+			$cdata['delvote_ips'] = serialize(array());
+		
+		// calculate wiki mode
+		$cdata['really_wiki_mode'] = ( $cdata['wiki_mode'] == 1 || ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode', 0) == 1 ) );
+		
+		// calculate protection
+		$cdata['really_protected'] = ( $cdata['protected'] > 0 );
+		if ( $cdata['protected'] == 2 )
+		{
+			$cdata['really_protected'] = !$session->user_logged_in || ( $session->user_logged_in && $session->reg_time + 86400*4 > time() );
+		}
+		
+		return $cdata;
+	}
+	
+	/**
+ 	* Grabs raw (unbaked) cdata from the database, caching if possible.
+ 	* @param string Page ID
+ 	* @param string Namespace.
+ 	* @static
+ 	*/
+	
+	public static function get_cdata_from_db($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		static $cache = array();
+		
+		$pathskey = $paths->get_pathskey($page_id, $namespace);
+		if ( isset($cache[$pathskey]) )
+			return $cache[$pathskey];
+		
+		$page_id_db = $db->escape($page_id);
+		$namespace_db = $db->escape($namespace);
+		
+		$q = $db->sql_query('SELECT p.*'
+											. '    FROM ' . table_prefix . "pages AS p\n"
+											. "  WHERE p.urlname = '$page_id_db' AND p.namespace = '$namespace_db'\n"
+											. "    GROUP BY p.urlname, p.name, p.namespace, p.page_order, p.special, p.visible, p.protected, p.wiki_mode, p.comments_on, p.delvotes, p.delvote_ips, p.page_format, p.password;");
+		
+		if ( !$q )
+			$db->_die();
+		
+		if ( $db->numrows() < 1 )
+		{
+			$db->free_result();
+			$cache[$pathskey] = false;
+			return false;
+		}
+		
+		$row = $db->fetchrow();
+		
+		// Get comment counts
+		// FIXME: Apparently there's a bit of recursion in here. Fetching permissions depends on this cdata function.
+		// Perhaps we should eliminate session's dependency on cdata? (What is it used for?)
+		$q = $db->sql_query('SELECT approved FROM ' . table_prefix . "comments WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';");
+		// yay parallel assignment
+		$row['comments_approved'] = $row['comments_unapproved'] = $row['comments_spam'] = 0;
+		while ( $commentrow = $db->fetchrow() )
+			switch($commentrow['approved'])
+			{
+				case COMMENT_APPROVED:
+				default:
+					$row['comments_approved']++;
+					break;
+				case COMMENT_UNAPPROVED:
+					$row['comments_unapproved']++;
+					break;
+				case COMMENT_SPAM:
+					$row['comments_spam']++;
+					break;
+			}
+		
+		$cache[$pathskey] = $row;
+		return $row;
+	}
 }
 
 /**
--- a/includes/namespaces/file.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/namespaces/file.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,255 +13,255 @@
 
 class Namespace_File extends Namespace_Default
 {
-  function send()
-  {
-    global $output;
-    
-    $output->add_before_footer($this->show_info());
-    $output->add_before_footer($this->display_categories());
-    
-    if ( $this->exists )
-    {
-      $this->send_from_db();
-    }
-    else
-    {
-      $output->header();
-      $this->error_404();
-      $output->footer();
-    }
-  }
-  
-  function show_info()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    require_once(ENANO_ROOT . '/includes/log.php');
-    
-    $local_page_id = $this->page_id;
-    $local_namespace = $this->namespace;
-    $html = '';
-    
-    // Prevent unnecessary work
-    if ( $local_namespace != 'File' )
-      return null;
-    
-    $selfn = $db->escape($this->page_id);
-    $q = $db->sql_query('SELECT f.mimetype,f.time_id,f.size,l.log_id FROM ' . table_prefix . "files AS f\n"
-                      . "  LEFT JOIN " . table_prefix . "logs AS l\n"
-                      . "    ON ( l.time_id = f.time_id AND ( l.action = 'reupload' OR l.action IS NULL ) )\n"
-                      . "  WHERE f.page_id = '$selfn'\n"
-                      . "    ORDER BY f.time_id DESC;");
-    if ( !$q )
-    {
-      $db->_die('The file type could not be fetched.');
-    }
-    
-    if ( $db->numrows() < 1 )
-    {
-      $html .= '<div class="mdg-comment" style="margin-left: 0;">
-              <h3>' . $lang->get('onpage_filebox_heading') . '</h3>
-              <p>' . $lang->get('onpage_filebox_msg_not_found', array('upload_link' => makeUrlNS('Special', 'UploadFile/'.$local_page_id))) . '</p>
-            </div>
-            <br />';
-      return $html;
-    }
-    $r = $db->fetchrow();
-    $mimetype = $r['mimetype'];
-    $datestring = enano_date(ED_DATE | ED_TIME, (int)$r['time_id']);
-    $html .= '<div class="mdg-comment" style="margin-left: 0;">
-            <h3>' . $lang->get('onpage_filebox_heading') . '</h3>
-            <p>' . $lang->get('onpage_filebox_lbl_type') . ' '.$r['mimetype'].'<br />';
-    
-    $size = $r['size'] . ' ' . $lang->get('etc_unit_bytes');
-    if ( $r['size'] >= 1048576 )
-    {
-      $size .= ' (' . ( round($r['size'] / 1048576, 1) ) . ' ' . $lang->get('etc_unit_megabytes_short') . ')';
-    }
-    else if ( $r['size'] >= 1024 )
-    {
-      $size .= ' (' . ( round($r['size'] / 1024, 1) ) . ' ' . $lang->get('etc_unit_kilobytes_short') . ')';
-    }
-    
-    $html .= $lang->get('onpage_filebox_lbl_size', array('size' => $size));
-    
-    $html .= '<br />' . $lang->get('onpage_filebox_lbl_uploaded') . ' ' . $datestring . '</p>';
-    // are we dealing with an image?
-    $is_image = substr($mimetype, 0, 6) == 'image/';
-    
-    // for anything other than plain text and 
-    if ( !$is_image && ( substr($mimetype, 0, 5) != 'text/' || $mimetype == 'text/html' || $mimetype == 'text/javascript' ) )
-    {
-      $html .= '<div class="warning-box">
-              ' . $lang->get('onpage_filebox_msg_virus_warning') . '
-            </div>';
-    }
-    if ( $is_image )
-    {
-      // show a thumbnail of the image
-      $html .= '<p>
-              <a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn).'">
-                <img style="border: 0;" alt="' . htmlspecialchars($paths->page) . '" src="' . makeUrlNS('Special', "DownloadFile/$selfn/{$r['time_id']}", 'preview', true) . '" />
-              </a>
-            </p>';
-    }
-    $html .= '<p>
-            <a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.'/'.$r['time_id'].htmlspecialchars(urlSeparator).'download').'">
-              ' . $lang->get('onpage_filebox_btn_download') . '
-            </a>';
-    // allow reupload if:
-    //   * we are allowed to upload new versions, and
-    //      - the file is unprotected, or
-    //      - we have permission to override protection
-    
-    if ( !$this->perms )
-      $this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
-    
-    if ( $this->perms->get_permissions('upload_new_version') && ( !$this->page_protected || $this->perms->get_permissions('even_when_protected') ) )
-    {
-      // upload new version link
-      $html .= '  |  <a href="'.makeUrlNS('Special', "UploadFile/$selfn", false, true).'">
-              ' . $lang->get('onpage_filebox_btn_upload_new') . '
-            </a>';
-    }
-    // close off paragraph
-    $html .= '</p>';
-    // only show this if there's more than one revision
-    if ( $db->numrows() > 1 )
-    {
-      // requery, sql_result_seek() doesn't work on postgres
-      $db->free_result();
-      $q = $db->sql_query('SELECT f.mimetype,f.time_id,f.size,l.log_id FROM ' . table_prefix . "files AS f\n"
-                      . "  LEFT JOIN " . table_prefix . "logs AS l\n"
-                      . "    ON ( l.time_id = f.time_id AND ( l.action = 'reupload' OR l.action IS NULL ) )\n"
-                      . "  WHERE f.page_id = '$selfn'\n"
-                      . "    ORDER BY f.time_id DESC;");
-      if ( !$q )
-        $db->_die();
-      
-      $log = new LogDisplay();
-      $log->add_criterion('page', $paths->nslist['File'] . $this->page_id);
-      $log->add_criterion('action', 'reupload');
-      $data = $log->get_data();
-      $i = -1;
-      
-      $html .= '<h3>' . $lang->get('onpage_filebox_heading_history') . '</h3><p>';
-      $last_rollback_id = false;
-      $download_flag = $is_image ? false : 'download';
-      while ( $r = $db->fetchrow($q) )
-      {
-        $html .= '(<a href="'.makeUrlNS('Special', "DownloadFile/$selfn/{$r['time_id']}", $download_flag, true).'">' . $lang->get('onpage_filebox_btn_this_version') . '</a>) ';
-        if ( $session->get_permissions('history_rollback') && $last_rollback_id )
-          $html .= ' (<a href="#rollback:' . $last_rollback_id . '" onclick="ajaxRollback(\''.$last_rollback_id.'\'); return false;">' . $lang->get('onpage_filebox_btn_revert') . '</a>) ';
-        else if ( $session->get_permissions('history_rollback') && !$last_rollback_id )
-          $html .= ' (' . $lang->get('onpage_filebox_btn_current') . ') ';
-        $last_rollback_id = $r['log_id'];
-        
-        $html .= $r['mimetype'].', ';
-        
-        $fs = $r['size'];
-        $fs = (int)$fs;
-        
-        if($fs >= 1048576)
-        {
-          $fs = round($fs / 1048576, 1);
-          $size = $fs . ' ' . $lang->get('etc_unit_megabytes_short');
-        }
-        else
-        if ( $fs >= 1024 )
-        {
-          $fs = round($fs / 1024, 1);
-          $size = $fs . ' ' . $lang->get('etc_unit_kilobytes_short');
-        }
-        else
-        {
-          $size = $fs . ' ' . $lang->get('etc_unit_bytes');
-        }
-        
-        $html .= $size;
-        if ( isset($data[++$i]) )
-          $html .= ': ' . LogDisplay::render_row($data[$i], false, false);
-        
-        $html .= '<br />';
-      }
-      $html .= '</p>';
-    }
-    $db->free_result();
-    $html .= '</div><br />';
-    return $html;
-  }
-  
-  /**
-   * Delete a file from the database and filesystem based on file ID.
-   * @param int File ID
-   * @return null
-   */
-  
-  public static function delete_file($file_id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !is_int($file_id) )
-      // seriously?
-      return null;
-    
-    // pull file info
-    $q = $db->sql_query('SELECT filename, page_id, time_id, file_extension, file_key FROM ' . table_prefix . "files WHERE file_id = $file_id;");
-    if ( !$q )
-      $db->_die();
-    
-    if ( $db->numrows() < 1 )
-    {
-      $db->free_result();
-      return null;
-    }
-    
-    $row = $db->fetchrow();
-    $db->free_result();
-    
-    // make sure the image isn't used by multiple revisions
-    $q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "files WHERE file_key = '{$row['file_key']}';");
-    if ( !$q )
-      $db->_die();
-    if ( $db->numrows() < 1 )
-    {
-      // remove from filesystem
-      $file_path = ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}";
-      @unlink($file_path);
-      // old filename standard
-      $file_path = ENANO_ROOT . "/files/{$row['file_key']}-{$row['time_id']}{$row['file_extension']}";
-      @unlink($file_path);
-    }
-    $db->free_result();
-    
-    // remove from cache
-    if ( $dp = @opendir(ENANO_ROOT . '/cache/') )
-    {
-      $regexp = '#' . preg_quote($row['filename']) . '-' . $row['time_id'] . '-[0-9]+x[0-9]+' . preg_quote($row['file_extension']) . '#';
-      while ( $dh = @readdir($dp) )
-      {
-        if ( preg_match($regexp, $dh) )
-        {
-          // it's a match, delete the cached thumbnail
-          @unlink(ENANO_ROOT . "/cache/$dh");
-        }
-      }
-      closedir($dp);
-    }
-    
-    // remove from database
-    $q = $db->sql_query('DELETE FROM ' . table_prefix . "files WHERE file_id = $file_id;");
-    if ( !$q )
-      $db->_die();
-    
-    // remove from logs
-    $page_id_db = $db->escape($row['page_id']);
-    $q = $db->sql_query('DELETE FROM ' . table_prefix . "logs WHERE page_id = '{$page_id_db}' AND namespace = 'File' AND action = 'reupload' AND time_id = {$row['time_id']};");
-    if ( !$q )
-      $db->_die();
-    
-    return true;
-  }
+	function send()
+	{
+		global $output;
+		
+		$output->add_before_footer($this->show_info());
+		$output->add_before_footer($this->display_categories());
+		
+		if ( $this->exists )
+		{
+			$this->send_from_db();
+		}
+		else
+		{
+			$output->header();
+			$this->error_404();
+			$output->footer();
+		}
+	}
+	
+	function show_info()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		require_once(ENANO_ROOT . '/includes/log.php');
+		
+		$local_page_id = $this->page_id;
+		$local_namespace = $this->namespace;
+		$html = '';
+		
+		// Prevent unnecessary work
+		if ( $local_namespace != 'File' )
+			return null;
+		
+		$selfn = $db->escape($this->page_id);
+		$q = $db->sql_query('SELECT f.mimetype,f.time_id,f.size,l.log_id FROM ' . table_prefix . "files AS f\n"
+											. "  LEFT JOIN " . table_prefix . "logs AS l\n"
+											. "    ON ( l.time_id = f.time_id AND ( l.action = 'reupload' OR l.action IS NULL ) )\n"
+											. "  WHERE f.page_id = '$selfn'\n"
+											. "    ORDER BY f.time_id DESC;");
+		if ( !$q )
+		{
+			$db->_die('The file type could not be fetched.');
+		}
+		
+		if ( $db->numrows() < 1 )
+		{
+			$html .= '<div class="mdg-comment" style="margin-left: 0;">
+							<h3>' . $lang->get('onpage_filebox_heading') . '</h3>
+							<p>' . $lang->get('onpage_filebox_msg_not_found', array('upload_link' => makeUrlNS('Special', 'UploadFile/'.$local_page_id))) . '</p>
+						</div>
+						<br />';
+			return $html;
+		}
+		$r = $db->fetchrow();
+		$mimetype = $r['mimetype'];
+		$datestring = enano_date(ED_DATE | ED_TIME, (int)$r['time_id']);
+		$html .= '<div class="mdg-comment" style="margin-left: 0;">
+						<h3>' . $lang->get('onpage_filebox_heading') . '</h3>
+						<p>' . $lang->get('onpage_filebox_lbl_type') . ' '.$r['mimetype'].'<br />';
+		
+		$size = $r['size'] . ' ' . $lang->get('etc_unit_bytes');
+		if ( $r['size'] >= 1048576 )
+		{
+			$size .= ' (' . ( round($r['size'] / 1048576, 1) ) . ' ' . $lang->get('etc_unit_megabytes_short') . ')';
+		}
+		else if ( $r['size'] >= 1024 )
+		{
+			$size .= ' (' . ( round($r['size'] / 1024, 1) ) . ' ' . $lang->get('etc_unit_kilobytes_short') . ')';
+		}
+		
+		$html .= $lang->get('onpage_filebox_lbl_size', array('size' => $size));
+		
+		$html .= '<br />' . $lang->get('onpage_filebox_lbl_uploaded') . ' ' . $datestring . '</p>';
+		// are we dealing with an image?
+		$is_image = substr($mimetype, 0, 6) == 'image/';
+		
+		// for anything other than plain text and 
+		if ( !$is_image && ( substr($mimetype, 0, 5) != 'text/' || $mimetype == 'text/html' || $mimetype == 'text/javascript' ) )
+		{
+			$html .= '<div class="warning-box">
+							' . $lang->get('onpage_filebox_msg_virus_warning') . '
+						</div>';
+		}
+		if ( $is_image )
+		{
+			// show a thumbnail of the image
+			$html .= '<p>
+							<a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn).'">
+								<img style="border: 0;" alt="' . htmlspecialchars($paths->page) . '" src="' . makeUrlNS('Special', "DownloadFile/$selfn/{$r['time_id']}", 'preview', true) . '" />
+							</a>
+						</p>';
+		}
+		$html .= '<p>
+						<a href="'.makeUrlNS('Special', 'DownloadFile'.'/'.$selfn.'/'.$r['time_id'].htmlspecialchars(urlSeparator).'download').'">
+							' . $lang->get('onpage_filebox_btn_download') . '
+						</a>';
+		// allow reupload if:
+		//   * we are allowed to upload new versions, and
+		//      - the file is unprotected, or
+		//      - we have permission to override protection
+		
+		if ( !$this->perms )
+			$this->perms = $session->fetch_page_acl($this->page_id, $this->namespace);
+		
+		if ( $this->perms->get_permissions('upload_new_version') && ( !$this->page_protected || $this->perms->get_permissions('even_when_protected') ) )
+		{
+			// upload new version link
+			$html .= '  |  <a href="'.makeUrlNS('Special', "UploadFile/$selfn", false, true).'">
+							' . $lang->get('onpage_filebox_btn_upload_new') . '
+						</a>';
+		}
+		// close off paragraph
+		$html .= '</p>';
+		// only show this if there's more than one revision
+		if ( $db->numrows() > 1 )
+		{
+			// requery, sql_result_seek() doesn't work on postgres
+			$db->free_result();
+			$q = $db->sql_query('SELECT f.mimetype,f.time_id,f.size,l.log_id FROM ' . table_prefix . "files AS f\n"
+											. "  LEFT JOIN " . table_prefix . "logs AS l\n"
+											. "    ON ( l.time_id = f.time_id AND ( l.action = 'reupload' OR l.action IS NULL ) )\n"
+											. "  WHERE f.page_id = '$selfn'\n"
+											. "    ORDER BY f.time_id DESC;");
+			if ( !$q )
+				$db->_die();
+			
+			$log = new LogDisplay();
+			$log->add_criterion('page', $paths->nslist['File'] . $this->page_id);
+			$log->add_criterion('action', 'reupload');
+			$data = $log->get_data();
+			$i = -1;
+			
+			$html .= '<h3>' . $lang->get('onpage_filebox_heading_history') . '</h3><p>';
+			$last_rollback_id = false;
+			$download_flag = $is_image ? false : 'download';
+			while ( $r = $db->fetchrow($q) )
+			{
+				$html .= '(<a href="'.makeUrlNS('Special', "DownloadFile/$selfn/{$r['time_id']}", $download_flag, true).'">' . $lang->get('onpage_filebox_btn_this_version') . '</a>) ';
+				if ( $session->get_permissions('history_rollback') && $last_rollback_id )
+					$html .= ' (<a href="#rollback:' . $last_rollback_id . '" onclick="ajaxRollback(\''.$last_rollback_id.'\'); return false;">' . $lang->get('onpage_filebox_btn_revert') . '</a>) ';
+				else if ( $session->get_permissions('history_rollback') && !$last_rollback_id )
+					$html .= ' (' . $lang->get('onpage_filebox_btn_current') . ') ';
+				$last_rollback_id = $r['log_id'];
+				
+				$html .= $r['mimetype'].', ';
+				
+				$fs = $r['size'];
+				$fs = (int)$fs;
+				
+				if($fs >= 1048576)
+				{
+					$fs = round($fs / 1048576, 1);
+					$size = $fs . ' ' . $lang->get('etc_unit_megabytes_short');
+				}
+				else
+				if ( $fs >= 1024 )
+				{
+					$fs = round($fs / 1024, 1);
+					$size = $fs . ' ' . $lang->get('etc_unit_kilobytes_short');
+				}
+				else
+				{
+					$size = $fs . ' ' . $lang->get('etc_unit_bytes');
+				}
+				
+				$html .= $size;
+				if ( isset($data[++$i]) )
+					$html .= ': ' . LogDisplay::render_row($data[$i], false, false);
+				
+				$html .= '<br />';
+			}
+			$html .= '</p>';
+		}
+		$db->free_result();
+		$html .= '</div><br />';
+		return $html;
+	}
+	
+	/**
+ 	* Delete a file from the database and filesystem based on file ID.
+ 	* @param int File ID
+ 	* @return null
+ 	*/
+	
+	public static function delete_file($file_id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !is_int($file_id) )
+			// seriously?
+			return null;
+		
+		// pull file info
+		$q = $db->sql_query('SELECT filename, page_id, time_id, file_extension, file_key FROM ' . table_prefix . "files WHERE file_id = $file_id;");
+		if ( !$q )
+			$db->_die();
+		
+		if ( $db->numrows() < 1 )
+		{
+			$db->free_result();
+			return null;
+		}
+		
+		$row = $db->fetchrow();
+		$db->free_result();
+		
+		// make sure the image isn't used by multiple revisions
+		$q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "files WHERE file_key = '{$row['file_key']}';");
+		if ( !$q )
+			$db->_die();
+		if ( $db->numrows() < 1 )
+		{
+			// remove from filesystem
+			$file_path = ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}";
+			@unlink($file_path);
+			// old filename standard
+			$file_path = ENANO_ROOT . "/files/{$row['file_key']}-{$row['time_id']}{$row['file_extension']}";
+			@unlink($file_path);
+		}
+		$db->free_result();
+		
+		// remove from cache
+		if ( $dp = @opendir(ENANO_ROOT . '/cache/') )
+		{
+			$regexp = '#' . preg_quote($row['filename']) . '-' . $row['time_id'] . '-[0-9]+x[0-9]+' . preg_quote($row['file_extension']) . '#';
+			while ( $dh = @readdir($dp) )
+			{
+				if ( preg_match($regexp, $dh) )
+				{
+					// it's a match, delete the cached thumbnail
+					@unlink(ENANO_ROOT . "/cache/$dh");
+				}
+			}
+			closedir($dp);
+		}
+		
+		// remove from database
+		$q = $db->sql_query('DELETE FROM ' . table_prefix . "files WHERE file_id = $file_id;");
+		if ( !$q )
+			$db->_die();
+		
+		// remove from logs
+		$page_id_db = $db->escape($row['page_id']);
+		$q = $db->sql_query('DELETE FROM ' . table_prefix . "logs WHERE page_id = '{$page_id_db}' AND namespace = 'File' AND action = 'reupload' AND time_id = {$row['time_id']};");
+		if ( !$q )
+			$db->_die();
+		
+		return true;
+	}
 }
 
--- a/includes/namespaces/special.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/namespaces/special.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,97 +13,97 @@
 
 class Namespace_Special extends Namespace_Default
 {
-  public function __construct($page_id, $namespace, $revision_id = 0)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $this->page_id = sanitize_page_id($page_id);
-    $this->namespace = $namespace;
-    $this->build_cdata();
-    $this->revision_id = intval($revision_id);
-    
-    $this->page_protected = true;
-    $this->wiki_mode = 0;
-  }
-  
-  public function build_cdata()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $this->exists = function_exists("page_{$this->namespace}_{$this->page_id}");
-    
-    if ( isset($paths->pages[ $paths->get_pathskey($this->page_id, $this->namespace) ]) )
-    {
-      $page_name = $paths->pages[ $paths->get_pathskey($this->page_id, $this->namespace) ]['name'];
-    }
-    else
-    {
-      $page_name = "{$paths->nslist[ $this->namespace ]}{$this->page_id}";
-      if ( ($_ = $lang->get('specialpage_' . strtolower($this->page_id))) !== 'specialpage_' . strtolower($this->page_id) )
-      {
-        $page_name = $_;
-      }
-    }
-    
-    $this->cdata = array(
-        'name' => $lang->get($page_name),
-        'urlname' => $this->page_id,
-        'namespace' => $this->namespace,
-        'special' => 0,
-        'visible' => 0,
-        'comments_on' => 0,
-        'protected' => 0,
-        'delvotes' => 0,
-        'delvote_ips' => '',
-        'wiki_mode' => 2,
-        'page_exists' => false,
-        'page_format' => getConfig('default_page_format', 'wikitext')
-      );
-    $this->cdata = Namespace_Default::bake_cdata($this->cdata);
-    
-    $this->title =& $this->cdata['name'];
-  }
-  
-  function send()
-  {
-    global $output;
-    
-    if ( $this->exists )
-    {
-      call_user_func("page_{$this->namespace}_{$this->page_id}");
-    }
-    else
-    {
-      $output->header();
-      $this->error_404();
-      $output->footer();
-    }
-  }
-  
-  // We add the unused variable $userpage here to silence "declaration should be compatible" errors
-  function error_404()
-  {
-    global $lang, $output;
-    $func_name = "page_{$this->namespace}_{$this->page_id}";
-    
-    if ( $this->namespace == 'Admin' )
-      die_semicritical($lang->get('page_msg_admin_404_title'), $lang->get('page_msg_admin_404_body', array('func_name' => $func_name)), true);
-    
-    $title = $lang->get('page_err_custompage_function_missing_title');
-    $message = $lang->get('page_err_custompage_function_missing_body', array( 'function_name' => $func_name ));
-    
-    $output->set_title($title);
-    $output->header();
-    echo "<p>$message</p>";
-    $output->footer();
-  }
-  
-  function set_conds()
-  {
-    parent::set_conds();
-    
-    $this->conds['printable'] = false;
-    $this->conds['adminpage'] = false;
-  }
+	public function __construct($page_id, $namespace, $revision_id = 0)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$this->page_id = sanitize_page_id($page_id);
+		$this->namespace = $namespace;
+		$this->build_cdata();
+		$this->revision_id = intval($revision_id);
+		
+		$this->page_protected = true;
+		$this->wiki_mode = 0;
+	}
+	
+	public function build_cdata()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$this->exists = function_exists("page_{$this->namespace}_{$this->page_id}");
+		
+		if ( isset($paths->pages[ $paths->get_pathskey($this->page_id, $this->namespace) ]) )
+		{
+			$page_name = $paths->pages[ $paths->get_pathskey($this->page_id, $this->namespace) ]['name'];
+		}
+		else
+		{
+			$page_name = "{$paths->nslist[ $this->namespace ]}{$this->page_id}";
+			if ( ($_ = $lang->get('specialpage_' . strtolower($this->page_id))) !== 'specialpage_' . strtolower($this->page_id) )
+			{
+				$page_name = $_;
+			}
+		}
+		
+		$this->cdata = array(
+				'name' => $lang->get($page_name),
+				'urlname' => $this->page_id,
+				'namespace' => $this->namespace,
+				'special' => 0,
+				'visible' => 0,
+				'comments_on' => 0,
+				'protected' => 0,
+				'delvotes' => 0,
+				'delvote_ips' => '',
+				'wiki_mode' => 2,
+				'page_exists' => false,
+				'page_format' => getConfig('default_page_format', 'wikitext')
+			);
+		$this->cdata = Namespace_Default::bake_cdata($this->cdata);
+		
+		$this->title =& $this->cdata['name'];
+	}
+	
+	function send()
+	{
+		global $output;
+		
+		if ( $this->exists )
+		{
+			call_user_func("page_{$this->namespace}_{$this->page_id}");
+		}
+		else
+		{
+			$output->header();
+			$this->error_404();
+			$output->footer();
+		}
+	}
+	
+	// We add the unused variable $userpage here to silence "declaration should be compatible" errors
+	function error_404()
+	{
+		global $lang, $output;
+		$func_name = "page_{$this->namespace}_{$this->page_id}";
+		
+		if ( $this->namespace == 'Admin' )
+			die_semicritical($lang->get('page_msg_admin_404_title'), $lang->get('page_msg_admin_404_body', array('func_name' => $func_name)), true);
+		
+		$title = $lang->get('page_err_custompage_function_missing_title');
+		$message = $lang->get('page_err_custompage_function_missing_body', array( 'function_name' => $func_name ));
+		
+		$output->set_title($title);
+		$output->header();
+		echo "<p>$message</p>";
+		$output->footer();
+	}
+	
+	function set_conds()
+	{
+		parent::set_conds();
+		
+		$this->conds['printable'] = false;
+		$this->conds['adminpage'] = false;
+	}
 }
--- a/includes/namespaces/template.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/namespaces/template.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,28 +13,28 @@
 
 class Namespace_Template extends Namespace_Default
 {
-  function send()
-  {
-    global $output;
-    
-    $output->add_before_footer($this->display_categories());
-    $output->header();
-    
-    if ( $this->exists )
-    {
-      $text = $this->fetch_text();
-      $text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '\\1', $text);
-      $text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '', $text);
-      
-      $text = RenderMan::render( $text );
-      
-      eval( '?>' . $text );
-    }
-    else
-    {
-      $this->error_404();
-    }
-    
-    $output->footer();
-  }
+	function send()
+	{
+		global $output;
+		
+		$output->add_before_footer($this->display_categories());
+		$output->header();
+		
+		if ( $this->exists )
+		{
+			$text = $this->fetch_text();
+			$text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '\\1', $text);
+			$text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '', $text);
+			
+			$text = RenderMan::render( $text );
+			
+			eval( '?>' . $text );
+		}
+		else
+		{
+			$this->error_404();
+		}
+		
+		$output->footer();
+	}
 }
--- a/includes/namespaces/user.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/namespaces/user.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,463 +13,463 @@
 
 class Namespace_User extends Namespace_Default
 {
-  public function __construct($page_id, $namespace, $revision_id = 0)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    parent::__construct($page_id, $namespace, $revision_id);
-    
-    if ( ( $this->title == str_replace('_', ' ', $this->page_id) || $this->title == $paths->nslist['User'] . str_replace('_', ' ', $this->page_id) ) || !$this->exists )
-    {
-      $this->title = $lang->get('userpage_page_title', array('username' => str_replace('_', ' ', dirtify_page_id($this->page_id))));
-      $this->cdata['name'] = $this->title;
-    }
-    
-  }
-  
-  public function send()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $email;
-    global $lang, $output;
-    
-    /**
-     * PLUGGING INTO USER PAGES
-     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-     * Userpages are highly programmable and extendable using a number of
-     * hooks. These hooks are:
-     *
-     *   - userpage_sidebar_left
-     *   - userpage_sidebar_right
-     *   - userpage_tabs_links
-     *   - userpage_tabs_body
-     *
-     * You can add a variety of sections to user pages, including new tabs
-     * and new sections on the tables. To add a tab, attach to
-     * userpage_tabs_links and echo out:
-     *
-     *   <li><a href="#tab:YOURTABID">YOUR TAB TEXT</a></li>
-     *
-     * Then hook into userpage_tabs_body and echo out:
-     *
-     *   <div id="tab:YOURTABID">YOUR TAB CONTENT</div>
-     *
-     * The userpage javascript runtime will take care of everything else,
-     * meaning transitions, click events, etc. Currently it's not possible
-     * to add custom click events to tabs, but any DOM-related JS that needs
-     * to run in your tab can be run onload and the effects will be seen when
-     * your tab is clicked. YOURTABID should be lowercase alphanumeric and
-     * have a short prefix so as to assure that it remains specific to your
-     * plugin.
-     *
-     * To hook into the "profile" tab, use userpage_sidebar_{left,right}. Just
-     * echo out table cells as normal. The table on the left (the wide one) has
-     * four columns, and the one on the right has one column.
-     * 
-     * See plugins.php for a guide on creating and attaching to hooks.
-     */
-     
-    $page_urlname = dirtify_page_id($this->page_id);
-    
-    $target_username = strtr($page_urlname, 
-      Array(
-        '_' => ' ',
-        '<' => '&lt;',
-        '>' => '&gt;'
-        ));
-    
-    $target_username = preg_replace('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', '', $target_username);
-    list($target_username) = explode('/', $target_username);
-    
-    $ux_columns = $db->columns_in(table_prefix . 'users_extra');
-    
-    $output->set_title($this->title);
-    $q = $db->sql_query('SELECT u.username, u.user_id AS authoritative_uid, u.real_name, u.email, u.reg_time, u.user_has_avatar, u.avatar_type, x.*, COUNT(c.comment_id) AS n_comments
-                           FROM '.table_prefix.'users u
-                           LEFT JOIN '.table_prefix.'users_extra AS x
-                             ON ( u.user_id = x.user_id OR x.user_id IS NULL ) 
-                           LEFT JOIN '.table_prefix.'comments AS c
-                             ON ( ( c.user_id=u.user_id AND c.name=u.username AND c.approved=1 ) OR ( c.comment_id IS NULL AND c.approved IS NULL ) )
-                           WHERE u.username=\'' . $db->escape($target_username) . '\'
-                           GROUP BY u.username, u.user_id, u.real_name, u.email, u.reg_time, u.user_has_avatar, u.avatar_type, x.' . implode(', x.', $ux_columns) . ';');
-    if ( !$q )
-      $db->_die();
-    
-    $user_exists = true;
-    
-    if ( $db->numrows() < 1 )
-    {
-      $user_exists = false;
-    }
-    else
-    {
-      $userdata = $db->fetchrow();
-      if ( $userdata['authoritative_uid'] == 1 )
-      {
-        // Hide data for anonymous user
-        $user_exists = false;
-        unset($userdata);
-      }
-    }
-    
-    // get the user's rank
-    if ( $user_exists )
-    {
-      $rank_data = $session->get_user_rank(intval($userdata['authoritative_uid']));
-    }
-    else
-    {
-      // get the rank data for the anonymous user (placeholder basically)
-      $rank_data = $session->get_user_rank(1);
-    }
-    
-    // add the userpage script to the header
-    $template->add_header('<script type="text/javascript" src="' . cdnPath . '/includes/clientside/static/userpage.js"></script>');
-    
-    $output->header();
-    
-    // if ( $send_headers )
-    // {
-    //  display_page_headers();
-    // }
-   
-    //
-    // BASIC INFORMATION
-    // Presentation of username/rank/avatar/basic info
-    //
-    
-    if ( $user_exists )
-    {
-    
-      ?>
-      <div id="userpage_wrap">
-        <ul id="userpage_links">
-          <li><a href="#tab:profile"><?php echo $lang->get('userpage_tab_profile'); ?></a></li>
-          <li><a href="#tab:content"><?php echo $lang->get('userpage_tab_content'); ?></a></li>
-          <?php
-          $code = $plugins->setHook('userpage_tabs_links');
-          foreach ( $code as $cmd )
-          {
-            eval($cmd);
-          }
-          ?>
-        </ul>
-        
-        <div id="tab:profile">
-      
-      <?php
-      
-      echo '<table border="0" cellspacing="0" cellpadding="0">
-              <tr>';
-                
-      echo '    <td valign="top">';
-      
-      echo '<div class="tblholder">
-              <table border="0" cellspacing="1" cellpadding="4">';
-              
-      // heading
-      echo '    <tr>
-                  <th colspan="' . ( $session->user_level >= USER_LEVEL_ADMIN ? '3' : '4' ) . '">
-                    ' . $lang->get('userpage_heading_basics', array('username' => htmlspecialchars($target_username))) . '
-                  </th>
-                  ' . (
-                    $session->user_level >= USER_LEVEL_ADMIN ?
-                    '<th class="subhead" style="width: 25%;"><a href="' . makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'UserManager&src=get&user=' . urlencode($target_username), true) . '" onclick="ajaxAdminUser(\'' . addslashes($target_username) . '\'); return false;">&raquo; ' . $lang->get('userpage_btn_administer_user') . '</a></th>'
-                      : ''
-                  ) . '
-                </tr>';
-                
-      // avi/rank/username
-      echo '    <tr>
-                  <td class="row3" colspan="4">
-                    ' . (
-                        $userdata['user_has_avatar'] == 1 ?
-                        '<div style="float: left; margin-right: 10px;">
-                          <img alt="' . $lang->get('usercp_avatar_image_alt', array('username' => $userdata['username'])) . '" src="' . make_avatar_url(intval($userdata['authoritative_uid']), $userdata['avatar_type'], $userdata['email']) . '" />
-                         </div>'
-                        : ''
-                      ) . '
-                      <span style="font-size: x-large; ' . $rank_data['rank_style'] . '">' . htmlspecialchars($userdata['username']) . '</span>
-                      ' . ( !empty($rank_data['user_title']) ? '<br />' . htmlspecialchars($rank_data['user_title']) : '' ) . '
-                      ' . ( !empty($rank_data['rank_title']) ? '<br />' . htmlspecialchars($lang->get($rank_data['rank_title'])) : '' ) . '
-                  </td>
-                </tr>';
-                
-      // join date & total comments
-      echo '<tr>';
-      echo '  <td class="row2" style="text-align: right; width: 25%;">
-                ' . $lang->get('userpage_lbl_joined') . '
-              </td>
-              <td class="row1" style="text-align: left; width: 25%;">
-                ' . enano_date(ED_DATE | ED_TIME, $userdata['reg_time']) . '
-              </td>';
-      echo '  <td class="row2" style="text-align: right; width: 25%;">
-                ' . $lang->get('userpage_lbl_num_comments') . '
-              </td>
-              <td class="row1" style="text-align: left; width: 25%;">
-                ' . $userdata['n_comments'] . '
-              </td>';
-      echo '</tr>';
-      
-      // real name
-      if ( !empty($userdata['real_name']) )
-      {
-        echo '<tr>
-                <td class="row2" style="text-align: right;">
-                  ' . $lang->get('userpage_lbl_real_name') . '
-                </td>
-                <td class="row1" colspan="3" style="text-align: left;">
-                  ' . htmlspecialchars($userdata['real_name']) . '
-                </td>
-              </tr>';
-      }
-                
-      // latest comments
-      
-      echo '<tr><th class="subhead" colspan="4">' . $lang->get('userpage_heading_comments', array('username' => htmlspecialchars($target_username))) . '</th></tr>';
-      $q = $db->sql_query('SELECT page_id, namespace, subject, time FROM '.table_prefix.'comments WHERE name=\'' . $db->escape($target_username) . '\' AND user_id=' . $userdata['authoritative_uid'] . ' AND approved=1 ORDER BY time DESC LIMIT 7;');
-      if ( !$q )
-        $db->_die();
-      
-      $comments = Array();
-      $no_comments = false;
-      
-      if ( $row = $db->fetchrow() )
-      {
-        do 
-        {
-          $row['time'] = enano_date(ED_DATE, $row['time']);
-          $comments[] = $row;
-        }
-        while ( $row = $db->fetchrow() );
-      }
-      else
-      {
-        $no_comments = true;
-      }
-      
-      echo '<tr><td class="row3" colspan="4">';
-      echo '<div style="border: 1px solid #000000; padding: 0px; width: 100%; clip: rect(0px,auto,auto,0px); overflow: auto; background-color: transparent;" class="tblholder">';
-      
-      echo '<table border="0" cellspacing="1" cellpadding="4" style="width: 200%;"><tr>';
-      $class = 'row1';
-      
-      $tpl = '  <td class="{CLASS}">
-                  <a href="{PAGE_LINK}" <!-- BEGINNOT page_exists -->class="wikilink-nonexistent"<!-- END page_exists -->>{PAGE}</a><br />
-                  <small>{lang:userpage_comments_lbl_posted} {DATE}<br /></small>
-                  <b><a href="{COMMENT_LINK}">{SUBJECT}</a></b>
-                </td>';
-      $parser = $template->makeParserText($tpl);
-      
-      if ( count($comments) > 0 )
-      {
-        foreach ( $comments as $comment )
-        {
-          $c_page_id = $paths->nslist[ $comment['namespace'] ] . sanitize_page_id($comment['page_id']);
-          if ( isPage($c_page_id) )
-          {
-            $parser->assign_bool(array(
-              'page_exists' => true
-              ));
-            $page_title = get_page_title($c_page_id);
-          }
-          else
-          {
-            $parser->assign_bool(array(
-              'page_exists' => false
-              ));
-            $page_title = htmlspecialchars(dirtify_page_id($c_page_id));
-          }
-          $parser->assign_vars(array(
-              'CLASS' => $class,
-              'PAGE_LINK' => makeUrlNS($comment['namespace'], sanitize_page_id($comment['page_id'])),
-              'PAGE' => $page_title,
-              'SUBJECT' => $comment['subject'],
-              'DATE' => $comment['time'],
-              'COMMENT_LINK' => makeUrlNS($comment['namespace'], sanitize_page_id($comment['page_id']), 'do=comments', true)
-            ));
-          $class = ( $class == 'row3' ) ? 'row1' : 'row3';
-          echo $parser->run();
-        }
-      }
-      else
-      {
-        echo '<td class="' . $class . '">' . $lang->get('userpage_msg_no_comments') . '</td>';
-      }
-      echo '</tr></table>';
-      
-      echo '</div>';
-      echo '</td></tr>';
-      
-      $code = $plugins->setHook('userpage_sidebar_left');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-              
-      echo '  </table>
-            </div>';
-            
-      echo '</td>';
-      
-      //
-      // CONTACT INFORMATION
-      //
-      
-      echo '    <td valign="top" style="width: 150px; padding-left: 10px;">';
-      
-      echo '<div class="tblholder">
-              <table border="0" cellspacing="1" cellpadding="4">';
-      
-      //
-      // Main part of sidebar
-      //
-      
-      // Contact information
-      
-      echo '<tr><th class="subhead">' . $lang->get('userpage_heading_contact') . '</th></tr>';
-      
-      $class = 'row3';
-      
-      if ( $userdata['email_public'] == 1 )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        $email_link = $email->encryptEmail($userdata['email']);
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_email') . ' ' . $email_link . '</td></tr>';
-      }
-      
-      if ( !empty($userdata['user_homepage']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        echo '<tr><td class="' . $class . '">' . $lang->get('userpage_lbl_homepage') . '<br /><a href="' . $userdata['user_homepage'] . '">' . $userdata['user_homepage'] . '</a></td></tr>';
-      }
-      
-      $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-      if ( $session->user_logged_in )
-      {
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_btn_send_pm', array('username' => htmlspecialchars($target_username), 'pm_link' => makeUrlNS('Special', 'PrivateMessages/Compose/to/' . $this->page_id, false, true))) . '</td></tr>';
-      }
-      else
-      {
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_btn_send_pm_guest', array('username' => htmlspecialchars($target_username), 'login_flags' => 'href="' . makeUrlNS('Special', 'Login/' . $paths->nslist[$this->namespace] . $this->page_id) . '" onclick="ajaxStartLogin(); return false;"')) . '</td></tr>';
-      }
-      
-      if ( !empty($userdata['user_aim']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_aim') . ' ' . $userdata['user_aim'] . '</td></tr>';
-      }
-      
-      if ( !empty($userdata['user_yahoo']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_yim') . ' ' . $userdata['user_yahoo'] . '</td></tr>';
-      }
-      
-      if ( !empty($userdata['user_msn']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        $email_link = $email->encryptEmail($userdata['user_msn']);
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_wlm') . ' ' . $email_link . '</td></tr>';
-      }
-      
-      if ( !empty($userdata['user_xmpp']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        $email_link = $email->encryptEmail($userdata['user_xmpp']);
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_xmpp') . ' ' . $email_link . '</td></tr>';
-      }
-      
-      // Real life
-      
-      echo '<tr><th class="subhead">' . $lang->get('userpage_heading_real_life', array('username' => htmlspecialchars($target_username))) . '</th></tr>';
-      
-      if ( !empty($userdata['user_location']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_location') . ' ' . $userdata['user_location'] . '</td></tr>';
-      }
-      
-      if ( !empty($userdata['user_job']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_job') . ' ' . $userdata['user_job'] . '</td></tr>';
-      }
-      
-      if ( !empty($userdata['user_hobbies']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_hobbies') . ' ' . $userdata['user_hobbies'] . '</td></tr>';
-      }
-      
-      if ( empty($userdata['user_location']) && empty($userdata['user_job']) && empty($userdata['user_hobbies']) )
-      {
-        $class = ( $class == 'row1' ) ? 'row3' : 'row1';
-        echo '<tr><td class="'.$class.'">' . $lang->get('userpage_msg_no_contact_info', array('username' => htmlspecialchars($target_username))) . '</td></tr>';
-      }
-      
-      $code = $plugins->setHook('userpage_sidebar_right');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      
-      echo '  </table>
-            </div>';
-      echo '</td>';
-      
-      //
-      // End of profile
-      //
-      
-      echo '</tr></table>';
-      
-      echo '</div>'; // tab:profile
-    
-    }
-    
-    // User's own content
-    
-    echo '<span class="menuclear"></span>';
-    
-    echo '<div id="tab:content">';
-    
-    if ( $this->exists )
-    {
-      $this->send_from_db(true, false);
-    }
-    else
-    {
-      $this->error_404();
-    }
-    
-    echo '</div>'; // tab:content
-    
-    $code = $plugins->setHook('userpage_tabs_body');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    if ( $user_exists )
-    {
-      echo '</div>'; // userpage_wrap
-    }
-    else
-    {
-      if ( !is_valid_ip($target_username) )
-      {
-        echo '<p>' . $lang->get('userpage_msg_user_not_exist', array('username' => htmlspecialchars($target_username))) . '</p>';
-      }
-    }
-    
-    // if ( $send_headers )
-    // {
-    //  display_page_footers();
-    // }
-    
-    $output->footer();
-  }
+	public function __construct($page_id, $namespace, $revision_id = 0)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		parent::__construct($page_id, $namespace, $revision_id);
+		
+		if ( ( $this->title == str_replace('_', ' ', $this->page_id) || $this->title == $paths->nslist['User'] . str_replace('_', ' ', $this->page_id) ) || !$this->exists )
+		{
+			$this->title = $lang->get('userpage_page_title', array('username' => str_replace('_', ' ', dirtify_page_id($this->page_id))));
+			$this->cdata['name'] = $this->title;
+		}
+		
+	}
+	
+	public function send()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $email;
+		global $lang, $output;
+		
+		/**
+ 		* PLUGGING INTO USER PAGES
+ 		* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 		* Userpages are highly programmable and extendable using a number of
+ 		* hooks. These hooks are:
+ 		*
+ 		*   - userpage_sidebar_left
+ 		*   - userpage_sidebar_right
+ 		*   - userpage_tabs_links
+ 		*   - userpage_tabs_body
+ 		*
+ 		* You can add a variety of sections to user pages, including new tabs
+ 		* and new sections on the tables. To add a tab, attach to
+ 		* userpage_tabs_links and echo out:
+ 		*
+ 		*   <li><a href="#tab:YOURTABID">YOUR TAB TEXT</a></li>
+ 		*
+ 		* Then hook into userpage_tabs_body and echo out:
+ 		*
+ 		*   <div id="tab:YOURTABID">YOUR TAB CONTENT</div>
+ 		*
+ 		* The userpage javascript runtime will take care of everything else,
+ 		* meaning transitions, click events, etc. Currently it's not possible
+ 		* to add custom click events to tabs, but any DOM-related JS that needs
+ 		* to run in your tab can be run onload and the effects will be seen when
+ 		* your tab is clicked. YOURTABID should be lowercase alphanumeric and
+ 		* have a short prefix so as to assure that it remains specific to your
+ 		* plugin.
+ 		*
+ 		* To hook into the "profile" tab, use userpage_sidebar_{left,right}. Just
+ 		* echo out table cells as normal. The table on the left (the wide one) has
+ 		* four columns, and the one on the right has one column.
+ 		* 
+ 		* See plugins.php for a guide on creating and attaching to hooks.
+ 		*/
+ 		
+		$page_urlname = dirtify_page_id($this->page_id);
+		
+		$target_username = strtr($page_urlname, 
+			Array(
+				'_' => ' ',
+				'<' => '&lt;',
+				'>' => '&gt;'
+				));
+		
+		$target_username = preg_replace('/^' . str_replace('/', '\\/', preg_quote($paths->nslist['User'])) . '/', '', $target_username);
+		list($target_username) = explode('/', $target_username);
+		
+		$ux_columns = $db->columns_in(table_prefix . 'users_extra');
+		
+		$output->set_title($this->title);
+		$q = $db->sql_query('SELECT u.username, u.user_id AS authoritative_uid, u.real_name, u.email, u.reg_time, u.user_has_avatar, u.avatar_type, x.*, COUNT(c.comment_id) AS n_comments
+ 													FROM '.table_prefix.'users u
+ 													LEFT JOIN '.table_prefix.'users_extra AS x
+ 														ON ( u.user_id = x.user_id OR x.user_id IS NULL ) 
+ 													LEFT JOIN '.table_prefix.'comments AS c
+ 														ON ( ( c.user_id=u.user_id AND c.name=u.username AND c.approved=1 ) OR ( c.comment_id IS NULL AND c.approved IS NULL ) )
+ 													WHERE u.username=\'' . $db->escape($target_username) . '\'
+ 													GROUP BY u.username, u.user_id, u.real_name, u.email, u.reg_time, u.user_has_avatar, u.avatar_type, x.' . implode(', x.', $ux_columns) . ';');
+		if ( !$q )
+			$db->_die();
+		
+		$user_exists = true;
+		
+		if ( $db->numrows() < 1 )
+		{
+			$user_exists = false;
+		}
+		else
+		{
+			$userdata = $db->fetchrow();
+			if ( $userdata['authoritative_uid'] == 1 )
+			{
+				// Hide data for anonymous user
+				$user_exists = false;
+				unset($userdata);
+			}
+		}
+		
+		// get the user's rank
+		if ( $user_exists )
+		{
+			$rank_data = $session->get_user_rank(intval($userdata['authoritative_uid']));
+		}
+		else
+		{
+			// get the rank data for the anonymous user (placeholder basically)
+			$rank_data = $session->get_user_rank(1);
+		}
+		
+		// add the userpage script to the header
+		$template->add_header('<script type="text/javascript" src="' . cdnPath . '/includes/clientside/static/userpage.js"></script>');
+		
+		$output->header();
+		
+		// if ( $send_headers )
+		// {
+		//  display_page_headers();
+		// }
+ 	
+		//
+		// BASIC INFORMATION
+		// Presentation of username/rank/avatar/basic info
+		//
+		
+		if ( $user_exists )
+		{
+		
+			?>
+			<div id="userpage_wrap">
+				<ul id="userpage_links">
+					<li><a href="#tab:profile"><?php echo $lang->get('userpage_tab_profile'); ?></a></li>
+					<li><a href="#tab:content"><?php echo $lang->get('userpage_tab_content'); ?></a></li>
+					<?php
+					$code = $plugins->setHook('userpage_tabs_links');
+					foreach ( $code as $cmd )
+					{
+						eval($cmd);
+					}
+					?>
+				</ul>
+				
+				<div id="tab:profile">
+			
+			<?php
+			
+			echo '<table border="0" cellspacing="0" cellpadding="0">
+							<tr>';
+								
+			echo '    <td valign="top">';
+			
+			echo '<div class="tblholder">
+							<table border="0" cellspacing="1" cellpadding="4">';
+							
+			// heading
+			echo '    <tr>
+									<th colspan="' . ( $session->user_level >= USER_LEVEL_ADMIN ? '3' : '4' ) . '">
+										' . $lang->get('userpage_heading_basics', array('username' => htmlspecialchars($target_username))) . '
+									</th>
+									' . (
+										$session->user_level >= USER_LEVEL_ADMIN ?
+										'<th class="subhead" style="width: 25%;"><a href="' . makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'UserManager&src=get&user=' . urlencode($target_username), true) . '" onclick="ajaxAdminUser(\'' . addslashes($target_username) . '\'); return false;">&raquo; ' . $lang->get('userpage_btn_administer_user') . '</a></th>'
+											: ''
+									) . '
+								</tr>';
+								
+			// avi/rank/username
+			echo '    <tr>
+									<td class="row3" colspan="4">
+										' . (
+												$userdata['user_has_avatar'] == 1 ?
+												'<div style="float: left; margin-right: 10px;">
+													<img alt="' . $lang->get('usercp_avatar_image_alt', array('username' => $userdata['username'])) . '" src="' . make_avatar_url(intval($userdata['authoritative_uid']), $userdata['avatar_type'], $userdata['email']) . '" />
+ 												</div>'
+												: ''
+											) . '
+											<span style="font-size: x-large; ' . $rank_data['rank_style'] . '">' . htmlspecialchars($userdata['username']) . '</span>
+											' . ( !empty($rank_data['user_title']) ? '<br />' . htmlspecialchars($rank_data['user_title']) : '' ) . '
+											' . ( !empty($rank_data['rank_title']) ? '<br />' . htmlspecialchars($lang->get($rank_data['rank_title'])) : '' ) . '
+									</td>
+								</tr>';
+								
+			// join date & total comments
+			echo '<tr>';
+			echo '  <td class="row2" style="text-align: right; width: 25%;">
+								' . $lang->get('userpage_lbl_joined') . '
+							</td>
+							<td class="row1" style="text-align: left; width: 25%;">
+								' . enano_date(ED_DATE | ED_TIME, $userdata['reg_time']) . '
+							</td>';
+			echo '  <td class="row2" style="text-align: right; width: 25%;">
+								' . $lang->get('userpage_lbl_num_comments') . '
+							</td>
+							<td class="row1" style="text-align: left; width: 25%;">
+								' . $userdata['n_comments'] . '
+							</td>';
+			echo '</tr>';
+			
+			// real name
+			if ( !empty($userdata['real_name']) )
+			{
+				echo '<tr>
+								<td class="row2" style="text-align: right;">
+									' . $lang->get('userpage_lbl_real_name') . '
+								</td>
+								<td class="row1" colspan="3" style="text-align: left;">
+									' . htmlspecialchars($userdata['real_name']) . '
+								</td>
+							</tr>';
+			}
+								
+			// latest comments
+			
+			echo '<tr><th class="subhead" colspan="4">' . $lang->get('userpage_heading_comments', array('username' => htmlspecialchars($target_username))) . '</th></tr>';
+			$q = $db->sql_query('SELECT page_id, namespace, subject, time FROM '.table_prefix.'comments WHERE name=\'' . $db->escape($target_username) . '\' AND user_id=' . $userdata['authoritative_uid'] . ' AND approved=1 ORDER BY time DESC LIMIT 7;');
+			if ( !$q )
+				$db->_die();
+			
+			$comments = Array();
+			$no_comments = false;
+			
+			if ( $row = $db->fetchrow() )
+			{
+				do 
+				{
+					$row['time'] = enano_date(ED_DATE, $row['time']);
+					$comments[] = $row;
+				}
+				while ( $row = $db->fetchrow() );
+			}
+			else
+			{
+				$no_comments = true;
+			}
+			
+			echo '<tr><td class="row3" colspan="4">';
+			echo '<div style="border: 1px solid #000000; padding: 0px; width: 100%; clip: rect(0px,auto,auto,0px); overflow: auto; background-color: transparent;" class="tblholder">';
+			
+			echo '<table border="0" cellspacing="1" cellpadding="4" style="width: 200%;"><tr>';
+			$class = 'row1';
+			
+			$tpl = '  <td class="{CLASS}">
+									<a href="{PAGE_LINK}" <!-- BEGINNOT page_exists -->class="wikilink-nonexistent"<!-- END page_exists -->>{PAGE}</a><br />
+									<small>{lang:userpage_comments_lbl_posted} {DATE}<br /></small>
+									<b><a href="{COMMENT_LINK}">{SUBJECT}</a></b>
+								</td>';
+			$parser = $template->makeParserText($tpl);
+			
+			if ( count($comments) > 0 )
+			{
+				foreach ( $comments as $comment )
+				{
+					$c_page_id = $paths->nslist[ $comment['namespace'] ] . sanitize_page_id($comment['page_id']);
+					if ( isPage($c_page_id) )
+					{
+						$parser->assign_bool(array(
+							'page_exists' => true
+							));
+						$page_title = get_page_title($c_page_id);
+					}
+					else
+					{
+						$parser->assign_bool(array(
+							'page_exists' => false
+							));
+						$page_title = htmlspecialchars(dirtify_page_id($c_page_id));
+					}
+					$parser->assign_vars(array(
+							'CLASS' => $class,
+							'PAGE_LINK' => makeUrlNS($comment['namespace'], sanitize_page_id($comment['page_id'])),
+							'PAGE' => $page_title,
+							'SUBJECT' => $comment['subject'],
+							'DATE' => $comment['time'],
+							'COMMENT_LINK' => makeUrlNS($comment['namespace'], sanitize_page_id($comment['page_id']), 'do=comments', true)
+						));
+					$class = ( $class == 'row3' ) ? 'row1' : 'row3';
+					echo $parser->run();
+				}
+			}
+			else
+			{
+				echo '<td class="' . $class . '">' . $lang->get('userpage_msg_no_comments') . '</td>';
+			}
+			echo '</tr></table>';
+			
+			echo '</div>';
+			echo '</td></tr>';
+			
+			$code = $plugins->setHook('userpage_sidebar_left');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+							
+			echo '  </table>
+						</div>';
+						
+			echo '</td>';
+			
+			//
+			// CONTACT INFORMATION
+			//
+			
+			echo '    <td valign="top" style="width: 150px; padding-left: 10px;">';
+			
+			echo '<div class="tblholder">
+							<table border="0" cellspacing="1" cellpadding="4">';
+			
+			//
+			// Main part of sidebar
+			//
+			
+			// Contact information
+			
+			echo '<tr><th class="subhead">' . $lang->get('userpage_heading_contact') . '</th></tr>';
+			
+			$class = 'row3';
+			
+			if ( $userdata['email_public'] == 1 )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				$email_link = $email->encryptEmail($userdata['email']);
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_email') . ' ' . $email_link . '</td></tr>';
+			}
+			
+			if ( !empty($userdata['user_homepage']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				echo '<tr><td class="' . $class . '">' . $lang->get('userpage_lbl_homepage') . '<br /><a href="' . $userdata['user_homepage'] . '">' . $userdata['user_homepage'] . '</a></td></tr>';
+			}
+			
+			$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+			if ( $session->user_logged_in )
+			{
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_btn_send_pm', array('username' => htmlspecialchars($target_username), 'pm_link' => makeUrlNS('Special', 'PrivateMessages/Compose/to/' . $this->page_id, false, true))) . '</td></tr>';
+			}
+			else
+			{
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_btn_send_pm_guest', array('username' => htmlspecialchars($target_username), 'login_flags' => 'href="' . makeUrlNS('Special', 'Login/' . $paths->nslist[$this->namespace] . $this->page_id) . '" onclick="ajaxStartLogin(); return false;"')) . '</td></tr>';
+			}
+			
+			if ( !empty($userdata['user_aim']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_aim') . ' ' . $userdata['user_aim'] . '</td></tr>';
+			}
+			
+			if ( !empty($userdata['user_yahoo']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_yim') . ' ' . $userdata['user_yahoo'] . '</td></tr>';
+			}
+			
+			if ( !empty($userdata['user_msn']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				$email_link = $email->encryptEmail($userdata['user_msn']);
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_wlm') . ' ' . $email_link . '</td></tr>';
+			}
+			
+			if ( !empty($userdata['user_xmpp']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				$email_link = $email->encryptEmail($userdata['user_xmpp']);
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_xmpp') . ' ' . $email_link . '</td></tr>';
+			}
+			
+			// Real life
+			
+			echo '<tr><th class="subhead">' . $lang->get('userpage_heading_real_life', array('username' => htmlspecialchars($target_username))) . '</th></tr>';
+			
+			if ( !empty($userdata['user_location']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_location') . ' ' . $userdata['user_location'] . '</td></tr>';
+			}
+			
+			if ( !empty($userdata['user_job']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_job') . ' ' . $userdata['user_job'] . '</td></tr>';
+			}
+			
+			if ( !empty($userdata['user_hobbies']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_lbl_hobbies') . ' ' . $userdata['user_hobbies'] . '</td></tr>';
+			}
+			
+			if ( empty($userdata['user_location']) && empty($userdata['user_job']) && empty($userdata['user_hobbies']) )
+			{
+				$class = ( $class == 'row1' ) ? 'row3' : 'row1';
+				echo '<tr><td class="'.$class.'">' . $lang->get('userpage_msg_no_contact_info', array('username' => htmlspecialchars($target_username))) . '</td></tr>';
+			}
+			
+			$code = $plugins->setHook('userpage_sidebar_right');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			
+			echo '  </table>
+						</div>';
+			echo '</td>';
+			
+			//
+			// End of profile
+			//
+			
+			echo '</tr></table>';
+			
+			echo '</div>'; // tab:profile
+		
+		}
+		
+		// User's own content
+		
+		echo '<span class="menuclear"></span>';
+		
+		echo '<div id="tab:content">';
+		
+		if ( $this->exists )
+		{
+			$this->send_from_db(true, false);
+		}
+		else
+		{
+			$this->error_404();
+		}
+		
+		echo '</div>'; // tab:content
+		
+		$code = $plugins->setHook('userpage_tabs_body');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		if ( $user_exists )
+		{
+			echo '</div>'; // userpage_wrap
+		}
+		else
+		{
+			if ( !is_valid_ip($target_username) )
+			{
+				echo '<p>' . $lang->get('userpage_msg_user_not_exist', array('username' => htmlspecialchars($target_username))) . '</p>';
+			}
+		}
+		
+		// if ( $send_headers )
+		// {
+		//  display_page_footers();
+		// }
+		
+		$output->footer();
+	}
 }
 
--- a/includes/output.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/output.php	Sun Mar 28 23:10:46 2010 -0400
@@ -20,111 +20,111 @@
 
 abstract class Output_Base
 {
-  /**
-   * Page title
-   * @var string
-   */
-  
-  public $title = 'Untitled';
-  
-  /**
-   * To allow scripts to determine whether we are outputting headers or not.
-   * @var bool
-   */
-  
-  public $naked = false;
-  
-  /**
-   * Added content
-   * @var string
-   * @var string
-   * @var string
-   * @var string
-   */
-  
-  public $before_header = '', $after_header = '', $before_footer = '', $after_footer = '';
-  
-  /**
-   * Call this to send content headers (e.g. the first third of the document if HTML) in place of $template->header().
-   * @access public
-   */
-  
-  abstract public function header();
-  
-  /**
-   * Call this to send extra stuff after the content (equivalent of $template->footer()).
-   * @access public
-   */
-  
-  abstract public function footer();
-  
-  /**
-   * Add some code just before the header.
-   * @access public
-   */
-  
-  public function add_before_header($code)
-  {
-    $this->before_header .= $code;
-  }
-  
-  /**
-   * Add some code just after the header.
-   * @access public
-   */
-  
-  public function add_after_header($code)
-  {
-    $this->after_header .= $code;
-  }
-  
-  /**
-   * Add some code just before the footer.
-   * @access public
-   */
-  
-  public function add_before_footer($code)
-  {
-    $this->before_footer .= $code;
-  }
-  
-  /**
-   * Add some code just after the footer.
-   * @access public
-   */
-  
-  public function add_after_footer($code)
-  {
-    $this->after_footer .= $code;
-  }
-  
-  /**
-   * Send any required HTML headers through, e.g. Content-type.
-   * @access public
-   */
-  
-  public function http_headers()
-  {
-    header('Content-type: text/html');
-  }
-  
-  /**
-   * Set the title of the page being output.
-   * @param string Page name
-   */
-  
-  public function set_title($title)
-  {
-    $this->title = $title;
-  }
-  
-  /**
-   * Avoid sending things out of order.
-   * @var bool
-   * @var bool
-   */
-  
-  public $headers_sent = false, $footers_sent = false;
+	/**
+ 	* Page title
+ 	* @var string
+ 	*/
+	
+	public $title = 'Untitled';
+	
+	/**
+ 	* To allow scripts to determine whether we are outputting headers or not.
+ 	* @var bool
+ 	*/
+	
+	public $naked = false;
+	
+	/**
+ 	* Added content
+ 	* @var string
+ 	* @var string
+ 	* @var string
+ 	* @var string
+ 	*/
+	
+	public $before_header = '', $after_header = '', $before_footer = '', $after_footer = '';
+	
+	/**
+ 	* Call this to send content headers (e.g. the first third of the document if HTML) in place of $template->header().
+ 	* @access public
+ 	*/
+	
+	abstract public function header();
+	
+	/**
+ 	* Call this to send extra stuff after the content (equivalent of $template->footer()).
+ 	* @access public
+ 	*/
+	
+	abstract public function footer();
+	
+	/**
+ 	* Add some code just before the header.
+ 	* @access public
+ 	*/
+	
+	public function add_before_header($code)
+	{
+		$this->before_header .= $code;
+	}
+	
+	/**
+ 	* Add some code just after the header.
+ 	* @access public
+ 	*/
+	
+	public function add_after_header($code)
+	{
+		$this->after_header .= $code;
+	}
+	
+	/**
+ 	* Add some code just before the footer.
+ 	* @access public
+ 	*/
+	
+	public function add_before_footer($code)
+	{
+		$this->before_footer .= $code;
+	}
+	
+	/**
+ 	* Add some code just after the footer.
+ 	* @access public
+ 	*/
+	
+	public function add_after_footer($code)
+	{
+		$this->after_footer .= $code;
+	}
+	
+	/**
+ 	* Send any required HTML headers through, e.g. Content-type.
+ 	* @access public
+ 	*/
+	
+	public function http_headers()
+	{
+		header('Content-type: text/html');
+	}
+	
+	/**
+ 	* Set the title of the page being output.
+ 	* @param string Page name
+ 	*/
+	
+	public function set_title($title)
+	{
+		$this->title = $title;
+	}
+	
+	/**
+ 	* Avoid sending things out of order.
+ 	* @var bool
+ 	* @var bool
+ 	*/
+	
+	public $headers_sent = false, $footers_sent = false;
 }
 
 /**
@@ -133,62 +133,62 @@
 
 class Output_HTML extends Output_Base
 {
-  public function header()
-  {
-    if ( $this->headers_sent )
-      return;
-    
-    $this->headers_sent = true;
-    
-    ob_start();
-  }
-  
-  public function footer()
-  {
-    global $template;
-    if ( !$this->headers_sent )
-      return;
-    
-    $this->headers_sent = false;
-    $content = ob_get_contents();
-    ob_end_clean();
-    
-    ob_start();
-    echo $this->before_header;
-    echo $template->getHeader();
-    echo $this->after_header;
-    echo $content;
-    echo $this->before_footer;
-    echo $template->getFooter();
-    echo $this->after_footer;
-    
-    global $aggressive_optimize_html;
-    if ( $aggressive_optimize_html )
-    {
-      $content = ob_get_contents();
-      ob_end_clean();
-      
-      ob_start();
-      echo aggressive_optimize_html($content);
-    }
-    else
-    {
-      $content = ob_get_contents();
-      ob_end_clean();
-      
-      ob_start();
-      echo preg_replace('~</?enano:no-opt>~', '', $content);
-    }
-    
-  }
-  
-  public function set_title($title)
-  {
-    global $template;
-    $template->assign_vars(array(
-        'PAGE_NAME' => htmlspecialchars($title)
-      ));
-  }
+	public function header()
+	{
+		if ( $this->headers_sent )
+			return;
+		
+		$this->headers_sent = true;
+		
+		ob_start();
+	}
+	
+	public function footer()
+	{
+		global $template;
+		if ( !$this->headers_sent )
+			return;
+		
+		$this->headers_sent = false;
+		$content = ob_get_contents();
+		ob_end_clean();
+		
+		ob_start();
+		echo $this->before_header;
+		echo $template->getHeader();
+		echo $this->after_header;
+		echo $content;
+		echo $this->before_footer;
+		echo $template->getFooter();
+		echo $this->after_footer;
+		
+		global $aggressive_optimize_html;
+		if ( $aggressive_optimize_html )
+		{
+			$content = ob_get_contents();
+			ob_end_clean();
+			
+			ob_start();
+			echo aggressive_optimize_html($content);
+		}
+		else
+		{
+			$content = ob_get_contents();
+			ob_end_clean();
+			
+			ob_start();
+			echo preg_replace('~</?enano:no-opt>~', '', $content);
+		}
+		
+	}
+	
+	public function set_title($title)
+	{
+		global $template;
+		$template->assign_vars(array(
+				'PAGE_NAME' => htmlspecialchars($title)
+			));
+	}
 }
 
 /**
@@ -197,43 +197,43 @@
 
 class Output_HTML_Simple extends Output_HTML
 {
-  public function footer()
-  {
-    global $template;
-    if ( !$this->headers_sent )
-      return;
-    
-    $this->headers_sent = false;
-    $content = ob_get_contents();
-    ob_end_clean();
-    
-    ob_start();
-    echo $this->before_header;
-    echo $template->getHeader(true);
-    echo $this->after_header;
-    echo $content;
-    echo $this->before_footer;
-    echo $template->getFooter(true);
-    echo $this->after_footer;
-    
-    global $aggressive_optimize_html;
-    if ( $aggressive_optimize_html )
-    {
-      $content = ob_get_contents();
-      ob_end_clean();
-      
-      ob_start();
-      echo aggressive_optimize_html($content);
-    }
-    else
-    {
-      $content = ob_get_contents();
-      ob_end_clean();
-      
-      ob_start();
-      echo preg_replace('~</?enano:no-opt>~', '', $content);
-    }
-  }
+	public function footer()
+	{
+		global $template;
+		if ( !$this->headers_sent )
+			return;
+		
+		$this->headers_sent = false;
+		$content = ob_get_contents();
+		ob_end_clean();
+		
+		ob_start();
+		echo $this->before_header;
+		echo $template->getHeader(true);
+		echo $this->after_header;
+		echo $content;
+		echo $this->before_footer;
+		echo $template->getFooter(true);
+		echo $this->after_footer;
+		
+		global $aggressive_optimize_html;
+		if ( $aggressive_optimize_html )
+		{
+			$content = ob_get_contents();
+			ob_end_clean();
+			
+			ob_start();
+			echo aggressive_optimize_html($content);
+		}
+		else
+		{
+			$content = ob_get_contents();
+			ob_end_clean();
+			
+			ob_start();
+			echo preg_replace('~</?enano:no-opt>~', '', $content);
+		}
+	}
 }
 
 /**
@@ -242,17 +242,17 @@
 
 class Output_Striptease extends Output_HTML
 {
-  public function header()
-  {
-    echo $this->before_header;
-    echo $this->after_header;
-  }
-  
-  public function footer()
-  {
-    echo $this->before_footer;
-    echo $this->after_footer;
-  }
+	public function header()
+	{
+		echo $this->before_header;
+		echo $this->after_header;
+	}
+	
+	public function footer()
+	{
+		echo $this->before_footer;
+		echo $this->after_footer;
+	}
 }
 
 /**
@@ -261,15 +261,15 @@
 
 class Output_Naked extends Output_HTML
 {
-  public $naked = true;
-  
-  public function header()
-  {
-  }
-  
-  public function footer()
-  {
-  }
+	public $naked = true;
+	
+	public function header()
+	{
+	}
+	
+	public function footer()
+	{
+	}
 }
 
 /**
@@ -278,47 +278,47 @@
 
 class Output_Safe
 {
-  protected $template;
-  protected $headers_sent = false;
-  public function __construct()
-  {
-    $this->template = new template_nodb();
-    $theme = ( defined('ENANO_CONFIG_FETCHED') ) ? getConfig('theme_default') : 'oxygen';
-    $style = ( defined('ENANO_CONFIG_FETCHED') ) ? '__foo__' : 'bleu';
-    
-    $this->template->load_theme($theme, $style);
-    $this->template->tpl_strings['SITE_NAME'] = getConfig('site_name');
-    $this->template->tpl_strings['SITE_DESC'] = getConfig('site_desc');
-    $this->template->tpl_strings['COPYRIGHT'] = getConfig('copyright_notice');
-    $this->template->tpl_strings['PAGE_NAME'] = 'Untitled';
-  }
-  public function header()
-  {
-    if ( $this->headers_sent )
-      return;
-    
-    $this->headers_sent = true;
-    
-    $this->template->header();
-  }
-  
-  public function footer()
-  {
-    global $template;
-    if ( !$this->headers_sent )
-    {
-      $this->template->header();
-    }
-    
-    $this->headers_sent = false;
-    $this->template->footer();
-    
-  }
-  
-  public function set_title($title)
-  {
-    $this->template->tpl_strings['PAGE_NAME'] = $title;
-  }
+	protected $template;
+	protected $headers_sent = false;
+	public function __construct()
+	{
+		$this->template = new template_nodb();
+		$theme = ( defined('ENANO_CONFIG_FETCHED') ) ? getConfig('theme_default') : 'oxygen';
+		$style = ( defined('ENANO_CONFIG_FETCHED') ) ? '__foo__' : 'bleu';
+		
+		$this->template->load_theme($theme, $style);
+		$this->template->tpl_strings['SITE_NAME'] = getConfig('site_name');
+		$this->template->tpl_strings['SITE_DESC'] = getConfig('site_desc');
+		$this->template->tpl_strings['COPYRIGHT'] = getConfig('copyright_notice');
+		$this->template->tpl_strings['PAGE_NAME'] = 'Untitled';
+	}
+	public function header()
+	{
+		if ( $this->headers_sent )
+			return;
+		
+		$this->headers_sent = true;
+		
+		$this->template->header();
+	}
+	
+	public function footer()
+	{
+		global $template;
+		if ( !$this->headers_sent )
+		{
+			$this->template->header();
+		}
+		
+		$this->headers_sent = false;
+		$this->template->footer();
+		
+	}
+	
+	public function set_title($title)
+	{
+		$this->template->tpl_strings['PAGE_NAME'] = $title;
+	}
 }
 
 ?>
--- a/includes/pageprocess.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/pageprocess.php	Sun Mar 28 23:10:46 2010 -0400
@@ -23,1271 +23,1271 @@
 
 class PageProcessor
 {
-  
-  /**
-   * Page ID and namespace of the page handled by this instance
-   * @var string
-   */
-  
-  var $page_id;
-  var $namespace;
-  
-  /**
-   * The instance of the namespace processor for the namespace we're doing.
-   * @var object
-   */
-  
-  var $ns;
-  
-  /**
-   * The title of the page sent to the template parser
-   * @var string
-   */
-  
-  var $title = '';
-  
-  /**
-   * The information about the page(s) we were redirected from
-   * @var array
-   */
-  
-  var $redirect_stack = array();
-  
-  /**
-   * The revision ID (history entry) to send. If set to 0 (the default) then the most recent revision will be sent.
-   * @var int
-   */
-  
-  var $revision_id = 0;
-  
-  /**
-   * The time this revision was saved, as a UNIX timestamp
-   * @var int
-   */
-  
-  var $revision_time = 0;
-  
-  /**
-   * Unsanitized page ID.
-   * @var string
-   */
-  
-  var $page_id_unclean;
-  
-  /**
-   * Tracks if the page we're loading exists in the database or not.
-   * @var bool
-   */
-  
-  var $page_exists = false;
-  
-  /**
-   * Permissions!
-   * @var object
-   */
-  
-  var $perms = null;
-  
-  /**
-   * The SHA1 hash of the user-inputted password for the page
-   * @var string
-   */
-   
-  var $password = '';
-  
-  /**
-   * Switch to track if redirects are allowed. Defaults to true.
-   * @var bool
-   */
-  
-  var $allow_redir = true;
-  
-  /**
-   * Holds any error message from redirection code. Defaults to false (no error).
-   * @var mixed
-   */
-   
-  var $redir_error = false;
-  
-  /**
-   * If this is set to true, this will call the header and footer funcs on $template when render() is called.
-   * @var bool
-   */
-  
-  var $send_headers = false;
-  
-  /**
-   * Cache the fetched text so we don't fetch it from the DB twice.
-   * @var string
-   */
-  
-  var $text_cache = '';
-  
-  /**
-   * Debugging information to track errors. You can set enable to false to disable sending debug information.
-   * @var array
-   */
-  
-  var $debug = array(
-      'enable' => false,
-      'works'  => false
-    );
-  
-  /**
-   * The list of errors raised in the class.
-   * @var array
-   */
-  
-  var $_errors = array();
-  
-  /**
-   * Constructor.
-   * @param string The page ID (urlname) of the page
-   * @param string The namespace of the page
-   * @param int Optional. The revision ID to send.
-   */
-  
-  function __construct( $page_id, $namespace, $revision_id = 0 )
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    profiler_log("PageProcessor [{$namespace}:{$page_id}]: Started constructor");
-    
-    // See if we can get some debug info
-    if ( function_exists('debug_backtrace') && $this->debug['enable'] )
-    {
-      $this->debug['works'] = true;
-      $this->debug['backtrace'] = enano_debug_print_backtrace(true);
-    }
-    
-    // First things first - check page existence and permissions
-    
-    if ( !isset($paths->nslist[$namespace]) )
-    {
-      $this->send_error('The namespace "' . htmlspecialchars($namespace) . '" does not exist.');
-    }
-    
-    if ( !is_int($revision_id) )
-      $revision_id = 0;
-    
-    $this->_setup( $page_id, $namespace, $revision_id );
-  }
-  
-  /**
-   * The main method to send the page content. Also responsible for checking permissions and calling the statistics counter.
-   * @param bool If true, the stat counter is called. Defaults to false.
-   */
-  
-  function send( $do_stats = false )
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang, $output;
-    
-    profiler_log('PageProcessor: send() called');
-    
-    if ( !$this->perms->get_permissions('read') )
-    {
-      // Permission denied to read page. Is this one of our core pages that must always be allowed?
-      // NOTE: Not even the administration panel will work if ACLs deny access to it.
-      if ( $this->namespace == 'Special' && in_array($this->page_id, array('Login', 'Logout', 'LangExportJSON', 'CSS')) )
-      {
-        // Do nothing; allow execution to continue
-      }
-      else
-      {
-        // Page isn't whitelisted, behave as normal
-        $this->err_access_denied();
-        return false;
-      }
-    }
-    if ( $this->revision_id > 0 && !$this->perms->get_permissions('history_view') )
-    {
-      $this->err_access_denied();
-      return false;
-    }
-    
-    // Is there a custom function registered for handling this namespace?
-    // DEPRECATED (even though it only saw its way into one alpha release.)
-    if ( $proc = $paths->get_namespace_processor($this->namespace) )
-    {
-      // yes, just call that
-      // this is protected aggressively by the PathManager against overriding critical namespaces
-      return call_user_func($proc, $this);
-    }
-    
-    $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
-    $strict_no_headers = false;
-    $admin_fail = false;
-    if ( $this->namespace == 'Admin' && strstr($this->page_id, '/') )
-    {
-      $this->page_id = substr($this->page_id, 0, strpos($this->page_id, '/'));
-      $funcname = "page_{$this->namespace}_{$this->page_id}";
-      if ( function_exists($funcname) )
-      {
-        $this->page_exists = true;
-      }
-    }
-    if ( isPage($pathskey) )
-    {
-      $cdata = $this->ns->get_cdata();
-      
-      if ( $cdata['special'] == 1 )
-      {
-        $this->send_headers = false;
-        $strict_no_headers = true;
-        $GLOBALS['output'] = new Output_Naked();
-      }
-      if ( isset($cdata['password']) )
-      {
-        if ( $cdata['password'] != '' && $cdata['password'] != sha1('') )
-        {
-          $password =& $cdata['password'];
-          if ( $this->password != $password )
-          {
-            $this->err_wrong_password();
-            return false;
-          }
-        }
-      }
-      if ( isset($cdata['require_admin']) && $cdata['require_admin'] )
-      {
-        if ( $session->auth_level < USER_LEVEL_ADMIN )
-        {
-          $admin_fail = true;
-        }
-      }
-    }
-    else if ( $this->namespace === $paths->namespace && $this->page_id == $paths->page_id )
-    {
-      if ( isset($paths->cpage['require_admin']) && $paths->cpage['require_admin'] )
-      {
-        if ( $session->auth_level < USER_LEVEL_ADMIN )
-        {
-          $admin_fail = true;
-        }
-      }
-    }
-    if ( $admin_fail )
-    {
-      header('Content-type: text/javascript');
-      echo enano_json_encode(array(
-          'mode' => 'error',
-          'error' => 'need_auth_to_admin'
-        ));
-      return true;
-    }
-    if ( $this->page_exists && $this->namespace != 'Special' && $this->namespace != 'Admin' && $do_stats )
-    {
-      require_once(ENANO_ROOT.'/includes/stats.php');
-      doStats($this->page_id, $this->namespace);
-    }
-    
-    // We are all done. Ship off the page.
-    
-    if ( !$this->allow_redir )
-    {
-      if ( method_exists($this->ns, 'get_redirect') )
-      {
-        if ( $result = $this->ns->get_redirect() )
-          display_redirect_notice($result['page_id'], $result['namespace']);
-      }
-    }
-    else
-    {
-      $this->process_redirects();
-      
-      if ( count($this->redirect_stack) > 0 )
-      {
-        $stack = array_reverse($this->redirect_stack);
-        foreach ( $stack as $stackel )
-        {
-          $url = makeUrlNS($stackel['old_namespace'], $stackel['old_page_id'], 'redirect=no', true);
-          $page_data = $this->ns->get_cdata();
-          $title = $stackel['old_title'];
-          $a = '<a href="' . $url . '">' . htmlspecialchars($title) . '</a>';
-          $output->add_after_header('<small>' . $lang->get('page_msg_redirected_from', array('from' => $a)) . '<br /></small>');
-        }
-        $template->set_page($this);
-      }
-      
-      if ( $this->redir_error )
-      {
-        $output->add_after_header('<div class="usermessage"><b>' . $this->redir_error . '</b></div>');
-        $result = $this->ns->get_redirect();
-        display_redirect_notice($result['page_id'], $result['namespace']);
-      }
-    }
-    
-    $this->ns->send();
-  }
-  
-  /**
-   * Sends the page through by fetching it from the database.
-   */
-   
-  function send_from_db($strict_no_headers = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $this->ns->send_from_db();
-  }
-  
-  /**
-   * Fetches the wikitext or HTML source for the page.
-   * @return string
-   */
-  
-  function fetch_source()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !$this->perms->get_permissions('view_source') )
-    {
-      return false;
-    }
-    if ( !$this->page_exists )
-    {
-      return '';
-    }
-    $cdata = $this->ns->get_cdata();
-    if ( isset($cdata['password']) )
-    {
-      if ( $cdata['password'] != sha1('') && $cdata['password'] !== $this->password && !empty($cdata['password']) )
-      {
-        return false;
-      }
-    }
-    return $this->fetch_text();
-  }
-  
-  /**
-   * Updates (saves/changes/edits) the content of the page.
-   * @param string The new text for the page
-   * @param string A summary of edits made to the page.
-   * @param bool If true, the edit is marked as a minor revision
-   * @param string Page format - wikitext or xhtml. REQUIRED, and new in 1.1.6.
-   * @return bool True on success, false on failure. When returning false, it will push errors to the PageProcessor error stack; read with $page->pop_error()
-   */
-  
-  function update_page($text, $edit_summary = false, $minor_edit = false, $page_format)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    // Create the page if it doesn't exist
-    if ( !$this->page_exists )
-    {
-      if ( !$this->create_page() )
-      {
-        return false;
-      }
-    }
-      
-    //
-    // Validation
-    //
-    
-    $page_id = $db->escape($this->page_id);
-    $namespace = $db->escape($this->namespace);
-    
-    $q = $db->sql_query('SELECT protected FROM ' . table_prefix . "pages WHERE urlname='$page_id' AND namespace='$namespace';");
-    if ( !$q )
-      $db->_die('PageProcess updating page content');
-    if ( $db->numrows() < 1 )
-    {
-      $this->raise_error($lang->get('editor_err_no_rows'));
-      return false;
-    }
-    
-    // Do we have permission to edit the page?
-    if ( !$this->perms->get_permissions('edit_page') )
-    {
-      $this->raise_error($lang->get('editor_err_no_permission'));
-      return false;
-    }
-    
-    list($protection) = $db->fetchrow_num();
-    $db->free_result();
-    
-    if ( $protection == 1 )
-    {
-      // The page is protected - do we have permission to edit protected pages?
-      if ( !$this->perms->get_permissions('even_when_protected') )
-      {
-        $this->raise_error($lang->get('editor_err_page_protected'));
-        return false;
-      }
-    }
-    else if ( $protection == 2 )
-    {
-      // The page is semi-protected.
-      if (
-           ( !$session->user_logged_in || // Is the user logged in?
-             ( $session->user_logged_in && $session->reg_time + ( 4 * 86400 ) >= time() ) ) // If so, have they been registered for 4 days?
-           && !$this->perms->get_permissions('even_when_protected') ) // And of course, is there an ACL that overrides semi-protection?
-      {
-        $this->raise_error($lang->get('editor_err_page_protected'));
-        return false;
-      }
-    }
-    
-    // Spam check
-    if ( !spamalyze($text) )
-    {
-      $this->raise_error($lang->get('editor_err_spamcheck_failed'));
-      return false;
-    }
-    
-    // Page format check
-    if ( !in_array($page_format, array('xhtml', 'wikitext')) )
-    {
-      $this->raise_error("format \"$page_format\" not one of [xhtml, wikitext]");
-      return false;
-    }
-    
-    //
-    // Protection validated; update page content
-    //
-    
-    $text_undb = RenderMan::preprocess_text($text, false, false);
-    $text = $db->escape($text_undb);
-    $author = $db->escape($session->username);
-    $time = time();
-    $edit_summary = ( strval($edit_summary) === $edit_summary ) ? $db->escape($edit_summary) : '';
-    $minor_edit = ( $minor_edit ) ? '1' : '0';
-    $date_string = enano_date(ED_DATE | ED_TIME);
-    
-    // Insert log entry
-    $sql = 'INSERT INTO ' . table_prefix . "logs ( time_id, date_string, log_type, action, page_id, namespace, author, author_uid, page_text, edit_summary, minor_edit, page_format )\n"
-         . "  VALUES ( $time, '$date_string', 'page', 'edit', '{$this->page_id}', '{$this->namespace}', '$author', $session->user_id, '$text', '$edit_summary', $minor_edit, '$page_format' );";
-    if ( !$db->sql_query($sql) )
-    {
-      $this->raise_error($db->get_error());
-      return false;
-    }
-    
-    // Update the master text entry
-    $sql = 'UPDATE ' . table_prefix . "page_text SET page_text = '$text' WHERE page_id = '{$this->page_id}' AND namespace = '{$this->namespace}';";
-    if ( !$db->sql_query($sql) )
-    {
-      $this->raise_error($db->get_error());
-      return false;
-    }
-    
-    // If there's an identical draft copy, delete it
-    $sql = 'DELETE FROM ' . table_prefix . "logs WHERE is_draft = 1 AND page_id = '{$this->page_id}' AND namespace = '{$this->namespace}' AND page_text = '{$text}';";
-    if ( !$db->sql_query($sql) )
-    {
-      $this->raise_error($db->get_error());
-      return false;
-    }
-    
-    // Set page_format
-    // Using @ due to warning thrown when saving new page
-    $cdata = $this->ns->get_cdata();
-    if ( @$cdata['page_format'] !== $page_format )
-    {
-      // Note: no SQL injection to worry about here. Everything that goes into this is sanitized already, barring some rogue plugin.
-      // (and if there's a rogue plugin running, we have bigger things to worry about anyway.)
-      if ( !$db->sql_query('UPDATE ' . table_prefix . "pages SET page_format = '$page_format' WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';") )
-      {
-        $this->raise_error($db->get_error());
-        return false;
-      }
-      $paths->update_metadata_cache();
-    }
-    
-    // Rebuild the search index
-    $paths->rebuild_page_index($this->page_id, $this->namespace);
-    
-    $this->text_cache = $text_undb;
-    
-    return true;
-    
-  }
-  
-  /**
-   * Creates the page if it doesn't already exist.
-   * @param string Optional page title.
-   * @param bool Visibility (allow indexing) flag
-   * @return bool True on success, false on failure.
-   */
-  
-  function create_page($title = false, $visible = true)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    // Do we have permission to create the page?
-    if ( !$this->perms->get_permissions('create_page') )
-    {
-      $this->raise_error($lang->get('pagetools_create_err_no_permission'));
-      return false;
-    }
-    
-    // Does it already exist?
-    if ( $this->page_exists )
-    {
-      $this->raise_error($lang->get('pagetools_create_err_already_exists'));
-      return false;
-    }
-    
-    // It's not in there. Perform validation.
-    
-    // We can't create special, admin, or external pages.
-    if ( $this->namespace == 'Special' || $this->namespace == 'Admin' || $this->namespace == 'API' )
-    {
-      $this->raise_error($lang->get('pagetools_create_err_nodb_namespace'));
-      return false;
-    }
-    
-    // Guess the proper title
-    $name = ( !empty($title) ) ? $title : str_replace('_', ' ', dirtify_page_id($this->page_id));
-    
-    // Check for the restricted Project: prefix
-    if ( substr($this->page_id, 0, 8) == 'Project:' )
-    {
-      $this->raise_error($lang->get('pagetools_create_err_reserved_prefix'));
-      return false;
-    }
-    
-    // Validation successful - insert the page
-    
-    $metadata = array(
-        'urlname' => $this->page_id,
-        'namespace' => $this->namespace,
-        'name' => $name,
-        'special' => 0,
-        'visible' => $visible ? 1 : 0,
-        'comments_on' => 1,
-        'protected' => ( $this->namespace == 'System' ? 1 : 0 ),
-        'delvotes' => 0,
-        'delvote_ips' => serialize(array()),
-        'wiki_mode' => 2
-      );
-    
-    $paths->add_page($metadata);
-    
-    $page_id = $db->escape($this->page_id);
-    $namespace = $db->escape($this->namespace);
-    $name = $db->escape($name);
-    $protect = ( $this->namespace == 'System' ) ? '1' : '0';
-    $blank_array = $db->escape(serialize(array()));
-    
-    // Query 1: Metadata entry
-    $q = $db->sql_query('INSERT INTO ' . table_prefix . "pages(name, urlname, namespace, visible, protected, delvotes, delvote_ips, wiki_mode)\n"
-                      . "  VALUES ( '$name', '$page_id', '$namespace', {$metadata['visible']}, $protect, 0, '$blank_array', 2 );");
-    if ( !$q )
-      $db->_die('PageProcessor page creation - metadata stage');
-    
-    // Query 2: Text insertion
-    $q = $db->sql_query('INSERT INTO ' . table_prefix . "page_text(page_id, namespace, page_text)\n"
-                        . "VALUES ( '$page_id', '$namespace', '' );");
-    if ( !$q )
-      $db->_die('PageProcessor page creation - text stage');
-    
-    // Query 3: Log entry
-    $db->sql_query('INSERT INTO ' . table_prefix."logs(time_id, date_string, log_type, action, author, author_uid, page_id, namespace)\n"
-                   . "  VALUES ( " . time() . ", 'DEPRECATED', 'page', 'create', \n"
-                   . "          '" . $db->escape($session->username) . "', $session->user_id, '" . $db->escape($this->page_id) . "', '" . $this->namespace . "');");
-    if ( !$q )
-      $db->_die('PageProcessor page creation - logging stage');
-    
-    // Update the cache
-    $paths->update_metadata_cache();
-    
-    // Make sure that when/if we save the page later in this instance it doesn't get re-created
-    $this->page_exists = true;
-    
-    // Page created. We're good!
-    return true;
-  }
-  
-  /**
-   * Rolls back a non-edit action in the logs
-   * @param int Log entry (log_id) to roll back
-   * @return array Standard Enano error/success protocol
-   */
-  
-  function rollback_log_entry($log_id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $cache;
-    
-    // Verify permissions
-    if ( !$this->perms->get_permissions('history_rollback') )
-    {
-      return array(
-        'success' => false,
-        'error' => 'access_denied'
-        );
-    }
-    
-    // Check input
-    $log_id = intval($log_id);
-    if ( empty($log_id) )
-    {
-      return array(
-        'success' => false,
-        'error' => 'invalid_parameter'
-        );
-    }
-    
-    // Fetch the log entry
-    $q = $db->sql_query('SELECT * FROM ' . table_prefix . "logs WHERE log_type = 'page' AND page_id='{$this->page_id}' AND namespace='{$this->namespace}' AND log_id = $log_id;");
-    if ( !$q )
-      $db->_die();
-    
-    // Is this even a valid log entry for this context?
-    if ( $db->numrows() < 1 )
-    {
-      return array(
-        'success' => false,
-        'error' => 'entry_not_found'
-        );
-    }
-    
-    // All good, fetch and free the result
-    $log_entry = $db->fetchrow();
-    $db->free_result();
-    
-    $dateline = enano_date(ED_DATE | ED_TIME, $log_entry['time_id']);
-    
-    // Let's see, what do we have here...
-    switch ( $log_entry['action'] )
-    {
-      case 'rename':
-        // Page was renamed, let the rename method handle this
-        return array_merge($this->rename_page($log_entry['edit_summary']), array('dateline' => $dateline, 'action' => $log_entry['action']));
-        break;
-      case 'prot':
-      case 'unprot':
-      case 'semiprot':
-        return array_merge($this->protect_page(intval($log_entry['page_text']), '__REVERSION__'), array('dateline' => $dateline, 'action' => $log_entry['action']));
-        break;
-      case 'delete':
-        
-        // Raising a previously dead page has implications...
-        
-        // FIXME: l10n
-        // rollback_extra is required because usually only moderators can undo page deletion AND restore the content.
-        // potential flaw here - once recreated, can past revisions be restored by users without rollback_extra? should
-        // probably modify editor routine to deny revert access if the timestamp < timestamp of last deletion if any.
-        if ( !$this->perms->get_permissions('history_rollback_extra') )
-          return 'Administrative privileges are required for page undeletion.';
-        
-        // Rolling back the deletion of a page that was since created?
-        $pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
-        if ( isPage($pathskey) )
-          return array(
-              'success' => false,
-              // This is a clean Christian in-joke.
-              'error' => 'seeking_living_among_dead'
-            );
-        
-        // Generate a crappy page name
-        $name = $db->escape( str_replace('_', ' ', dirtify_page_id($this->page_id)) );
-        
-        // Stage 1 - re-insert page
-        $e = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace) VALUES( \'' . $name . '\', \'' . $this->page_id . '\',\'' . $this->namespace . '\' )');
-        if ( !$e )
-          $db->die_json();
-        
-        // Select the latest published revision
-        $q = $db->sql_query('SELECT page_text FROM ' . table_prefix . "logs WHERE\n"
-                          . "      log_type  = 'page'\n"
-                          . "  AND action    = 'edit'\n"
-                          . "  AND page_id   = '$this->page_id'\n"
-                          . "  AND namespace = '$this->namespace'\n"
-                          . "  AND is_draft != 1\n"
-                          . "ORDER BY time_id DESC LIMIT 1;");
-        if ( !$q )
-          $db->die_json();
-        list($page_text) = $db->fetchrow_num();
-        $db->free_result($q);
-        
-        // Apply the latest revision as the current page text
-        $page_text = $db->escape($page_text);
-        $e = $db->sql_query('INSERT INTO ' . table_prefix."page_text(page_id, namespace, page_text) VALUES\n"
-                          . "  ( '$this->page_id', '$this->namespace', '$page_text' );");
-        if ( !$e )
-          $db->die_json();
-        
-        $cache->purge('page_meta');
-        
-        return array(
-            'success' => true,
-            'dateline' => $dateline,
-            'action' => $log_entry['action']
-          );
-        
-        break;
-      case 'reupload':
-        
-        // given a log id and some revision info, restore the old file.
-        // get the timestamp of the file before this one
-        $q = $db->sql_query('SELECT time_id, file_key, file_extension, filename, size, mimetype FROM ' . table_prefix . "files WHERE time_id < {$log_entry['time_id']} ORDER BY time_id DESC LIMIT 1;");
-        if ( !$q )
-          $db->_die();
-        
-        $row = $db->fetchrow();
-        $db->free_result();
-        
-        // If the file hasn't been renamed to the new format (omitting timestamp), do that now.
-        $fname = ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}";
-        if ( @file_exists($fname) )
-        {
-          // it's stored in the old format - rename
-          $fname_new = ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}";
-          if ( !@rename($fname, $fname_new) )
-          {
-            return array(
-              'success' => false,
-              'error' => 'rb_file_rename_failed',
-              'action' => $log_entry['action']
-              );
-          }
-        }
-        
-        // Insert a new file entry
-        $time = time();
-        $filename = $db->escape($row['filename']);
-        $mimetype = $db->escape($row['mimetype']);
-        $ext = $db->escape($row['file_extension']);
-        $key = $db->escape($row['file_key']);
-        
-        $q = $db->sql_query('INSERT INTO ' . table_prefix . "files ( time_id, page_id, filename, size, mimetype, file_extension, file_key ) VALUES\n"
-              . "  ( $time, '$this->page_id', '$filename', {$row['size']}, '$mimetype', '$ext', '$key' );");
-        if ( !$q )
-          $db->die_json();
-        
-        // add reupload log entry
-        $username = $db->escape($session->username);
-        $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, page_id, namespace, author, author_uid, edit_summary ) VALUES\n"
-                          . "  ( 'page', 'reupload', $time, '$this->page_id', '$this->namespace', '$username', $session->user_id, '__ROLLBACK__' )");
-        if ( !$q )
-          $db->die_json();
-        
-        return array(
-            'success' => true,
-            'dateline' => $dateline,
-            'action' => $log_entry['action']
-          );
-        
-        break;
-      case 'votereset':
-        if ( !$this->perms->get_permissions('history_rollback_extra') )
-          return 'Denied!';
-        
-        // pull existing vote data
-        $q = $db->sql_query('SELECT delvotes, delvote_ips FROM ' . table_prefix . "pages WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';");
-        if ( !$q )
-          $db->_die();
-        
-        if ( $db->numrows() < 1 )
-          return array(
-              'success' => false,
-              'error' => 'page_not_exist',
-              'action' => $log_entry['action']
-            );
-          
-        list($curr_delvotes, $curr_delvote_ips) = $db->fetchrow_num();
-        $db->free_result();
-        
-        // merge with existing votes
-        $old_delvote_ips = unserialize($log_entry['page_text']);
-        $new_delvote_ips = unserialize($curr_delvote_ips);
-        $new_delvote_ips['u'] = array_unique(array_merge($new_delvote_ips['u'], $old_delvote_ips['u']));
-        $new_delvote_ips['ip'] = array_unique(array_merge($new_delvote_ips['ip'], $old_delvote_ips['ip']));
-        $new_delvotes = count($new_delvote_ips['ip']);
-        $new_delvote_ips = $db->escape(serialize($new_delvote_ips));
-        
-        // update pages table
-        $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET delvotes = $new_delvotes, delvote_ips = '$new_delvote_ips' WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';");
-        
-        $cache->purge('page_meta');
-        
-        return array(
-            'success' => true,
-            'dateline' => $dateline,
-            'action' => $log_entry['action']
-          );
-        break;
-      default:
-        
-        return array(
-            'success' => false,
-            'error' => 'rb_action_not_supported',
-            'action' => $log_entry['action']
-          );
-        
-        break;
-    }
-  }
-  
-  /**
-   * Renames the page
-   * @param string New name
-   * @return array Standard Enano error/success protocol
-   */
-  
-  function rename_page($new_name)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Check permissions
-    if ( !$this->perms->get_permissions('rename') )
-    {
-      return array(
-        'success' => false,
-        'error' => 'access_denied'
-        );
-    }
-    
-    // If this is the same as the current name, return success
-    $page_name = get_page_title_ns($this->page_id, $this->namespace);
-    if ( $page_name === $new_name )
-    {
-      return array(
-        'success' => true
-        );
-    }
-    
-    // Make sure the name is valid
-    $new_name = trim($new_name);
-    if ( empty($new_name) )
-    {
-      return array(
-        'success' => false,
-        'error' => 'invalid_parameter'
-        );
-    }
-    
-    // Log the action
-    $username = $db->escape($session->username);
-    $page_name = $db->escape($page_name);
-    $time = time();
-    
-    $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, time_id, date_string ) VALUES\n"
-                      . "  ( 'page', 'rename', '{$this->page_id}', '{$this->namespace}', '$username', $session->user_id, '$page_name', '$time', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );");
-    if ( !$q )
-      $db->_die();
-    
-    // Not much to do but to rename it now
-    $new_name = $db->escape($new_name);
-    $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET name = '$new_name' WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
-    if ( !$q )
-      $db->_die();
-    
-    // Update the cache
-    $paths->update_metadata_cache();
-    
-    return array(
-      'success' => true
-      );
-  }
-  
-  /**
-   * Sets the protection level of the page
-   * @param int Protection level, one of PROTECT_{FULL,SEMI,NONE}
-   * @param string Reason for protection - required
-   */
-  
-  function protect_page($protection_level, $reason)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $cache;
-    
-    // Validate permissions
-    if ( !$this->perms->get_permissions('protect') )
-    {
-      return array(
-        'success' => false,
-        'error' => 'access_denied'
-        );
-    }
-    
-    // Validate re-auth
-    if ( !$session->sid_super )
-    {
-      return array(
-        'success' => false,
-        'error' => 'access_denied_need_reauth'
-        );
-    }
-    
-    // Validate input
-    $reason = trim($reason);
-    if ( !in_array($protection_level, array(PROTECT_NONE, PROTECT_FULL, PROTECT_SEMI)) || empty($reason) )
-    {
-      return array(
-        'success' => false,
-        'error' => 'invalid_parameter'
-        );
-    }
-    
-    // Retrieve page metadata
-    $metadata = $this->ns->get_cdata();
-    
-    // Log the action
-    $username = $db->escape($session->username);
-    $time = time();
-    $existing_protection = intval($metadata['protected']);
-    $reason = $db->escape($reason);
-    
-    if ( $existing_protection == $protection_level )
-    {
-      return array(
-        'success' => false,
-        'error' => 'protection_already_there'
-        );
-    }
-    
-    $action = '[ insanity ]';
-    switch($protection_level)
-    {
-      case PROTECT_FULL: $action = 'prot'; break;
-      case PROTECT_NONE: $action = 'unprot'; break;
-      case PROTECT_SEMI: $action = 'semiprot'; break;
-    }
-    
-    $sql = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, time_id, page_text, date_string ) VALUES\n"
-         . "  ( 'page', '$action', '{$this->page_id}', '{$this->namespace}', '$username', $author_uid, '$reason', '$time', '$existing_protection', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );";
-    if ( !$db->sql_query($sql) )
-    {
-      $db->die_json();
-    }
-    
-    // Perform the actual protection
-    $q = $db->sql_query('UPDATE ' . table_prefix . "pages SET protected = $protection_level WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
-    if ( !$q )
-      $db->die_json();
-    
-    $cache->purge('page_meta');
-    
-    return array(
-      'success' => true
-      );
-  }
-  
-  /**
-   * Sets internal variables.
-   * @access private
-   */
-  
-  function _setup($page_id, $namespace, $revision_id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $page_id_cleaned = sanitize_page_id($page_id);
-    
-    $this->revision_id = $revision_id;
-    $this->page_id_unclean = dirtify_page_id($page_id);
-    
-    // resolve namespace
-    $this->ns = namespace_factory($page_id, $namespace, $this->revision_id);
-    $this->page_id =& $this->ns->page_id;
-    $this->namespace =& $this->ns->namespace;
-    
-    $this->perms = $session->fetch_page_acl( $page_id, $namespace );
-    
-    $this->page_exists = $this->ns->exists();
-    $this->title = get_page_title_ns($this->page_id, $this->namespace);
-    
-    profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Ran _setup()");
-  }
-  
-  /**
-   * Processes any redirects.
-   * @access private
-   */
-  
-  function process_redirects()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $output, $lang;
-    
-    $this->redirect_stack = array();
-    
-    if ( !method_exists($this->ns, 'get_redirect') )
-      return true;
-    
-    if ( !$this->allow_redir )
-      return true;
-    
-    $redirect_count = 0;
-    
-    while ( $result = $this->ns->get_redirect() )
-    {
-      if ( $result['namespace'] == 'Special' || $result['namespace'] == 'Admin' )
-      {
-        // Can't redirect to special/admin page
-        $this->redir_error = $lang->get('page_err_redirect_to_special');
-        break;
-      }
-      if ( $redirect_count == 3 )
-      {
-        // max of 3 internal redirects exceeded
-        $this->redir_error = $lang->get('page_err_redirects_exceeded');
-        break;
-      }
-      
-      $loop = false;
-      foreach ( $this->redirect_stack as $stackel )
-      {
-        if ( $result['page_id'] == $stackel['old_page_id'] && $result['namespace'] == $stackel['old_namespace'] )
-        {
-          $loop = true;
-          break;
-        }
-      }
-      
-      if ( $loop )
-      {
-        // redirect loop
-        $this->redir_error = $lang->get('page_err_redirect_infinite_loop');
-        break;
-      }
-      $new_ns = namespace_factory($result['page_id'], $result['namespace']);
-      if ( !$new_ns->exists() )
-      {
-        // new page doesn't exist
-        $this->redir_error = $lang->get('page_err_redirect_to_nonexistent');
-        break;
-      }
-      
-      // build stack entry
-      $stackel = array(
-          'page_id' => $result['page_id'],
-          'namespace' => $result['namespace'],
-          'old_page_id' => $this->page_id,
-          'old_namespace' => $this->namespace,
-          'old_title' => $this->ns->title
-        );
-      
-      // replace everything (perform the actual redirect)
-      $this->ns = $new_ns;
-      
-      $this->page_id =& $this->ns->page_id;
-      $this->namespace =& $this->ns->namespace;
-      
-      $this->redirect_stack[] = $stackel;
-      
-      $redirect_count++;
-    }
-  }
-    
-  /**
-   * Sends the page header, dependent on, of course, whether we're supposed to.
-   */
-  
-  function header()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( $this->send_headers )
-      $template->header();
-  }
-  
-  /**
-   * Sends the page footer, dependent on, of course, whether we're supposed to.
-   */
-  
-  function footer()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( $this->send_headers )
-      $template->footer();
-  }
-  
-  /**
-   * Fetches the raw, unfiltered page text.
-   * @access public
-   */
-  
-  function fetch_text()
-  {
-    return $this->ns->fetch_text();
-  }
-  
-  /**
-   * Tells us if the page exists.
-   * @return bool
-   */
-  
-  function exists()
-  {
-    return $this->ns->exists();
-  }
-  
-  /**
-   * Send the error message to the user that the access to this page is denied.
-   * @access private
-   */
-  
-  function err_access_denied()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $email;
-    
-    // Log it for crying out loud
-    $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'illegal_page\', '.time().', \'DEPRECATED\', \''.$db->escape($session->username).'\', ' . $session->user_id . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', \'' . $db->escape(serialize(array($this->page_id, $this->namespace))) . '\')');
-    
-    $ob = '';
-    //$template->tpl_strings['PAGE_NAME'] = 'Access denied';
-    $template->tpl_strings['PAGE_NAME'] = htmlspecialchars( $this->title );
-      
-    if ( $this->send_headers )
-    {
-      $ob .= $template->getHeader();
-    }
-    
-    if ( count($this->redirect_stack) > 0 )
-    {
-      $stack = array_reverse($this->redirect_stack);
-      foreach ( $stack as $oldtarget )
-      {
-        $url = makeUrlNS($oldtarget[1], $oldtarget[0], 'redirect=no', true);
-        $old_page = namespace_factory($oldtarget[0], $oldtarget[1]);
-        $page_data = $old_page->get_cdata();
-        $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$oldtarget[1]] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $oldtarget[0] ) ) );
-        $a = '<a href="' . $url . '">' . $title . '</a>';
-        
-        $url = makeUrlNS($this->namespace, $this->page_id, 'redirect=no', true);
-        $page_data = $this->ns->get_cdata();
-        $title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$this->namespace] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $this->page_id ) ) );
-        $b = '<a href="' . $url . '">' . $title . '</a>';
-        
-        $ob .= '<small>' . $lang->get('page_msg_redirected_from_to', array('from' => $a, 'to' => $b)) . '<br /></small>';
-      }
-    }
-    
-    $email_link = $email->encryptEmail(getConfig('contact_email'), '', '', $lang->get('page_err_access_denied_siteadmin'));
-    
-    $ob .= "<h3>" . $lang->get('page_err_access_denied_title') . "</h3>";
-    $ob .= "<p>" . $lang->get('page_err_access_denied_body', array('site_administration' => $email_link)) . "</p>";
-    
-    if ( $this->send_headers )
-    {
-      $ob .= $template->getFooter();
-    }
-    echo $ob;
-  }
-  
-  /**
-   * Inform the user of an incorrect or absent password
-   * @access private
-   */
-   
-  function err_wrong_password()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $title = $lang->get('page_msg_passrequired_title');
-    $message = ( empty($this->password) ) ?
-                 '<p>' . $lang->get('page_msg_passrequired') . '</p>' :
-                 '<p>' . $lang->get('page_msg_pass_wrong') . '</p>';
-    $message .= '<form action="' . makeUrlNS($this->namespace, $this->page_id) . '" method="post">
-                   <p>
-                     <label>' . $lang->get('page_lbl_password') . ' <input name="pagepass" type="password" /></label>&nbsp;&nbsp;<input type="submit" value="' . $lang->get('page_btn_password_submit') . '" />
-                   </p>
-                 </form>';
-    if ( $this->send_headers )
-    {
-      $template->tpl_strings['PAGE_NAME'] = $title;
-      $template->header();
-      echo "$message";
-      $template->footer();
-    }
-    else
-    {
-      echo "<h2>$title</h2>
-            $message";
-    }
-  }
-  
-  /**
-   * Send the error message to the user complaining that there weren't any rows.
-   * @access private
-   */
-  
-  function err_no_rows()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $title = 'No text rows';
-    $message = 'While the page\'s existence was verified, there were no rows in the database that matched the query for the text. This may indicate a bug with the software; ask the webmaster for more information. The offending query was:<pre>' . $db->latest_query . '</pre>';
-    if ( $this->send_headers )
-    {
-      $template->tpl_strings['PAGE_NAME'] = $title;
-      $template->header();
-      echo "<p>$message</p>";
-      $template->footer();
-    }
-    else
-    {
-      echo "<h2>$title</h2>
-            <p>$message</p>";
-    }
-  }
-  
-  /**
-   * Send an error message and die. For debugging or critical technical errors only - nothing that would under normal circumstances be shown to the user.
-   * @param string Error message
-   * @param bool If true, send DBAL's debugging information as well
-   */
-   
-  function send_error($message, $sql = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $content = "<p>$message</p>";
-    $template->tpl_strings['PAGE_NAME'] = $lang->get('page_msg_general_error');
-    
-    if ( $this->debug['works'] )
-    {
-      $content .= $this->debug['backtrace'];
-    }
-    
-    header('HTTP/1.1 500 Internal Server Error');
-    
-    $template->header();
-    echo $content;
-    $template->footer();
-    
-    $db->close();
-    
-    exit;
-    
-  }
-  
-  /**
-   * Raises an error.
-   * @param string Error string
-   */
-   
-  function raise_error($string)
-  {
-    if ( !is_string($string) )
-      return false;
-    $this->_errors[] = $string;
-  }
-  
-  /**
-   * Retrieves the latest error from the error stack and returns it ('pops' the error stack)
-   * @return string
-   */
-  
-  function pop_error()
-  {
-    if ( count($this->_errors) < 1 )
-      return false;
-    return array_pop($this->_errors);
-  }
-  
+	
+	/**
+ 	* Page ID and namespace of the page handled by this instance
+ 	* @var string
+ 	*/
+	
+	var $page_id;
+	var $namespace;
+	
+	/**
+ 	* The instance of the namespace processor for the namespace we're doing.
+ 	* @var object
+ 	*/
+	
+	var $ns;
+	
+	/**
+ 	* The title of the page sent to the template parser
+ 	* @var string
+ 	*/
+	
+	var $title = '';
+	
+	/**
+ 	* The information about the page(s) we were redirected from
+ 	* @var array
+ 	*/
+	
+	var $redirect_stack = array();
+	
+	/**
+ 	* The revision ID (history entry) to send. If set to 0 (the default) then the most recent revision will be sent.
+ 	* @var int
+ 	*/
+	
+	var $revision_id = 0;
+	
+	/**
+ 	* The time this revision was saved, as a UNIX timestamp
+ 	* @var int
+ 	*/
+	
+	var $revision_time = 0;
+	
+	/**
+ 	* Unsanitized page ID.
+ 	* @var string
+ 	*/
+	
+	var $page_id_unclean;
+	
+	/**
+ 	* Tracks if the page we're loading exists in the database or not.
+ 	* @var bool
+ 	*/
+	
+	var $page_exists = false;
+	
+	/**
+ 	* Permissions!
+ 	* @var object
+ 	*/
+	
+	var $perms = null;
+	
+	/**
+ 	* The SHA1 hash of the user-inputted password for the page
+ 	* @var string
+ 	*/
+ 	
+	var $password = '';
+	
+	/**
+ 	* Switch to track if redirects are allowed. Defaults to true.
+ 	* @var bool
+ 	*/
+	
+	var $allow_redir = true;
+	
+	/**
+ 	* Holds any error message from redirection code. Defaults to false (no error).
+ 	* @var mixed
+ 	*/
+ 	
+	var $redir_error = false;
+	
+	/**
+ 	* If this is set to true, this will call the header and footer funcs on $template when render() is called.
+ 	* @var bool
+ 	*/
+	
+	var $send_headers = false;
+	
+	/**
+ 	* Cache the fetched text so we don't fetch it from the DB twice.
+ 	* @var string
+ 	*/
+	
+	var $text_cache = '';
+	
+	/**
+ 	* Debugging information to track errors. You can set enable to false to disable sending debug information.
+ 	* @var array
+ 	*/
+	
+	var $debug = array(
+			'enable' => false,
+			'works'  => false
+		);
+	
+	/**
+ 	* The list of errors raised in the class.
+ 	* @var array
+ 	*/
+	
+	var $_errors = array();
+	
+	/**
+ 	* Constructor.
+ 	* @param string The page ID (urlname) of the page
+ 	* @param string The namespace of the page
+ 	* @param int Optional. The revision ID to send.
+ 	*/
+	
+	function __construct( $page_id, $namespace, $revision_id = 0 )
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		profiler_log("PageProcessor [{$namespace}:{$page_id}]: Started constructor");
+		
+		// See if we can get some debug info
+		if ( function_exists('debug_backtrace') && $this->debug['enable'] )
+		{
+			$this->debug['works'] = true;
+			$this->debug['backtrace'] = enano_debug_print_backtrace(true);
+		}
+		
+		// First things first - check page existence and permissions
+		
+		if ( !isset($paths->nslist[$namespace]) )
+		{
+			$this->send_error('The namespace "' . htmlspecialchars($namespace) . '" does not exist.');
+		}
+		
+		if ( !is_int($revision_id) )
+			$revision_id = 0;
+		
+		$this->_setup( $page_id, $namespace, $revision_id );
+	}
+	
+	/**
+ 	* The main method to send the page content. Also responsible for checking permissions and calling the statistics counter.
+ 	* @param bool If true, the stat counter is called. Defaults to false.
+ 	*/
+	
+	function send( $do_stats = false )
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang, $output;
+		
+		profiler_log('PageProcessor: send() called');
+		
+		if ( !$this->perms->get_permissions('read') )
+		{
+			// Permission denied to read page. Is this one of our core pages that must always be allowed?
+			// NOTE: Not even the administration panel will work if ACLs deny access to it.
+			if ( $this->namespace == 'Special' && in_array($this->page_id, array('Login', 'Logout', 'LangExportJSON', 'CSS')) )
+			{
+				// Do nothing; allow execution to continue
+			}
+			else
+			{
+				// Page isn't whitelisted, behave as normal
+				$this->err_access_denied();
+				return false;
+			}
+		}
+		if ( $this->revision_id > 0 && !$this->perms->get_permissions('history_view') )
+		{
+			$this->err_access_denied();
+			return false;
+		}
+		
+		// Is there a custom function registered for handling this namespace?
+		// DEPRECATED (even though it only saw its way into one alpha release.)
+		if ( $proc = $paths->get_namespace_processor($this->namespace) )
+		{
+			// yes, just call that
+			// this is protected aggressively by the PathManager against overriding critical namespaces
+			return call_user_func($proc, $this);
+		}
+		
+		$pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
+		$strict_no_headers = false;
+		$admin_fail = false;
+		if ( $this->namespace == 'Admin' && strstr($this->page_id, '/') )
+		{
+			$this->page_id = substr($this->page_id, 0, strpos($this->page_id, '/'));
+			$funcname = "page_{$this->namespace}_{$this->page_id}";
+			if ( function_exists($funcname) )
+			{
+				$this->page_exists = true;
+			}
+		}
+		if ( isPage($pathskey) )
+		{
+			$cdata = $this->ns->get_cdata();
+			
+			if ( $cdata['special'] == 1 )
+			{
+				$this->send_headers = false;
+				$strict_no_headers = true;
+				$GLOBALS['output'] = new Output_Naked();
+			}
+			if ( isset($cdata['password']) )
+			{
+				if ( $cdata['password'] != '' && $cdata['password'] != sha1('') )
+				{
+					$password =& $cdata['password'];
+					if ( $this->password != $password )
+					{
+						$this->err_wrong_password();
+						return false;
+					}
+				}
+			}
+			if ( isset($cdata['require_admin']) && $cdata['require_admin'] )
+			{
+				if ( $session->auth_level < USER_LEVEL_ADMIN )
+				{
+					$admin_fail = true;
+				}
+			}
+		}
+		else if ( $this->namespace === $paths->namespace && $this->page_id == $paths->page_id )
+		{
+			if ( isset($paths->cpage['require_admin']) && $paths->cpage['require_admin'] )
+			{
+				if ( $session->auth_level < USER_LEVEL_ADMIN )
+				{
+					$admin_fail = true;
+				}
+			}
+		}
+		if ( $admin_fail )
+		{
+			header('Content-type: text/javascript');
+			echo enano_json_encode(array(
+					'mode' => 'error',
+					'error' => 'need_auth_to_admin'
+				));
+			return true;
+		}
+		if ( $this->page_exists && $this->namespace != 'Special' && $this->namespace != 'Admin' && $do_stats )
+		{
+			require_once(ENANO_ROOT.'/includes/stats.php');
+			doStats($this->page_id, $this->namespace);
+		}
+		
+		// We are all done. Ship off the page.
+		
+		if ( !$this->allow_redir )
+		{
+			if ( method_exists($this->ns, 'get_redirect') )
+			{
+				if ( $result = $this->ns->get_redirect() )
+					display_redirect_notice($result['page_id'], $result['namespace']);
+			}
+		}
+		else
+		{
+			$this->process_redirects();
+			
+			if ( count($this->redirect_stack) > 0 )
+			{
+				$stack = array_reverse($this->redirect_stack);
+				foreach ( $stack as $stackel )
+				{
+					$url = makeUrlNS($stackel['old_namespace'], $stackel['old_page_id'], 'redirect=no', true);
+					$page_data = $this->ns->get_cdata();
+					$title = $stackel['old_title'];
+					$a = '<a href="' . $url . '">' . htmlspecialchars($title) . '</a>';
+					$output->add_after_header('<small>' . $lang->get('page_msg_redirected_from', array('from' => $a)) . '<br /></small>');
+				}
+				$template->set_page($this);
+			}
+			
+			if ( $this->redir_error )
+			{
+				$output->add_after_header('<div class="usermessage"><b>' . $this->redir_error . '</b></div>');
+				$result = $this->ns->get_redirect();
+				display_redirect_notice($result['page_id'], $result['namespace']);
+			}
+		}
+		
+		$this->ns->send();
+	}
+	
+	/**
+ 	* Sends the page through by fetching it from the database.
+ 	*/
+ 	
+	function send_from_db($strict_no_headers = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$this->ns->send_from_db();
+	}
+	
+	/**
+ 	* Fetches the wikitext or HTML source for the page.
+ 	* @return string
+ 	*/
+	
+	function fetch_source()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !$this->perms->get_permissions('view_source') )
+		{
+			return false;
+		}
+		if ( !$this->page_exists )
+		{
+			return '';
+		}
+		$cdata = $this->ns->get_cdata();
+		if ( isset($cdata['password']) )
+		{
+			if ( $cdata['password'] != sha1('') && $cdata['password'] !== $this->password && !empty($cdata['password']) )
+			{
+				return false;
+			}
+		}
+		return $this->fetch_text();
+	}
+	
+	/**
+ 	* Updates (saves/changes/edits) the content of the page.
+ 	* @param string The new text for the page
+ 	* @param string A summary of edits made to the page.
+ 	* @param bool If true, the edit is marked as a minor revision
+ 	* @param string Page format - wikitext or xhtml. REQUIRED, and new in 1.1.6.
+ 	* @return bool True on success, false on failure. When returning false, it will push errors to the PageProcessor error stack; read with $page->pop_error()
+ 	*/
+	
+	function update_page($text, $edit_summary = false, $minor_edit = false, $page_format)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		// Create the page if it doesn't exist
+		if ( !$this->page_exists )
+		{
+			if ( !$this->create_page() )
+			{
+				return false;
+			}
+		}
+			
+		//
+		// Validation
+		//
+		
+		$page_id = $db->escape($this->page_id);
+		$namespace = $db->escape($this->namespace);
+		
+		$q = $db->sql_query('SELECT protected FROM ' . table_prefix . "pages WHERE urlname='$page_id' AND namespace='$namespace';");
+		if ( !$q )
+			$db->_die('PageProcess updating page content');
+		if ( $db->numrows() < 1 )
+		{
+			$this->raise_error($lang->get('editor_err_no_rows'));
+			return false;
+		}
+		
+		// Do we have permission to edit the page?
+		if ( !$this->perms->get_permissions('edit_page') )
+		{
+			$this->raise_error($lang->get('editor_err_no_permission'));
+			return false;
+		}
+		
+		list($protection) = $db->fetchrow_num();
+		$db->free_result();
+		
+		if ( $protection == 1 )
+		{
+			// The page is protected - do we have permission to edit protected pages?
+			if ( !$this->perms->get_permissions('even_when_protected') )
+			{
+				$this->raise_error($lang->get('editor_err_page_protected'));
+				return false;
+			}
+		}
+		else if ( $protection == 2 )
+		{
+			// The page is semi-protected.
+			if (
+ 					( !$session->user_logged_in || // Is the user logged in?
+ 						( $session->user_logged_in && $session->reg_time + ( 4 * 86400 ) >= time() ) ) // If so, have they been registered for 4 days?
+ 					&& !$this->perms->get_permissions('even_when_protected') ) // And of course, is there an ACL that overrides semi-protection?
+			{
+				$this->raise_error($lang->get('editor_err_page_protected'));
+				return false;
+			}
+		}
+		
+		// Spam check
+		if ( !spamalyze($text) )
+		{
+			$this->raise_error($lang->get('editor_err_spamcheck_failed'));
+			return false;
+		}
+		
+		// Page format check
+		if ( !in_array($page_format, array('xhtml', 'wikitext')) )
+		{
+			$this->raise_error("format \"$page_format\" not one of [xhtml, wikitext]");
+			return false;
+		}
+		
+		//
+		// Protection validated; update page content
+		//
+		
+		$text_undb = RenderMan::preprocess_text($text, false, false);
+		$text = $db->escape($text_undb);
+		$author = $db->escape($session->username);
+		$time = time();
+		$edit_summary = ( strval($edit_summary) === $edit_summary ) ? $db->escape($edit_summary) : '';
+		$minor_edit = ( $minor_edit ) ? '1' : '0';
+		$date_string = enano_date(ED_DATE | ED_TIME);
+		
+		// Insert log entry
+		$sql = 'INSERT INTO ' . table_prefix . "logs ( time_id, date_string, log_type, action, page_id, namespace, author, author_uid, page_text, edit_summary, minor_edit, page_format )\n"
+ 				. "  VALUES ( $time, '$date_string', 'page', 'edit', '{$this->page_id}', '{$this->namespace}', '$author', $session->user_id, '$text', '$edit_summary', $minor_edit, '$page_format' );";
+		if ( !$db->sql_query($sql) )
+		{
+			$this->raise_error($db->get_error());
+			return false;
+		}
+		
+		// Update the master text entry
+		$sql = 'UPDATE ' . table_prefix . "page_text SET page_text = '$text' WHERE page_id = '{$this->page_id}' AND namespace = '{$this->namespace}';";
+		if ( !$db->sql_query($sql) )
+		{
+			$this->raise_error($db->get_error());
+			return false;
+		}
+		
+		// If there's an identical draft copy, delete it
+		$sql = 'DELETE FROM ' . table_prefix . "logs WHERE is_draft = 1 AND page_id = '{$this->page_id}' AND namespace = '{$this->namespace}' AND page_text = '{$text}';";
+		if ( !$db->sql_query($sql) )
+		{
+			$this->raise_error($db->get_error());
+			return false;
+		}
+		
+		// Set page_format
+		// Using @ due to warning thrown when saving new page
+		$cdata = $this->ns->get_cdata();
+		if ( @$cdata['page_format'] !== $page_format )
+		{
+			// Note: no SQL injection to worry about here. Everything that goes into this is sanitized already, barring some rogue plugin.
+			// (and if there's a rogue plugin running, we have bigger things to worry about anyway.)
+			if ( !$db->sql_query('UPDATE ' . table_prefix . "pages SET page_format = '$page_format' WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';") )
+			{
+				$this->raise_error($db->get_error());
+				return false;
+			}
+			$paths->update_metadata_cache();
+		}
+		
+		// Rebuild the search index
+		$paths->rebuild_page_index($this->page_id, $this->namespace);
+		
+		$this->text_cache = $text_undb;
+		
+		return true;
+		
+	}
+	
+	/**
+ 	* Creates the page if it doesn't already exist.
+ 	* @param string Optional page title.
+ 	* @param bool Visibility (allow indexing) flag
+ 	* @return bool True on success, false on failure.
+ 	*/
+	
+	function create_page($title = false, $visible = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		// Do we have permission to create the page?
+		if ( !$this->perms->get_permissions('create_page') )
+		{
+			$this->raise_error($lang->get('pagetools_create_err_no_permission'));
+			return false;
+		}
+		
+		// Does it already exist?
+		if ( $this->page_exists )
+		{
+			$this->raise_error($lang->get('pagetools_create_err_already_exists'));
+			return false;
+		}
+		
+		// It's not in there. Perform validation.
+		
+		// We can't create special, admin, or external pages.
+		if ( $this->namespace == 'Special' || $this->namespace == 'Admin' || $this->namespace == 'API' )
+		{
+			$this->raise_error($lang->get('pagetools_create_err_nodb_namespace'));
+			return false;
+		}
+		
+		// Guess the proper title
+		$name = ( !empty($title) ) ? $title : str_replace('_', ' ', dirtify_page_id($this->page_id));
+		
+		// Check for the restricted Project: prefix
+		if ( substr($this->page_id, 0, 8) == 'Project:' )
+		{
+			$this->raise_error($lang->get('pagetools_create_err_reserved_prefix'));
+			return false;
+		}
+		
+		// Validation successful - insert the page
+		
+		$metadata = array(
+				'urlname' => $this->page_id,
+				'namespace' => $this->namespace,
+				'name' => $name,
+				'special' => 0,
+				'visible' => $visible ? 1 : 0,
+				'comments_on' => 1,
+				'protected' => ( $this->namespace == 'System' ? 1 : 0 ),
+				'delvotes' => 0,
+				'delvote_ips' => serialize(array()),
+				'wiki_mode' => 2
+			);
+		
+		$paths->add_page($metadata);
+		
+		$page_id = $db->escape($this->page_id);
+		$namespace = $db->escape($this->namespace);
+		$name = $db->escape($name);
+		$protect = ( $this->namespace == 'System' ) ? '1' : '0';
+		$blank_array = $db->escape(serialize(array()));
+		
+		// Query 1: Metadata entry
+		$q = $db->sql_query('INSERT INTO ' . table_prefix . "pages(name, urlname, namespace, visible, protected, delvotes, delvote_ips, wiki_mode)\n"
+											. "  VALUES ( '$name', '$page_id', '$namespace', {$metadata['visible']}, $protect, 0, '$blank_array', 2 );");
+		if ( !$q )
+			$db->_die('PageProcessor page creation - metadata stage');
+		
+		// Query 2: Text insertion
+		$q = $db->sql_query('INSERT INTO ' . table_prefix . "page_text(page_id, namespace, page_text)\n"
+												. "VALUES ( '$page_id', '$namespace', '' );");
+		if ( !$q )
+			$db->_die('PageProcessor page creation - text stage');
+		
+		// Query 3: Log entry
+		$db->sql_query('INSERT INTO ' . table_prefix."logs(time_id, date_string, log_type, action, author, author_uid, page_id, namespace)\n"
+ 									. "  VALUES ( " . time() . ", 'DEPRECATED', 'page', 'create', \n"
+ 									. "          '" . $db->escape($session->username) . "', $session->user_id, '" . $db->escape($this->page_id) . "', '" . $this->namespace . "');");
+		if ( !$q )
+			$db->_die('PageProcessor page creation - logging stage');
+		
+		// Update the cache
+		$paths->update_metadata_cache();
+		
+		// Make sure that when/if we save the page later in this instance it doesn't get re-created
+		$this->page_exists = true;
+		
+		// Page created. We're good!
+		return true;
+	}
+	
+	/**
+ 	* Rolls back a non-edit action in the logs
+ 	* @param int Log entry (log_id) to roll back
+ 	* @return array Standard Enano error/success protocol
+ 	*/
+	
+	function rollback_log_entry($log_id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $cache;
+		
+		// Verify permissions
+		if ( !$this->perms->get_permissions('history_rollback') )
+		{
+			return array(
+				'success' => false,
+				'error' => 'access_denied'
+				);
+		}
+		
+		// Check input
+		$log_id = intval($log_id);
+		if ( empty($log_id) )
+		{
+			return array(
+				'success' => false,
+				'error' => 'invalid_parameter'
+				);
+		}
+		
+		// Fetch the log entry
+		$q = $db->sql_query('SELECT * FROM ' . table_prefix . "logs WHERE log_type = 'page' AND page_id='{$this->page_id}' AND namespace='{$this->namespace}' AND log_id = $log_id;");
+		if ( !$q )
+			$db->_die();
+		
+		// Is this even a valid log entry for this context?
+		if ( $db->numrows() < 1 )
+		{
+			return array(
+				'success' => false,
+				'error' => 'entry_not_found'
+				);
+		}
+		
+		// All good, fetch and free the result
+		$log_entry = $db->fetchrow();
+		$db->free_result();
+		
+		$dateline = enano_date(ED_DATE | ED_TIME, $log_entry['time_id']);
+		
+		// Let's see, what do we have here...
+		switch ( $log_entry['action'] )
+		{
+			case 'rename':
+				// Page was renamed, let the rename method handle this
+				return array_merge($this->rename_page($log_entry['edit_summary']), array('dateline' => $dateline, 'action' => $log_entry['action']));
+				break;
+			case 'prot':
+			case 'unprot':
+			case 'semiprot':
+				return array_merge($this->protect_page(intval($log_entry['page_text']), '__REVERSION__'), array('dateline' => $dateline, 'action' => $log_entry['action']));
+				break;
+			case 'delete':
+				
+				// Raising a previously dead page has implications...
+				
+				// FIXME: l10n
+				// rollback_extra is required because usually only moderators can undo page deletion AND restore the content.
+				// potential flaw here - once recreated, can past revisions be restored by users without rollback_extra? should
+				// probably modify editor routine to deny revert access if the timestamp < timestamp of last deletion if any.
+				if ( !$this->perms->get_permissions('history_rollback_extra') )
+					return 'Administrative privileges are required for page undeletion.';
+				
+				// Rolling back the deletion of a page that was since created?
+				$pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
+				if ( isPage($pathskey) )
+					return array(
+							'success' => false,
+							// This is a clean Christian in-joke.
+							'error' => 'seeking_living_among_dead'
+						);
+				
+				// Generate a crappy page name
+				$name = $db->escape( str_replace('_', ' ', dirtify_page_id($this->page_id)) );
+				
+				// Stage 1 - re-insert page
+				$e = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace) VALUES( \'' . $name . '\', \'' . $this->page_id . '\',\'' . $this->namespace . '\' )');
+				if ( !$e )
+					$db->die_json();
+				
+				// Select the latest published revision
+				$q = $db->sql_query('SELECT page_text FROM ' . table_prefix . "logs WHERE\n"
+													. "      log_type  = 'page'\n"
+													. "  AND action    = 'edit'\n"
+													. "  AND page_id   = '$this->page_id'\n"
+													. "  AND namespace = '$this->namespace'\n"
+													. "  AND is_draft != 1\n"
+													. "ORDER BY time_id DESC LIMIT 1;");
+				if ( !$q )
+					$db->die_json();
+				list($page_text) = $db->fetchrow_num();
+				$db->free_result($q);
+				
+				// Apply the latest revision as the current page text
+				$page_text = $db->escape($page_text);
+				$e = $db->sql_query('INSERT INTO ' . table_prefix."page_text(page_id, namespace, page_text) VALUES\n"
+													. "  ( '$this->page_id', '$this->namespace', '$page_text' );");
+				if ( !$e )
+					$db->die_json();
+				
+				$cache->purge('page_meta');
+				
+				return array(
+						'success' => true,
+						'dateline' => $dateline,
+						'action' => $log_entry['action']
+					);
+				
+				break;
+			case 'reupload':
+				
+				// given a log id and some revision info, restore the old file.
+				// get the timestamp of the file before this one
+				$q = $db->sql_query('SELECT time_id, file_key, file_extension, filename, size, mimetype FROM ' . table_prefix . "files WHERE time_id < {$log_entry['time_id']} ORDER BY time_id DESC LIMIT 1;");
+				if ( !$q )
+					$db->_die();
+				
+				$row = $db->fetchrow();
+				$db->free_result();
+				
+				// If the file hasn't been renamed to the new format (omitting timestamp), do that now.
+				$fname = ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}";
+				if ( @file_exists($fname) )
+				{
+					// it's stored in the old format - rename
+					$fname_new = ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}";
+					if ( !@rename($fname, $fname_new) )
+					{
+						return array(
+							'success' => false,
+							'error' => 'rb_file_rename_failed',
+							'action' => $log_entry['action']
+							);
+					}
+				}
+				
+				// Insert a new file entry
+				$time = time();
+				$filename = $db->escape($row['filename']);
+				$mimetype = $db->escape($row['mimetype']);
+				$ext = $db->escape($row['file_extension']);
+				$key = $db->escape($row['file_key']);
+				
+				$q = $db->sql_query('INSERT INTO ' . table_prefix . "files ( time_id, page_id, filename, size, mimetype, file_extension, file_key ) VALUES\n"
+							. "  ( $time, '$this->page_id', '$filename', {$row['size']}, '$mimetype', '$ext', '$key' );");
+				if ( !$q )
+					$db->die_json();
+				
+				// add reupload log entry
+				$username = $db->escape($session->username);
+				$q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, page_id, namespace, author, author_uid, edit_summary ) VALUES\n"
+													. "  ( 'page', 'reupload', $time, '$this->page_id', '$this->namespace', '$username', $session->user_id, '__ROLLBACK__' )");
+				if ( !$q )
+					$db->die_json();
+				
+				return array(
+						'success' => true,
+						'dateline' => $dateline,
+						'action' => $log_entry['action']
+					);
+				
+				break;
+			case 'votereset':
+				if ( !$this->perms->get_permissions('history_rollback_extra') )
+					return 'Denied!';
+				
+				// pull existing vote data
+				$q = $db->sql_query('SELECT delvotes, delvote_ips FROM ' . table_prefix . "pages WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';");
+				if ( !$q )
+					$db->_die();
+				
+				if ( $db->numrows() < 1 )
+					return array(
+							'success' => false,
+							'error' => 'page_not_exist',
+							'action' => $log_entry['action']
+						);
+					
+				list($curr_delvotes, $curr_delvote_ips) = $db->fetchrow_num();
+				$db->free_result();
+				
+				// merge with existing votes
+				$old_delvote_ips = unserialize($log_entry['page_text']);
+				$new_delvote_ips = unserialize($curr_delvote_ips);
+				$new_delvote_ips['u'] = array_unique(array_merge($new_delvote_ips['u'], $old_delvote_ips['u']));
+				$new_delvote_ips['ip'] = array_unique(array_merge($new_delvote_ips['ip'], $old_delvote_ips['ip']));
+				$new_delvotes = count($new_delvote_ips['ip']);
+				$new_delvote_ips = $db->escape(serialize($new_delvote_ips));
+				
+				// update pages table
+				$q = $db->sql_query('UPDATE ' . table_prefix . "pages SET delvotes = $new_delvotes, delvote_ips = '$new_delvote_ips' WHERE urlname = '$this->page_id' AND namespace = '$this->namespace';");
+				
+				$cache->purge('page_meta');
+				
+				return array(
+						'success' => true,
+						'dateline' => $dateline,
+						'action' => $log_entry['action']
+					);
+				break;
+			default:
+				
+				return array(
+						'success' => false,
+						'error' => 'rb_action_not_supported',
+						'action' => $log_entry['action']
+					);
+				
+				break;
+		}
+	}
+	
+	/**
+ 	* Renames the page
+ 	* @param string New name
+ 	* @return array Standard Enano error/success protocol
+ 	*/
+	
+	function rename_page($new_name)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Check permissions
+		if ( !$this->perms->get_permissions('rename') )
+		{
+			return array(
+				'success' => false,
+				'error' => 'access_denied'
+				);
+		}
+		
+		// If this is the same as the current name, return success
+		$page_name = get_page_title_ns($this->page_id, $this->namespace);
+		if ( $page_name === $new_name )
+		{
+			return array(
+				'success' => true
+				);
+		}
+		
+		// Make sure the name is valid
+		$new_name = trim($new_name);
+		if ( empty($new_name) )
+		{
+			return array(
+				'success' => false,
+				'error' => 'invalid_parameter'
+				);
+		}
+		
+		// Log the action
+		$username = $db->escape($session->username);
+		$page_name = $db->escape($page_name);
+		$time = time();
+		
+		$q = $db->sql_query('INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, time_id, date_string ) VALUES\n"
+											. "  ( 'page', 'rename', '{$this->page_id}', '{$this->namespace}', '$username', $session->user_id, '$page_name', '$time', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );");
+		if ( !$q )
+			$db->_die();
+		
+		// Not much to do but to rename it now
+		$new_name = $db->escape($new_name);
+		$q = $db->sql_query('UPDATE ' . table_prefix . "pages SET name = '$new_name' WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
+		if ( !$q )
+			$db->_die();
+		
+		// Update the cache
+		$paths->update_metadata_cache();
+		
+		return array(
+			'success' => true
+			);
+	}
+	
+	/**
+ 	* Sets the protection level of the page
+ 	* @param int Protection level, one of PROTECT_{FULL,SEMI,NONE}
+ 	* @param string Reason for protection - required
+ 	*/
+	
+	function protect_page($protection_level, $reason)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $cache;
+		
+		// Validate permissions
+		if ( !$this->perms->get_permissions('protect') )
+		{
+			return array(
+				'success' => false,
+				'error' => 'access_denied'
+				);
+		}
+		
+		// Validate re-auth
+		if ( !$session->sid_super )
+		{
+			return array(
+				'success' => false,
+				'error' => 'access_denied_need_reauth'
+				);
+		}
+		
+		// Validate input
+		$reason = trim($reason);
+		if ( !in_array($protection_level, array(PROTECT_NONE, PROTECT_FULL, PROTECT_SEMI)) || empty($reason) )
+		{
+			return array(
+				'success' => false,
+				'error' => 'invalid_parameter'
+				);
+		}
+		
+		// Retrieve page metadata
+		$metadata = $this->ns->get_cdata();
+		
+		// Log the action
+		$username = $db->escape($session->username);
+		$time = time();
+		$existing_protection = intval($metadata['protected']);
+		$reason = $db->escape($reason);
+		
+		if ( $existing_protection == $protection_level )
+		{
+			return array(
+				'success' => false,
+				'error' => 'protection_already_there'
+				);
+		}
+		
+		$action = '[ insanity ]';
+		switch($protection_level)
+		{
+			case PROTECT_FULL: $action = 'prot'; break;
+			case PROTECT_NONE: $action = 'unprot'; break;
+			case PROTECT_SEMI: $action = 'semiprot'; break;
+		}
+		
+		$sql = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, page_id, namespace, author, author_uid, edit_summary, time_id, page_text, date_string ) VALUES\n"
+ 				. "  ( 'page', '$action', '{$this->page_id}', '{$this->namespace}', '$username', $author_uid, '$reason', '$time', '$existing_protection', 'DATE_STRING COLUMN OBSOLETE, USE time_id' );";
+		if ( !$db->sql_query($sql) )
+		{
+			$db->die_json();
+		}
+		
+		// Perform the actual protection
+		$q = $db->sql_query('UPDATE ' . table_prefix . "pages SET protected = $protection_level WHERE urlname = '{$this->page_id}' AND namespace = '{$this->namespace}';");
+		if ( !$q )
+			$db->die_json();
+		
+		$cache->purge('page_meta');
+		
+		return array(
+			'success' => true
+			);
+	}
+	
+	/**
+ 	* Sets internal variables.
+ 	* @access private
+ 	*/
+	
+	function _setup($page_id, $namespace, $revision_id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$page_id_cleaned = sanitize_page_id($page_id);
+		
+		$this->revision_id = $revision_id;
+		$this->page_id_unclean = dirtify_page_id($page_id);
+		
+		// resolve namespace
+		$this->ns = namespace_factory($page_id, $namespace, $this->revision_id);
+		$this->page_id =& $this->ns->page_id;
+		$this->namespace =& $this->ns->namespace;
+		
+		$this->perms = $session->fetch_page_acl( $page_id, $namespace );
+		
+		$this->page_exists = $this->ns->exists();
+		$this->title = get_page_title_ns($this->page_id, $this->namespace);
+		
+		profiler_log("PageProcessor [{$this->namespace}:{$this->page_id}]: Ran _setup()");
+	}
+	
+	/**
+ 	* Processes any redirects.
+ 	* @access private
+ 	*/
+	
+	function process_redirects()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $output, $lang;
+		
+		$this->redirect_stack = array();
+		
+		if ( !method_exists($this->ns, 'get_redirect') )
+			return true;
+		
+		if ( !$this->allow_redir )
+			return true;
+		
+		$redirect_count = 0;
+		
+		while ( $result = $this->ns->get_redirect() )
+		{
+			if ( $result['namespace'] == 'Special' || $result['namespace'] == 'Admin' )
+			{
+				// Can't redirect to special/admin page
+				$this->redir_error = $lang->get('page_err_redirect_to_special');
+				break;
+			}
+			if ( $redirect_count == 3 )
+			{
+				// max of 3 internal redirects exceeded
+				$this->redir_error = $lang->get('page_err_redirects_exceeded');
+				break;
+			}
+			
+			$loop = false;
+			foreach ( $this->redirect_stack as $stackel )
+			{
+				if ( $result['page_id'] == $stackel['old_page_id'] && $result['namespace'] == $stackel['old_namespace'] )
+				{
+					$loop = true;
+					break;
+				}
+			}
+			
+			if ( $loop )
+			{
+				// redirect loop
+				$this->redir_error = $lang->get('page_err_redirect_infinite_loop');
+				break;
+			}
+			$new_ns = namespace_factory($result['page_id'], $result['namespace']);
+			if ( !$new_ns->exists() )
+			{
+				// new page doesn't exist
+				$this->redir_error = $lang->get('page_err_redirect_to_nonexistent');
+				break;
+			}
+			
+			// build stack entry
+			$stackel = array(
+					'page_id' => $result['page_id'],
+					'namespace' => $result['namespace'],
+					'old_page_id' => $this->page_id,
+					'old_namespace' => $this->namespace,
+					'old_title' => $this->ns->title
+				);
+			
+			// replace everything (perform the actual redirect)
+			$this->ns = $new_ns;
+			
+			$this->page_id =& $this->ns->page_id;
+			$this->namespace =& $this->ns->namespace;
+			
+			$this->redirect_stack[] = $stackel;
+			
+			$redirect_count++;
+		}
+	}
+		
+	/**
+ 	* Sends the page header, dependent on, of course, whether we're supposed to.
+ 	*/
+	
+	function header()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( $this->send_headers )
+			$template->header();
+	}
+	
+	/**
+ 	* Sends the page footer, dependent on, of course, whether we're supposed to.
+ 	*/
+	
+	function footer()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( $this->send_headers )
+			$template->footer();
+	}
+	
+	/**
+ 	* Fetches the raw, unfiltered page text.
+ 	* @access public
+ 	*/
+	
+	function fetch_text()
+	{
+		return $this->ns->fetch_text();
+	}
+	
+	/**
+ 	* Tells us if the page exists.
+ 	* @return bool
+ 	*/
+	
+	function exists()
+	{
+		return $this->ns->exists();
+	}
+	
+	/**
+ 	* Send the error message to the user that the access to this page is denied.
+ 	* @access private
+ 	*/
+	
+	function err_access_denied()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $email;
+		
+		// Log it for crying out loud
+		$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'illegal_page\', '.time().', \'DEPRECATED\', \''.$db->escape($session->username).'\', ' . $session->user_id . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', \'' . $db->escape(serialize(array($this->page_id, $this->namespace))) . '\')');
+		
+		$ob = '';
+		//$template->tpl_strings['PAGE_NAME'] = 'Access denied';
+		$template->tpl_strings['PAGE_NAME'] = htmlspecialchars( $this->title );
+			
+		if ( $this->send_headers )
+		{
+			$ob .= $template->getHeader();
+		}
+		
+		if ( count($this->redirect_stack) > 0 )
+		{
+			$stack = array_reverse($this->redirect_stack);
+			foreach ( $stack as $oldtarget )
+			{
+				$url = makeUrlNS($oldtarget[1], $oldtarget[0], 'redirect=no', true);
+				$old_page = namespace_factory($oldtarget[0], $oldtarget[1]);
+				$page_data = $old_page->get_cdata();
+				$title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$oldtarget[1]] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $oldtarget[0] ) ) );
+				$a = '<a href="' . $url . '">' . $title . '</a>';
+				
+				$url = makeUrlNS($this->namespace, $this->page_id, 'redirect=no', true);
+				$page_data = $this->ns->get_cdata();
+				$title = ( isset($page_data['name']) ) ? $page_data['name'] : $paths->nslist[$this->namespace] . htmlspecialchars( str_replace('_', ' ', dirtify_page_id( $this->page_id ) ) );
+				$b = '<a href="' . $url . '">' . $title . '</a>';
+				
+				$ob .= '<small>' . $lang->get('page_msg_redirected_from_to', array('from' => $a, 'to' => $b)) . '<br /></small>';
+			}
+		}
+		
+		$email_link = $email->encryptEmail(getConfig('contact_email'), '', '', $lang->get('page_err_access_denied_siteadmin'));
+		
+		$ob .= "<h3>" . $lang->get('page_err_access_denied_title') . "</h3>";
+		$ob .= "<p>" . $lang->get('page_err_access_denied_body', array('site_administration' => $email_link)) . "</p>";
+		
+		if ( $this->send_headers )
+		{
+			$ob .= $template->getFooter();
+		}
+		echo $ob;
+	}
+	
+	/**
+ 	* Inform the user of an incorrect or absent password
+ 	* @access private
+ 	*/
+ 	
+	function err_wrong_password()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$title = $lang->get('page_msg_passrequired_title');
+		$message = ( empty($this->password) ) ?
+ 								'<p>' . $lang->get('page_msg_passrequired') . '</p>' :
+ 								'<p>' . $lang->get('page_msg_pass_wrong') . '</p>';
+		$message .= '<form action="' . makeUrlNS($this->namespace, $this->page_id) . '" method="post">
+ 									<p>
+ 										<label>' . $lang->get('page_lbl_password') . ' <input name="pagepass" type="password" /></label>&nbsp;&nbsp;<input type="submit" value="' . $lang->get('page_btn_password_submit') . '" />
+ 									</p>
+ 								</form>';
+		if ( $this->send_headers )
+		{
+			$template->tpl_strings['PAGE_NAME'] = $title;
+			$template->header();
+			echo "$message";
+			$template->footer();
+		}
+		else
+		{
+			echo "<h2>$title</h2>
+						$message";
+		}
+	}
+	
+	/**
+ 	* Send the error message to the user complaining that there weren't any rows.
+ 	* @access private
+ 	*/
+	
+	function err_no_rows()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$title = 'No text rows';
+		$message = 'While the page\'s existence was verified, there were no rows in the database that matched the query for the text. This may indicate a bug with the software; ask the webmaster for more information. The offending query was:<pre>' . $db->latest_query . '</pre>';
+		if ( $this->send_headers )
+		{
+			$template->tpl_strings['PAGE_NAME'] = $title;
+			$template->header();
+			echo "<p>$message</p>";
+			$template->footer();
+		}
+		else
+		{
+			echo "<h2>$title</h2>
+						<p>$message</p>";
+		}
+	}
+	
+	/**
+ 	* Send an error message and die. For debugging or critical technical errors only - nothing that would under normal circumstances be shown to the user.
+ 	* @param string Error message
+ 	* @param bool If true, send DBAL's debugging information as well
+ 	*/
+ 	
+	function send_error($message, $sql = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$content = "<p>$message</p>";
+		$template->tpl_strings['PAGE_NAME'] = $lang->get('page_msg_general_error');
+		
+		if ( $this->debug['works'] )
+		{
+			$content .= $this->debug['backtrace'];
+		}
+		
+		header('HTTP/1.1 500 Internal Server Error');
+		
+		$template->header();
+		echo $content;
+		$template->footer();
+		
+		$db->close();
+		
+		exit;
+		
+	}
+	
+	/**
+ 	* Raises an error.
+ 	* @param string Error string
+ 	*/
+ 	
+	function raise_error($string)
+	{
+		if ( !is_string($string) )
+			return false;
+		$this->_errors[] = $string;
+	}
+	
+	/**
+ 	* Retrieves the latest error from the error stack and returns it ('pops' the error stack)
+ 	* @return string
+ 	*/
+	
+	function pop_error()
+	{
+		if ( count($this->_errors) < 1 )
+			return false;
+		return array_pop($this->_errors);
+	}
+	
 } // class PageProcessor
 
 ?>
--- a/includes/pageutils.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/pageutils.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,2446 +13,2446 @@
  */
  
 class PageUtils {
-  
-  /**
-   * Tell if a username is used or not.
-   * @param $name the name to check for
-   * @return string
-   */
-  
-  public static function checkusername($name)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $name = str_replace('_', ' ', $name);
-    $q = $db->sql_query('SELECT username FROM ' . table_prefix.'users WHERE username=\'' . $db->escape(rawurldecode($name)) . '\'');
-    if ( !$q )
-    {
-      die($db->get_error());
-    }
-    if ( $db->numrows() < 1)
-    {
-      $db->free_result(); return('good');
-    }
-    else
-    {
-      $db->free_result(); return('bad');
-    }
-  }
-  
-  /**
-   * Get the wiki formatting source for a page
-   * @param $page the full page id (Namespace:Pagename)
-   * @return string
-   * @todo (DONE) Make it require a password (just for security purposes)
-   */
-   
-  public static function getsource($page, $password = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( !isPage($page) )
-    {
-      return '';
-    }
-    
-    list($page_id, $namespace) = RenderMan::strToPageID($page);
-    $ns = namespace_factory($page_id, $namespace);
-    $cdata = $ns->get_cdata();
-    
-    if ( strlen($cdata['password']) == 40 )
-    {
-      if(!$password || ( $password != $cdata['password']))
-      {
-        return 'invalid_password';
-      }
-    }
-    
-    if(!$session->get_permissions('view_source')) // Dependencies handle this for us - this also checks for read privileges
-      return 'access_denied';
-    $pid = RenderMan::strToPageID($page);
-    if($pid[1] == 'Special' || $pid[1] == 'Admin')
-    {
-      die('This type of page (' . $paths->nslist[$pid[1]] . ') cannot be edited because the page source code is not stored in the database.');
-    }
-    
-    $e = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix.'page_text WHERE page_id=\'' . $pid[0] . '\' AND namespace=\'' . $pid[1] . '\'');
-    if ( !$e )
-    {
-      $db->_die('The page text could not be selected.');
-    }
-    if( $db->numrows() < 1 )
-    {
-      return ''; //$db->_die('There were no rows in the text table that matched the page text query.');
-    }
-    
-    $r = $db->fetchrow();
-    $db->free_result();
-    $message = $r['page_text'];
-    
-    return htmlspecialchars($message);
-  }
-  
-  /**
-   * DEPRECATED. Previously returned the full rendered contents of a page.
-   * @param $page the full page id (Namespace:Pagename)
-   * @param $send_headers true if the theme headers should be sent (still dependent on current page settings), false otherwise
-   * @return string
-   */
-  
-  public static function getpage($page, $send_headers = false, $hist_id = false)
-  {
-    die('PageUtils->getpage is deprecated.');
-  }
-  
-  /**
-   * Writes page data to the database, after verifying permissions and running the XSS filter
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $message the text to save
-   * @return string
-   */
-   
-  public static function savepage($page_id, $namespace, $message, $summary = 'No edit summary given', $minor = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $page = new PageProcessor($page_id, $namespace);
-    $cdata = $page->ns->get_cdata();
-    return $page->update_page($message, $summary, $minor, $cdata['page_format']);
-  }
-  
-  /**
-   * Creates a page, both in memory and in the database.
-   * @param string $page_id
-   * @param string $namespace
-   * @return bool true on success, false on failure
-   */
-  
-  public static function createPage($page_id, $namespace, $name = false, $visible = 1)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if(in_array($namespace, Array('Special', 'Admin')))
-    {
-      // echo '<b>Notice:</b> PageUtils::createPage: You can\'t create a special page in the database<br />';
-      return 'You can\'t create a special page in the database';
-    }
-    
-    if(!isset($paths->nslist[$namespace]))
-    {
-      // echo '<b>Notice:</b> PageUtils::createPage: Couldn\'t look up the namespace<br />';
-      return 'Couldn\'t look up the namespace';
-    }
-    
-    $pname = $paths->nslist[$namespace] . $page_id;
-    if(isPage($pname))
-    {
-      // echo '<b>Notice:</b> PageUtils::createPage: Page already exists<br />';
-      return 'Page already exists';
-    }
-    
-    if(!$session->get_permissions('create_page'))
-    {
-      // echo '<b>Notice:</b> PageUtils::createPage: Not authorized to create pages<br />';
-      return 'Not authorized to create pages';
-    }
-    
-    if($session->user_level < USER_LEVEL_ADMIN && $namespace == 'System')
-    {
-      // echo '<b>Notice:</b> PageUtils::createPage: Not authorized to create system messages<br />';
-      return 'Not authorized to create system messages';
-    }
-    
-    if ( substr($page_id, 0, 8) == 'Project:' )
-    {
-      // echo '<b>Notice:</b> PageUtils::createPage: Prefix "Project:" is reserved<br />';
-      return 'The prefix "Project:" is reserved for a parser shortcut; if a page was created using this prefix, it would not be possible to link to it.';
-    }
-    
-    /*
-    // Dunno why this was here. Enano can handle more flexible names than this...
-    $regex = '#^([A-z0-9 _\-\.\/\!\@\(\)]*)$#is';
-    if(!preg_match($regex, $name))
-    {
-      //echo '<b>Notice:</b> PageUtils::createPage: Name contains invalid characters<br />';
-      return 'Name contains invalid characters';
-    }
-    */
-    
-    $page_id = dirtify_page_id($page_id);
-    
-    if ( !$name )
-      $name = str_replace('_', ' ', $page_id);
-    
-    $page_id = sanitize_page_id( $page_id );
-    
-    $prot = ( $namespace == 'System' ) ? 1 : 0;
-    
-    $ips = array(
-      'ip' => array(),
-      'u' => array()
-      );
-    
-    $page_data = Array(
-      'name'=>$name,
-      'urlname'=>$page_id,
-      'namespace'=>$namespace,
-      'special'=>0,'visible'=>1,'comments_on'=>0,'protected'=>$prot,'delvotes'=>0,'delvote_ips'=>serialize($ips),'wiki_mode'=>2,
-    );
-    
-    // die('PageUtils::createpage: Creating page with this data:<pre>' . print_r($page_data, true) . '</pre>');
-    
-    $paths->add_page($page_data);
-    
-    $qa = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace,visible,protected,delvote_ips) VALUES(\'' . $db->escape($name) . '\', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\', '. ( $visible ? '1' : '0' ) .', ' . $prot . ', \'' . $db->escape(serialize($ips)) . '\');');
-    $qb = $db->sql_query('INSERT INTO ' . table_prefix.'page_text(page_id,namespace) VALUES(\'' . $db->escape($page_id) . '\', \'' . $namespace . '\');');
-    $qc = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,author,author_uid,page_id,namespace) VALUES('.time().', \'DEPRECATED\', \'page\', \'create\', \'' . $session->username . '\', ' . $session->user_id . ', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\');');
-    
-    if($qa && $qb && $qc)
-      return 'good';
-    else
-    {
-      return $db->get_error();
-    }
-  }
-  
-  /**
-   * Sets the protection level on a page.
-   * @param $page_id string the page ID
-   * @param $namespace string the namespace
-   * @param $level int level of protection - 0 is off, 1 is full, 2 is semi
-   * @param $reason string why the page is being (un)protected
-   * @return string - "good" on success, in all other cases, an error string (on query failure, calls $db->_die() )
-   */
-  public static function protect($page_id, $namespace, $level, $reason)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $page = new PageProcessor($page_id, $namespace);
-    return $page->protect_page($level, $reason);
-  }
-  
-  /**
-   * Generates an HTML table with history information in it.
-   * @param string the page ID
-   * @param string the namespace
-   * @param string page password
-   * @return string
-   */
-  
-  public static function histlist($page_id, $namespace, $password = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if(!$session->get_permissions('history_view'))
-      return 'Access denied';
-    
-    ob_start();
-    
-    $pname = $paths->get_pathskey($page_id, $namespace);
-    $ns = namespace_factory($page_id, $namespace);
-    $cdata = $ns->get_cdata();
-    
-    if ( !isPage($pname) )
-    {
-      return 'DNE';
-    }
-    
-    if ( isPage($pname) )
-    {
-      $password_exists = ( !empty($cdata['password']) && $cdata['password'] !== sha1('') );
-      if ( $password_exists && $password !== $cdata['password'] )
-      {
-        return '<p>' . $lang->get('history_err_wrong_password') . '</p>';
-      }
-    }
-    
-    $wiki = ( ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $cdata['wiki_mode'] == 1) ? true : false;
-    $prot = ( ( $cdata['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $cdata['protected'] == 1) ? true : false;
-    
-    $q = 'SELECT log_id,time_id,date_string,page_id,namespace,author,author_uid,u.username,edit_summary,minor_edit FROM ' . table_prefix . "logs AS l\n"
-       . "  LEFT JOIN " . table_prefix . "users AS u\n"
-       . "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
-       . "  WHERE log_type='page' AND action='edit' AND page_id='$page_id' AND namespace='$namespace' AND is_draft != 1 ORDER BY time_id DESC;";
-    
-    if ( !($q = $db->sql_query($q)) )
-      $db->_die('The history data for the page "' . $paths->cpage['name'] . '" could not be selected.');
-    
-    echo $lang->get('history_page_subtitle') . '
-          <h3>' . $lang->get('history_heading_edits') . '</h3>';
-    $numrows = $db->numrows();
-    if ( $numrows < 1 )
-    {
-      echo $lang->get('history_no_entries');
-    }
-    else
-    {
-      echo '<form action="'.makeUrlNS($namespace, $page_id, 'do=diff').'" onsubmit="ajaxHistDiff(); return false;" method="get">
-            <input type="submit" value="' . $lang->get('history_btn_compare') . '" />
-            ' . ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars($paths->nslist[$namespace] . $page_id) . '" />' : '' ) . '
-            ' . ( $session->sid_super ? '<input type="hidden" name="auth"  value="' . $session->sid_super . '" />' : '') . '
-            <input type="hidden" name="do" value="diff" />
-            <br /><span>&nbsp;</span>
-            <div class="tblholder">
-            <table border="0" width="100%" cellspacing="1" cellpadding="4">
-            <tr>
-              <th colspan="2">' . $lang->get('history_col_diff') . '</th>
-              <th>' . $lang->get('history_col_datetime') . '</th>
-              <th>' . $lang->get('history_col_user') . '</th>
-              <th>' . $lang->get('history_col_summary') . '</th>
-              <th>' . $lang->get('history_col_minor') . '</th>
-              <th colspan="3">' . $lang->get('history_col_actions') . '</th>
-            </tr>'."\n"."\n";
-      $cls = 'row2';
-      $ticker = 0;
-      
-      while ( $r = $db->fetchrow($q) )
-      {
-        
-        $ticker++;
-        
-        if($cls == 'row2') $cls = 'row1';
-        else $cls = 'row2';
-        
-        echo '<tr>'."\n";
-        
-        // Diff selection
-        if($ticker == 1)
-        {
-          $s1 = '';
-          $s2 = 'checked="checked" ';
-        }
-        elseif($ticker == 2)
-        {
-          $s1 = 'checked="checked" ';
-          $s2 = '';
-        }
-        else
-        {
-          $s1 = '';
-          $s2 = '';
-        }
-        if($ticker > 1)        echo '<td class="' . $cls . '" style="padding: 0;"><input ' . $s1 . 'name="diff1" type="radio" value="' . $r['time_id'] . '" id="diff1_' . $r['time_id'] . '" class="clsDiff1Radio" onclick="selectDiff1Button(this);" /></td>'."\n"; else echo '<td class="' . $cls . '"></td>';
-        if($ticker < $numrows) echo '<td class="' . $cls . '" style="padding: 0;"><input ' . $s2 . 'name="diff2" type="radio" value="' . $r['time_id'] . '" id="diff2_' . $r['time_id'] . '" class="clsDiff2Radio" onclick="selectDiff2Button(this);" /></td>'."\n"; else echo '<td class="' . $cls . '"></td>';
-        
-        // Date and time
-        echo '<td class="' . $cls . '" style="white-space: nowrap;">' . enano_date(ED_DATE | ED_TIME, intval($r['time_id'])) . '</td class="' . $cls . '">'."\n";
-        
-        // User
-        $real_username = $r['author_uid'] > 1 && !empty($r['username']) ? $r['username'] : $r['author'];
-        $rank_info = $session->get_user_rank($r['author_uid']);
-        if ( $session->get_permissions('mod_misc') && is_valid_ip($r['author']) && $r['author_uid'] == 1 )
-        {
-          $rc = ' style="cursor: pointer;" title="' . $lang->get('history_tip_rdns') . '" onclick="ajaxReverseDNS(this, \'' . $r['author'] . '\');"';
-        }
-        else
-        {
-          $rc = '';
-        }
-        echo '<td class="' . $cls . '"' . $rc . '><a href="'.makeUrlNS('User', sanitize_page_id($real_username)).'" ';
-        if ( !isPage($paths->nslist['User'] . sanitize_page_id($real_username)) )
-        {
-          echo 'class="wikilink-nonexistent"';
-        }
-        echo 'style="' . $rank_info['rank_style'] . '">' . htmlspecialchars($real_username) . '</a></td class="' . $cls . '">'."\n";
-        
-        // Edit summary
-        if ( $r['edit_summary'] == 'Automatic backup created when logs were purged' )
-        {
-          $r['edit_summary'] = $lang->get('history_summary_clearlogs');
-        }
-        echo '<td class="' . $cls . '">' . $r['edit_summary'] . '</td>'."\n";
-        
-        // Minor edit
-        echo '<td class="' . $cls . '" style="text-align: center;">'. (( $r['minor_edit'] ) ? 'M' : '' ) .'</td>'."\n";
-        
-        // Actions!
-        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'oldid=' . $r['log_id']) . '" onclick="ajaxHistView(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_view') . '</a></td>'."\n";
-        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrl($paths->nslist['Special'].'Contributions/' . $r['author']) . '">' . $lang->get('history_action_contrib') . '</a></td>'."\n";
-        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'do=edit&amp;revid=' . $r['log_id']) . '" onclick="ajaxEditor(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_restore') . '</a></td>'."\n";
-        
-        echo '</tr>'."\n"."\n";
-        
-      }
-      echo '</table>
-            </div>
-            <br />
-            <input type="hidden" name="do" value="diff" />
-            <input type="submit" value="' . $lang->get('history_btn_compare') . '" />
-            </form>
-            <script type="text/javascript">if ( !KILL_SWITCH ) { buildDiffList(); }</script>';
-    }
-    $db->free_result();
-    echo '<h3>' . $lang->get('history_heading_other') . '</h3>';
-    
-    $sql = 'SELECT log_id,action,time_id,date_string,page_id,namespace,author,author_uid,u.username,edit_summary,minor_edit FROM ' . table_prefix . "logs AS l\n"
-         . "  LEFT JOIN " . table_prefix . "users AS u\n"
-         . "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
-         . "  WHERE log_type='page' AND action!='edit' AND page_id='$page_id' AND namespace='$namespace' AND is_draft != 1 ORDER BY time_id DESC;";
-    
-    if ( !( $q = $db->sql_query($sql)) )
-    {
-      $db->_die('The history data for the page "' . htmlspecialchars($paths->cpage['name']) . '" could not be selected.');
-    }
-    if ( $db->numrows() < 1 )
-    {
-      echo $lang->get('history_no_entries');
-    }
-    else
-    {
-      
-      echo '<div class="tblholder">
-              <table border="0" width="100%" cellspacing="1" cellpadding="4"><tr>
-                <th>' . $lang->get('history_col_datetime') . '</th>
-                <th>' . $lang->get('history_col_user') . '</th>
-                <th>' . $lang->get('history_col_minor') . '</th>
-                <th>' . $lang->get('history_col_action_taken') . '</th>
-                <th>' . $lang->get('history_col_extra') . '</th>
-                <th colspan="2"></th>
-              </tr>';
-      $cls = 'row2';
-      while($r = $db->fetchrow($q)) {
-        
-        if($cls == 'row2') $cls = 'row1';
-        else $cls = 'row2';
-        
-        echo '<tr>';
-        
-        // Date and time
-        echo '<td class="' . $cls . '">' . enano_date(ED_DATE | ED_TIME, intval($r['time_id'])) . '</td class="' . $cls . '">';
-        
-        // User
-        $real_username = $r['author_uid'] > 1 && !empty($r['username']) ? $r['username'] : $r['author'];
-        $rank_info = $session->get_user_rank($r['author_uid']);
-        if ( $session->get_permissions('mod_misc') && is_valid_ip($r['author']) && $r['author_uid'] == 1 )
-        {
-          $rc = ' style="cursor: pointer;" title="' . $lang->get('history_tip_rdns') . '" onclick="ajaxReverseDNS(this, \'' . $r['author'] . '\');"';
-        }
-        else
-        {
-          $rc = '';
-        }
-        echo '<td class="' . $cls . '"' . $rc . '><a href="'.makeUrlNS('User', sanitize_page_id($real_username)).'" ';
-        if ( !isPage($paths->nslist['User'] . sanitize_page_id($real_username)) )
-        {
-          echo 'class="wikilink-nonexistent"';
-        }
-        echo 'style="' . $rank_info['rank_style'] . '">' . htmlspecialchars($real_username) . '</a></td class="' . $cls . '">'."\n";
-        
-        
-        // Minor edit
-        echo '<td class="' . $cls . '" style="text-align: center;">'. (( $r['minor_edit'] ) ? 'M' : '' ) .'</td>';
-        
-        // Action taken
-        echo '<td class="' . $cls . '">';
-        // Some of these are sanitized at insert-time. Others follow the newer Enano policy of stripping HTML at runtime.
-        if    ($r['action']=='prot')     echo $lang->get('history_log_protect') . '</td><td class="' . $cls . '">'     . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
-        elseif($r['action']=='unprot')   echo $lang->get('history_log_unprotect') . '</td><td class="' . $cls . '">'   . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
-        elseif($r['action']=='semiprot') echo $lang->get('history_log_semiprotect') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
-        elseif($r['action']=='rename')   echo $lang->get('history_log_rename') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_oldtitle') . ' '.htmlspecialchars($r['edit_summary']);
-        elseif($r['action']=='create')   echo $lang->get('history_log_create') . '</td><td class="' . $cls . '">';
-        elseif($r['action']=='delete')   echo $lang->get('history_log_delete') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . $r['edit_summary'];
-        elseif($r['action']=='reupload') echo $lang->get('history_log_uploadnew') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__ROLLBACK__' ? $lang->get('history_extra_upload_reversion') : htmlspecialchars($r['edit_summary']) );
-        elseif($r['action']=='votereset')echo $lang->get('history_log_votereset') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_numvotes') . ' ' . $r['edit_summary'];
-        echo '</td>';
-        
-        // Actions!
-        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrl($paths->nslist['Special'].'Contributions/' . $r['author']) . '">' . $lang->get('history_action_contrib') . '</a></td>';
-        echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'do=rollback&amp;id=' . $r['log_id']) . '" onclick="ajaxRollback(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_revert') . '</a></td>';
-        
-        echo '</tr>';
-      }
-      echo '</table></div>';
-    }
-    $db->free_result();
-    $ret = ob_get_contents();
-    ob_end_clean();
-    return $ret;
-  }
-  
-  /**
-   * Rolls back a logged action
-   * @param $id the time ID, a.k.a. the primary key in the logs table
-   * @return string
-   */
-   
-  public static function rollback($id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    // placeholder
-    return 'PageUtils->rollback() is deprecated - use PageProcessor instead.';
-  }
-  
-  /**
-   * Posts a comment.
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $name the name of the person posting, defaults to current username/IP
-   * @param $subject the subject line of the comment
-   * @param $text the comment text
-   * @return string javascript code
-   */
-   
-  public static function addcomment($page_id, $namespace, $name, $subject, $text, $captcha_code = false, $captcha_id = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $_ob = '';
-    if(!$session->get_permissions('post_comments'))
-      return 'Access denied';
-    if(getConfig('comments_need_login') == '2' && !$session->user_logged_in) _die('Access denied to post comments: you need to be logged in first.');
-    if(getConfig('comments_need_login') == '1' && !$session->user_logged_in)
-    {
-      if(!$captcha_code || !$captcha_id) _die('BUG: PageUtils::addcomment: no CAPTCHA data passed to method');
-      $result = $session->get_captcha($captcha_id);
-      if(strtolower($captcha_code) != strtolower($result)) _die('The confirmation code you entered was incorrect.');
-    }
-    $text = RenderMan::preprocess_text($text);
-    $name = $session->user_logged_in ? RenderMan::preprocess_text($session->username) : RenderMan::preprocess_text($name);
-    $subj = RenderMan::preprocess_text($subject);
-    if(getConfig('approve_comments', '0')=='1') $appr = '0'; else $appr = '1';
-    $q = 'INSERT INTO ' . table_prefix.'comments(page_id,namespace,subject,comment_data,name,user_id,approved,time) VALUES(\'' . $page_id . '\',\'' . $namespace . '\',\'' . $subj . '\',\'' . $text . '\',\'' . $name . '\',' . $session->user_id . ',' . $appr . ','.time().')';
-    $e = $db->sql_query($q);
-    if(!$e) die('alert(unescape(\''.rawurlencode('Error inserting comment data: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'))');
-    else $_ob .= '<div class="info-box">Your comment has been posted.</div>';
-    return PageUtils::comments($page_id, $namespace, false, Array(), $_ob);
-  }
-  
-  /**
-   * Generates partly-compiled HTML/Javascript code to be eval'ed by the user's browser to display comments
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $action administrative action to perform, default is false
-   * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc.
-   * @param $_ob text to prepend to output, used by PageUtils::addcomment
-   * @return array
-   * @access private
-   */
-   
-  public static function comments_raw($page_id, $namespace, $action = false, $flags = Array(), $_ob = '')
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $pname = $paths->nslist[$namespace] . $page_id;
-    $template->init_vars();
-    
-    ob_start();
-    
-    if($action && $session->get_permissions('mod_comments')) // Nip hacking attempts in the bud
-    {
-      switch($action) {
-      case "delete":
-        if(isset($flags['id']))
-        {
-          $q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND comment_id='.intval($flags['id']).' LIMIT 1;';
-        } else {
-          $n = $db->escape($flags['name']);
-          $s = $db->escape($flags['subj']);
-          $t = $db->escape($flags['text']);
-          $q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND name=\'' . $n . '\' AND subject=\'' . $s . '\' AND comment_data=\'' . $t . '\' LIMIT 1;';
-        }
-        $e=$db->sql_query($q);
-        if(!$e) die('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
-        break;
-      case "approve":
-        if(isset($flags['id']))
-        {
-          $where = 'comment_id='.intval($flags['id']);
-        } else {
-          $n = $db->escape($flags['name']);
-          $s = $db->escape($flags['subj']);
-          $t = $db->escape($flags['text']);
-          $where = 'name=\'' . $n . '\' AND subject=\'' . $s . '\' AND comment_data=\'' . $t . '\'';
-        }
-        $q = 'SELECT approved FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND ' . $where . ' LIMIT 1;';
-        $e = $db->sql_query($q);
-        if(!$e) die('alert(unesape(\''.rawurlencode('Error selecting approval status: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
-        $r = $db->fetchrow();
-        $db->free_result();
-        $a = ( $r['approved'] ) ? '0' : '1';
-        $q = 'UPDATE ' . table_prefix.'comments SET approved=' . $a . ' WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND ' . $where . ';';
-        $e=$db->sql_query($q);
-        if(!$e) die('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
-        if($a=='1') $v = $lang->get('comment_btn_mod_unapprove');
-        else $v = $lang->get('comment_btn_mod_approve');
-        echo 'document.getElementById("mdgApproveLink'.intval($_GET['id']).'").innerHTML="' . $v . '";';
-        break;
-      }
-    }
-    
-    if(!defined('ENANO_TEMPLATE_LOADED'))
-    {
-      $template->load_theme($session->theme, $session->style);
-    }
-    
-    $tpl = $template->makeParser('comment.tpl');
-    
-    $e = $db->sql_query('SELECT * FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND approved=0;');
-    if(!$e) $db->_die('The comment text data could not be selected.');
-    $num_unapp = $db->numrows();
-    $db->free_result();
-    $e = $db->sql_query('SELECT * FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND approved=1;');
-    if(!$e) $db->_die('The comment text data could not be selected.');
-    $num_app = $db->numrows();
-    $db->free_result();
-    $lq = $db->sql_query('SELECT c.comment_id,c.subject,c.name,c.comment_data,c.approved,c.time,c.user_id,c.ip_address,u.user_level,u.email,u.signature,u.user_has_avatar,u.avatar_type
-                  FROM ' . table_prefix.'comments AS c
-                  LEFT JOIN ' . table_prefix.'users AS u
-                    ON c.user_id=u.user_id
-                  WHERE page_id=\'' . $page_id . '\'
-                  AND namespace=\'' . $namespace . '\' ORDER BY c.time ASC;');
-    if(!$lq) _die('The comment text data could not be selected. '.$db->get_error());
-    $_ob .= '<h3>' . $lang->get('comment_heading') . '</h3>';
-    
-    $n = ( $session->get_permissions('mod_comments')) ? $db->numrows() : $num_app;
-    
-    $subst = array(
-        'num_comments' => $n,
-        'page_type' => $template->namespace_string
-      );
-    
-    $_ob .= '<p>';
-    $_ob .= ( $n == 0 ) ? $lang->get('comment_msg_count_zero', $subst) : ( $n == 1 ? $lang->get('comment_msg_count_one', $subst) : $lang->get('comment_msg_count_plural', $subst) );
-    
-    if ( $session->get_permissions('mod_comments') && $num_unapp > 0 )
-    {
-      $_ob .= ' <span style="color: #D84308">' . $lang->get('comment_msg_count_unapp_mod', array( 'num_unapp' => $num_unapp )) . '</span>';
-    }
-    else if ( !$session->get_permissions('mod_comments') && $num_unapp > 0 )
-    {
-      $ls = ( $num_unapp == 1 ) ? 'comment_msg_count_unapp_one' : 'comment_msg_count_unapp_plural';
-      $_ob .= ' <span>' . $lang->get($ls, array( 'num_unapp' => $num_unapp )) . '</span>';
-    }
-    $_ob .= '</p>';
-    $list = 'list = { ';
-    // _die(htmlspecialchars($ttext));
-    $i = -1;
-    while ( $row = $db->fetchrow($lq) )
-    {
-      $i++;
-      $strings = Array();
-      $bool = Array();
-      if ( $session->get_permissions('mod_comments') || $row['approved'] == COMMENT_APPROVED )
-      {
-        $list .= $i . ' : { \'comment\' : unescape(\''.rawurlencode($row['comment_data']).'\'), \'name\' : unescape(\''.rawurlencode($row['name']).'\'), \'subject\' : unescape(\''.rawurlencode($row['subject']).'\'), }, ';
-        
-        // Comment ID (used in the Javascript apps)
-        $strings['ID'] = (string)$i;
-        
-        // Determine the name, and whether to link to the user page or not
-        $name = '';
-        if($row['user_id'] > 1) $name .= '<a href="'.makeUrlNS('User', sanitize_page_id(' ', '_', $row['name'])).'">';
-        $name .= $row['name'];
-        if($row['user_id'] > 1) $name .= '</a>';
-        $strings['NAME'] = $name; unset($name);
-        
-        // Subject
-        $s = $row['subject'];
-        if(!$row['approved']) $s .= ' <span style="color: #D84308">' . $lang->get('comment_msg_note_unapp') . '</span>';
-        $strings['SUBJECT'] = $s;
-        
-        // Date and time
-        $strings['DATETIME'] = enano_date(ED_DATE | ED_TIME, $row['time']);
-        
-        // User level
-        switch($row['user_level'])
-        {
-          default:
-          case USER_LEVEL_GUEST:
-            $l = $lang->get('user_type_guest');
-            break;
-          case USER_LEVEL_MEMBER:
-          case USER_LEVEL_CHPREF:
-            $l = $lang->get('user_type_member');
-            break;
-          case USER_LEVEL_MOD:
-            $l = $lang->get('user_type_mod');
-            break;
-          case USER_LEVEL_ADMIN:
-            $l = $lang->get('user_type_admin');
-            break;
-        }
-        $strings['USER_LEVEL'] = $l; unset($l);
-        
-        // The actual comment data
-        $strings['DATA'] = RenderMan::render($row['comment_data']);
-        
-        if($session->get_permissions('edit_comments'))
-        {
-          // Edit link
-          $strings['EDIT_LINK'] = '<a href="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=editcomment&amp;id=' . $row['comment_id']) . '" id="editbtn_' . $i . '">' . $lang->get('comment_btn_edit') . '</a>';
-        
-          // Delete link
-          $strings['DELETE_LINK'] = '<a href="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=deletecomment&amp;id=' . $row['comment_id']) . '">' . $lang->get('comment_btn_delete') . '</a>';
-        }
-        else
-        {
-          // Edit link
-          $strings['EDIT_LINK'] = '';
-        
-          // Delete link
-          $strings['DELETE_LINK'] = '';
-        }
-        
-        // Send PM link
-        $strings['SEND_PM_LINK'] = ( $session->user_logged_in && $row['user_id'] > 1 ) ? '<a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/To/' . $row['name']) . '">' . $lang->get('comment_btn_send_privmsg') . '</a><br />' : '';
-        
-        // Add Buddy link
-        $strings['ADD_BUDDY_LINK'] = ( $session->user_logged_in && $row['user_id'] > 1 ) ? '<a href="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Add/' . $row['name']) . '">' . $lang->get('comment_btn_add_buddy') . '</a>' : '';
-        
-        // Mod links
-        $applink = '';
-        $applink .= '<a href="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=admin&amp;action=approve&amp;id=' . $row['comment_id']) . '" id="mdgApproveLink' . $i . '">';
-        if($row['approved']) $applink .= $lang->get('comment_btn_mod_unapprove');
-        else $applink .= $lang->get('comment_btn_mod_approve');
-        $applink .= '</a>';
-        $strings['MOD_APPROVE_LINK'] = $applink; unset($applink);
-        $strings['MOD_DELETE_LINK'] = '<a href="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=admin&amp;action=delete&amp;id=' . $row['comment_id']) . '">' . $lang->get('comment_btn_mod_delete') . '</a>';
-        $strings['MOD_IP_LINK'] = '<span style="opacity: 0.5; filter: alpha(opacity=50);">' . ( ( empty($row['ip_address']) ) ? $lang->get('comment_btn_mod_ip_missing') : $lang->get('comment_btn_mod_ip_notimplemented') ) . '</span>';
-        
-        // Signature
-        $strings['SIGNATURE'] = '';
-        if($row['signature'] != '') $strings['SIGNATURE'] = RenderMan::render($row['signature']);
-        
-        // Avatar
-        if ( $row['user_has_avatar'] == 1 )
-        {
-          $bool['user_has_avatar'] = true;
-          $strings['AVATAR_ALT'] = $lang->get('usercp_avatar_image_alt', array('username' => $row['name']));
-          $strings['AVATAR_URL'] = make_avatar_url(intval($row['user_id']), $row['avatar_type'], $row['email']);
-          $strings['USERPAGE_LINK'] = makeUrlNS('User', $row['name']);
-        }
-        
-        $bool['auth_mod'] = ($session->get_permissions('mod_comments')) ? true : false;
-        $bool['can_edit'] = ( ( $session->user_logged_in && $row['name'] == $session->username && $session->get_permissions('edit_comments') ) || $session->get_permissions('mod_comments') ) ? true : false;
-        $bool['signature'] = ( $strings['SIGNATURE'] == '' ) ? false : true;
-        
-        // Done processing and compiling, now let's cook it into HTML
-        $tpl->assign_vars($strings);
-        $tpl->assign_bool($bool);
-        $_ob .= $tpl->run();
-      }
-    }
-    if(getConfig('comments_need_login') != '2' || $session->user_logged_in)
-    {
-      if($session->get_permissions('post_comments'))
-      {
-        $_ob .= '<h3>' . $lang->get('comment_postform_title') . '</h3>';
-        $_ob .= $lang->get('comment_postform_blurb');
-        if(getConfig('approve_comments', '0')=='1') $_ob .= ' ' . $lang->get('comment_postform_blurb_unapp');
-        if(getConfig('comments_need_login') == '1' && !$session->user_logged_in)
-        {
-          $_ob .= ' ' . $lang->get('comment_postform_blurb_captcha');
-        }
-        $sn = $session->user_logged_in ? $session->username . '<input name="name" id="mdgScreenName" type="hidden" value="' . $session->username . '" />' : '<input name="name" id="mdgScreenName" type="text" size="35" />';
-        $_ob .= '  <a href="#" id="mdgCommentFormLink" style="display: none;" onclick="document.getElementById(\'mdgCommentForm\').style.display=\'block\';this.style.display=\'none\';return false;">' . $lang->get('comment_postform_blurb_link') . '</a>
-        <div id="mdgCommentForm">
-        <form action="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=postcomment').'" method="post" style="margin-left: 1em">
-        <table border="0">
-        <tr><td>' . $lang->get('comment_postform_field_name') . '</td><td>' . $sn . '</td></tr>
-        <tr><td>' . $lang->get('comment_postform_field_subject') . '</td><td><input name="subj" id="mdgSubject" type="text" size="35" /></td></tr>';
-        if(getConfig('comments_need_login') == '1' && !$session->user_logged_in)
-        {
-          $session->kill_captcha();
-          $captcha = $session->make_captcha();
-          $_ob .= '<tr><td>' . $lang->get('comment_postform_field_captcha_title') . '<br /><small>' . $lang->get('comment_postform_field_captcha_blurb') . '</small></td><td><img src="'.makeUrlNS('Special', 'Captcha/' . $captcha) . '" alt="Visual confirmation" style="cursor: pointer;" onclick="this.src = \''.makeUrlNS("Special", "Captcha/".$captcha).'/\'+Math.floor(Math.random() * 100000);" /><input name="captcha_id" id="mdgCaptchaID" type="hidden" value="' . $captcha . '" /><br />' . $lang->get('comment_postform_field_captcha_label') . ' <input name="captcha_input" id="mdgCaptchaInput" type="text" size="10" /><br /><small><script type="text/javascript">document.write("' . $lang->get('comment_postform_field_captcha_cantread_js') . '");</script><noscript>' . $lang->get('comment_postform_field_captcha_cantread_nojs') . '</noscript></small></td></tr>';
-        }
-        $_ob .= '
-        <tr><td valign="top">' . $lang->get('comment_postform_field_comment') . '</td><td><textarea name="text" id="mdgCommentArea" rows="10" cols="40"></textarea></td></tr>
-        <tr><td colspan="2" style="text-align: center;"><input type="submit" value="' . $lang->get('comment_postform_btn_submit') . '" /></td></tr>
-        </table>
-        </form>
-        </div>';
-      }
-    } else {
-      // FIXME: l10n
-      $_ob .= '<h3>' . $lang->get('comment_postform_title') . '</h3><p>You need to be logged in to post comments. <a href="'.makeUrlNS('Special', 'Login/' . $pname . '%2523comments').'">Log in</a></p>';
-    }
-    $list .= '};';
-    echo 'document.getElementById(\'ajaxEditContainer\').innerHTML = unescape(\''. rawurlencode($_ob) .'\');
-    ' . $list;
-    echo 'Fat.fade_all(); document.getElementById(\'mdgCommentForm\').style.display = \'none\'; document.getElementById(\'mdgCommentFormLink\').style.display="inline";';
-    
-    $ret = ob_get_contents();
-    ob_end_clean();
-    return Array($ret, $_ob);
-    
-  }
-  
-  /**
-   * Generates ready-to-execute Javascript code to be eval'ed by the user's browser to display comments
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $action administrative action to perform, default is false
-   * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc.
-   * @param $_ob text to prepend to output, used by PageUtils::addcomment
-   * @return string
-   */
-   
-  public static function comments($page_id, $namespace, $action = false, $id = -1, $_ob = '')
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob);
-    return $r[0];
-  }
-  
-  /**
-   * Generates HTML code for comments - used in browser compatibility mode
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $action administrative action to perform, default is false
-   * @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc.
-   * @param $_ob text to prepend to output, used by PageUtils::addcomment
-   * @return string
-   */
-  
-  public static function comments_html($page_id, $namespace, $action = false, $id = -1, $_ob = '')
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob);
-    return $r[1];
-  }
-  
-  /**
-   * Updates comment data.
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $subject new subject
-   * @param $text new text
-   * @param $old_subject the old subject, unprocessed and identical to the value in the DB
-   * @param $old_text the old text, unprocessed and identical to the value in the DB
-   * @param $id the javascript list ID, used internally by the client-side app
-   * @return string
-   */
-  
-  public static function savecomment($page_id, $namespace, $subject, $text, $old_subject, $old_text, $id = -1)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if(!$session->get_permissions('edit_comments'))
-      return 'result="BAD";error="Access denied"';
-    // Avoid SQL injection
-    $old_text    = $db->escape($old_text);
-    $old_subject = $db->escape($old_subject);
-    // Safety check - username/login
-    if(!$session->get_permissions('mod_comments')) // allow mods to edit comments
-    {
-      if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.<br /><br />Please log in and try again.');
-      $q = 'SELECT c.name FROM ' . table_prefix.'comments c, ' . table_prefix.'users u WHERE comment_data=\'' . $old_text . '\' AND subject=\'' . $old_subject . '\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND u.user_id=c.user_id;';
-      $s = $db->sql_query($q);
-      if(!$s) _die('SQL error during safety check: '.$db->get_error().'<br /><br />Attempted SQL:<br /><pre>'.htmlspecialchars($q).'</pre>');
-      $r = $db->fetchrow($s);
-      $db->free_result();
-      if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.');
-    }
-    $s = RenderMan::preprocess_text($subject);
-    $t = RenderMan::preprocess_text($text);
-    $sql  = 'UPDATE ' . table_prefix.'comments SET subject=\'' . $s . '\',comment_data=\'' . $t . '\' WHERE comment_data=\'' . $old_text . '\' AND subject=\'' . $old_subject . '\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'';
-    $result = $db->sql_query($sql);
-    if($result)
-    {
-      return 'result="GOOD";
-                      list[' . $id . '][\'subject\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $s))))).'\');
-                      list[' . $id . '][\'comment\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t))))).'\'); id = ' . $id . ';
-      s = unescape(\''.rawurlencode($s).'\');
-      t = unescape(\''.str_replace('%5Cn', '<br \\/>', rawurlencode(RenderMan::render(str_replace('{{EnAnO:Newline}}', "\n", stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t)))))).'\');';
-    }
-    else
-    {
-      return 'result="BAD"; error=unescape("'.rawurlencode('Enano encountered a problem whilst saving the comment.
-      Performed SQL:
-      ' . $sql . '
-    
-      Error returned by MySQL: '.$db->get_error()).'");';
-    }
-  }
-  
-  /**
-   * Updates comment data using the comment_id column instead of the old, messy way
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $subject new subject
-   * @param $text new text
-   * @param $id the comment ID (primary key in enano_comments table)
-   * @return string
-   */
-  
-  public static function savecomment_neater($page_id, $namespace, $subject, $text, $id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if(!is_int($id)) die('PageUtils::savecomment: $id is not an integer, aborting for safety');
-    if(!$session->get_permissions('edit_comments'))
-      return 'Access denied';
-    // Safety check - username/login
-    if(!$session->get_permissions('mod_comments')) // allow mods to edit comments
-    {
-      if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.<br /><br />Please log in and try again.');
-      $q = 'SELECT c.name FROM ' . table_prefix.'comments c, ' . table_prefix.'users u WHERE comment_id=' . $id . ' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND u.user_id=c.user_id;';
-      $s = $db->sql_query($q);
-      if(!$s) _die('SQL error during safety check: '.$db->get_error().'<br /><br />Attempted SQL:<br /><pre>'.htmlspecialchars($q).'</pre>');
-      $r = $db->fetchrow($s);
-      if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.');
-      $db->free_result();
-    }
-    $s = RenderMan::preprocess_text($subject);
-    $t = RenderMan::preprocess_text($text);
-    $sql  = 'UPDATE ' . table_prefix.'comments SET subject=\'' . $s . '\',comment_data=\'' . $t . '\' WHERE comment_id=' . $id . ' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'';
-    $result = $db->sql_query($sql);
-    if($result)
-    return 'good';
-    else return 'Enano encountered a problem whilst saving the comment.
-    Performed SQL:
-    ' . $sql . '
-    
-    Error returned by MySQL: '.$db->get_error();
-  }
-  
-  /**
-   * Deletes a comment.
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $name the name the user posted under
-   * @param $subj the subject of the comment to be deleted
-   * @param $text the text of the comment to be deleted
-   * @param $id the javascript list ID, used internally by the client-side app
-   * @return string
-   */
-  
-  public static function deletecomment($page_id, $namespace, $name, $subj, $text, $id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if(!$session->get_permissions('edit_comments'))
-      return 'alert("Access to delete/edit comments is denied");';
-    
-    if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.');
-    $n = $db->escape($name);
-    $s = $db->escape($subj);
-    $t = $db->escape($text);
-    
-    // Safety check - username/login
-    if(!$session->get_permissions('mod_comments')) // allows mods to delete comments
-    {
-      if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.<br /><br />Please log in and try again.');
-      $q = 'SELECT c.name FROM ' . table_prefix.'comments c, ' . table_prefix.'users u WHERE comment_data=\'' . $t . '\' AND subject=\'' . $s . '\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND u.user_id=c.user_id;';
-      $s = $db->sql_query($q);
-      if(!$s) _die('SQL error during safety check: '.$db->get_error().'<br /><br />Attempted SQL:<br /><pre>'.htmlspecialchars($q).'</pre>');
-      $r = $db->fetchrow($s);
-      if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.');
-      $db->free_result();
-    }
-    $q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND name=\'' . $n . '\' AND subject=\'' . $s . '\' AND comment_data=\'' . $t . '\' LIMIT 1;';
-    $e=$db->sql_query($q);
-    if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
-    return('good');
-  }
-  
-  /**
-   * Deletes a comment in a cleaner fashion.
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $id the comment ID (primary key)
-   * @return string
-   */
-  
-  public static function deletecomment_neater($page_id, $namespace, $id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.');
-    
-    if(!$session->get_permissions('edit_comments'))
-      return 'alert("Access to delete/edit comments is denied");';
-    
-    // Safety check - username/login
-    if(!$session->get_permissions('mod_comments')) // allows mods to delete comments
-    {
-      if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.<br /><br />Please log in and try again.');
-      $q = 'SELECT c.name FROM ' . table_prefix.'comments c, ' . table_prefix.'users u WHERE comment_id=' . $id . ' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND u.user_id=c.user_id;';
-      $s = $db->sql_query($q);
-      if(!$s) _die('SQL error during safety check: '.$db->get_error().'<br /><br />Attempted SQL:<br /><pre>'.htmlspecialchars($q).'</pre>');
-      $r = $db->fetchrow($s);
-      if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.');
-      $db->free_result();
-    }
-    $q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND comment_id=' . $id . ' LIMIT 1;';
-    $e=$db->sql_query($q);
-    if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
-    return('good');
-  }
-  
-  /**
-   * Renames a page.
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $name the new name for the page
-   * @return string error string or success message
-   */
-   
-  public static function rename($page_id, $namespace, $name)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $page = new PageProcessor($page_id, $namespace);
-    return $page->rename_page($name);
-  }
-  
-  /**
-   * Flushes (clears) the action logs for a given page
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @return string error/success string
-   */
-   
-  public static function flushlogs($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    if ( !is_object($lang) && defined('IN_ENANO_INSTALL') )
-    {
-      // This is a special exception for the Enano installer, which doesn't init languages yet.
-      $lang = new Language('eng');
-    }
-    if(!$session->get_permissions('clear_logs') && !defined('IN_ENANO_INSTALL'))
-    {
-      return $lang->get('etc_access_denied');
-    }
-    if ( !$session->sid_super )
-    {
-      return $lang->get('etc_access_denied_need_reauth');
-    }
-    
-    $page_id_db = $db->escape($page_id);
-    $namespace_db = $db->escape($namespace);
-    
-    // If we're flushing a file, also clear all revisions before the current
-    if ( $namespace == 'File' )
-    {
-      $q = $db->sql_query('SELECT file_id FROM ' . table_prefix . "files WHERE page_id='$page_id_db' ORDER BY time_id DESC;");
-      if ( !$q )
-        $db->_die();
-      // discard first row (current revision)
-      $db->fetchrow();
-      $id_list = array();
-      while ( $row = $db->fetchrow() )
-        $id_list[] = $row['file_id'];
-      
-      require_once(ENANO_ROOT . '/includes/namespaces/file.php');
-      
-      // clear out each file
-      foreach ( $id_list as $id )
-        Namespace_File::delete_file($id);
-    }
-    
-    $q = $db->sql_query('DELETE FROM ' . table_prefix . "logs WHERE page_id='$page_id_db' AND namespace='$namespace';");
-    if ( !$q )
-      $db->_die('The log entries could not be deleted.');
-    
-    // If the page exists, make a backup of it in case it gets spammed/vandalized
-    // If not, the admin's probably deleting a trash page
-    if ( isPage($paths->get_pathskey($page_id, $namespace)) )
-    {
-      $q = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix . "page_text WHERE page_id='$page_id_db' AND namespace='$namespace_db';");
-      if ( !$q )
-        $db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.');
-      $row = $db->fetchrow();
-      $db->free_result();
-      $minor_edit = ( ENANO_DBLAYER == 'MYSQL' ) ? 'false' : '0';
-      $username = $db->escape($session->username);
-      $q = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, date_string, page_id, namespace, page_text, char_tag, author, author_uid, edit_summary, minor_edit ) VALUES\n"
-         . "  ('page', 'edit', " . time() . ", 'DEPRECATED', '$page_id', '$namespace', '" . $db->escape($row['page_text']) . "', '', '{$username}', $session->user_id, '" . $lang->get('page_flushlogs_backup_summary') . "', $minor_edit);";
-      if ( !$db->sql_query($q) )
-        $db->_die('The history (log) entry could not be inserted into the logs table.');
-    }
-    
-    return $lang->get('ajax_clearlogs_success');
-  }
-  
-  /**
-   * Deletes a page.
-   * @param string $page_id the condemned page ID
-   * @param string $namespace the condemned namespace
-   * @param string The reason for deleting the page in question
-   * @return string
-   */
-   
-  public static function deletepage($page_id, $namespace, $reason)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $cache;
-    $perms = $session->fetch_page_acl($page_id, $namespace);
-    $x = trim($reason);
-    if ( empty($x) )
-    {
-      return $lang->get('ajax_delete_need_reason');
-    }
-    if(!$perms->get_permissions('delete_page')) return('Administrative privileges are required to delete pages, you loser.');
-    
-    if ( !$session->sid_super )
-    {
-      return $lang->get('etc_access_denied_need_reauth');
-    }
-    
-    $e = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,author_uid,edit_summary) VALUES('.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \'page\', \'delete\', \'' . $page_id . '\', \'' . $namespace . '\', \'' . $session->username . '\', ' . $session->user_id . ', \'' . $db->escape(htmlspecialchars($reason)) . '\')');
-    if(!$e) $db->_die('The page log entry could not be inserted.');
-    $e = $db->sql_query('DELETE FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'');
-    if(!$e) $db->_die('The page categorization entries could not be deleted.');
-    $e = $db->sql_query('DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'');
-    if(!$e) $db->_die('The page comments could not be deleted.');
-    $e = $db->sql_query('DELETE FROM ' . table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'');
-    if(!$e) $db->_die('The page text entry could not be deleted.');
-    $e = $db->sql_query('DELETE FROM ' . table_prefix.'pages WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'');
-    if(!$e) $db->_die('The page entry could not be deleted.');
-    if ( $namespace == 'File' )
-    {
-      $e = $db->sql_query('DELETE FROM ' . table_prefix.'files WHERE page_id=\'' . $page_id . '\'');
-      if(!$e) $db->_die('The file entry could not be deleted.');
-    }
-    $cache->purge('page_meta');
-    return $lang->get('ajax_delete_success');
-  }
-  
-  /**
-   * Deletes files associated with a File page.
-   * @param string Page ID
-   */
-  
-  public static function delete_page_files($page_id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $q = $db->sql_query('SELECT file_id, filename, file_key, time_id, file_extension FROM ' . table_prefix . "files WHERE page_id = '{$db->escape($page_id)}';");
-    if ( !$q )
-      $db->_die();
-    
-    while ( $row = $db->fetchrow() )
-    {
-      // wipe original file
-      foreach ( array(
-          ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}",
-          ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}"
-        ) as $orig_file )
-      {
-        if ( file_exists($orig_file) )
-          @unlink($orig_file);
-      }
-      
-      // wipe cached files
-      if ( $dr = @opendir(ENANO_ROOT . '/cache/') )
-      {
-        // lol404.jpg-1217958283-200x320.jpg
-        while ( $dh = @readdir($dr) )
-        {
-          $regexp = ':^' . preg_quote("{$row['filename']}-{$row['time_id']}-") . '[0-9]+x[0-9]+\.' . ltrim($row['file_extension'], '.') . '$:';
-          if ( preg_match($regexp, $dh) )
-          {
-            @unlink(ENANO_ROOT . "/cache/$dh");
-          }
-        }
-        @closedir($dr);
-      }
-    }
-    
-    $q = $db->sql_query('DELETE FROM ' . table_prefix . "files WHERE page_id = '{$db->escape($page_id)}';");
-    if ( !$q )
-      $db->die();
-    
-    return true;
-  }
-  
-  /**
-   * Increments the deletion votes for a page by 1, and adds the current username/IP to the list of users that have voted for the page to prevent dual-voting
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @return string
-   */
-   
-  public static function delvote($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $cache;
-    
-    if ( !$session->get_permissions('vote_delete') )
-    {
-      return $lang->get('etc_access_denied');
-    }
-    
-    if ( $namespace == 'Admin' || $namespace == 'Special' || $namespace == 'System' )
-    {
-      return 'Special pages and system messages can\'t be voted for deletion.';
-    }
-    
-    $pname = $paths->nslist[$namespace] . sanitize_page_id($page_id);
-    
-    if ( !isPage($pname) )
-    {
-      return 'The page does not exist.';
-    }
-    
-    $ns = namespace_factory($page_id, $namespace);
-    $cdata = $ns->get_cdata();
-    
-    $cv  =& $cdata['delvotes'];
-    $ips =& $cdata['delvote_ips'];
-    
-    if ( empty($ips) )
-    {
-      $ips = array(
-        'ip' => array(),
-        'u' => array()
-        );
-    }
-    else
-    {
-      $ips = @unserialize($ips);
-      if ( !$ips )
-      {
-        $ips = array(
-        'ip' => array(),
-        'u' => array()
-        );
-      }
-    }
-    
-    if ( in_array($session->username, $ips['u']) || in_array($_SERVER['REMOTE_ADDR'], $ips['ip']) )
-    {
-      return $lang->get('ajax_delvote_already_voted');
-    }
-    
-    $ips['u'][] = $session->username;
-    $ips['ip'][] = $_SERVER['REMOTE_ADDR'];
-    $ips = $db->escape( serialize($ips) );
-    
-    $cv++;
-    
-    $q = 'UPDATE ' . table_prefix.'pages SET delvotes=' . $cv . ',delvote_ips=\'' . $ips . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'';
-    $w = $db->sql_query($q);
-    if ( !$w )
-      $db->_die();
-    
-    // all done, flush page cache to mark it up
-    $cache->purge('page_meta');
-    
-    return $lang->get('ajax_delvote_success');
-  }
-  
-  /**
-   * Resets the number of votes against a page to 0.
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @return string
-   */
-  
-  public static function resetdelvotes($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $cache;
-    
-    if ( !$session->get_permissions('vote_reset') )
-    {
-      return $lang->get('etc_access_denied');
-    }
-    
-    $page_id = $db->escape($page_id);
-    $namespace = $db->escape($namespace);
-    
-    // pull existing info
-    $q = $db->sql_query('SELECT delvotes, delvote_ips FROM ' . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$namespace'");
-    if ( !$q )
-      $db->_die();
-    if ( $db->numrows() < 1 )
-      return $lang->get('page_err_page_not_exist');
-    
-    list($delvotes, $delvote_ips) = $db->fetchrow_num();
-    $db->free_result();
-    $delvote_ips = $db->escape($delvote_ips);
-    $username = $db->escape($session->username);
-    
-    // log action
-    $time = time();
-    $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs (time_id, log_type, action, edit_summary, page_text, author, author_uid, page_id, namespace) VALUES\n"
-                      . "  ( $time, 'page', 'votereset', '$delvotes', '$delvote_ips', '$username', $session->user_id, '$page_id', '$namespace' )");
-    if ( !$q )
-      $db->_die();
-    
-    // reset votes
-    $empty_vote_record = $db->escape(serialize(array('ip'=>array(),'u'=>array())));
-    $q = 'UPDATE ' . table_prefix.'pages SET delvotes=0,delvote_ips=\'' . $empty_vote_record . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'';
-    $e = $db->sql_query($q);
-    if ( !$e )
-    {
-      $db->_die('The number of delete votes was not reset.');
-    }
-    else
-    {
-      $cache->purge('page_meta');
-      return $lang->get('ajax_delvote_reset_success');
-    }
-  }
-  
-  /**
-   * Gets a list of styles for a given theme name. As of Banshee, this returns JSON.
-   * @param $id the name of the directory for the theme
-   * @return string JSON string with an array containing a list of themes
-   */
-   
-  public static function getstyles()
-  {
-    
-    if ( !preg_match('/^([a-z0-9_-]+)$/', $_GET['id']) )
-      return enano_json_encode(false);
-    
-    $dir = './themes/' . $_GET['id'] . '/css/';
-    $list = Array();
-    // Open a known directory, and proceed to read its contents
-    if (is_dir($dir)) {
-      if ($dh = opendir($dir)) {
-        while (($file = readdir($dh)) !== false) {
-          if ( preg_match('#^(.*?)\.css$#is', $file) && $file != '_printable.css' ) // _printable.css should be included with every theme
-          {                                                                         // it should be a copy of the original style, but
-                                                                                    // mostly black and white
-                                                                                    // Note to self: document this
-            $list[] = substr($file, 0, strlen($file)-4);
-          }
-        }
-        closedir($dh);
-      }
-    }
-    else
-    {
-      return(enano_json_encode(Array('mode' => 'error', 'error' => $dir.' is not a dir')));
-    }
-    
-    return enano_json_encode($list);
-  }
-  
-  /**
-   * Assembles a Javascript app with category information
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @return string Javascript code
-   */
-   
-  public static function catedit($page_id, $namespace)
-  {
-    $d = PageUtils::catedit_raw($page_id, $namespace);
-    return $d[0] . ' /* BEGIN CONTENT */ document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.rawurlencode($d[1]).'\');';
-  }
-  
-  /**
-   * Does the actual HTML/javascript generation for cat editing, but returns an array
-   * @access private
-   */
-   
-  public static function catedit_raw($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    ob_start();
-    $_ob = '';
-    $e = $db->sql_query('SELECT category_id FROM ' . table_prefix.'categories WHERE page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $paths->namespace . '\'');
-    if(!$e) jsdie('Error selecting category information for current page: '.$db->get_error());
-    $cat_current = Array();
-    while($r = $db->fetchrow())
-    {
-      $cat_current[] = $r;
-    }
-    $db->free_result();
-    
-    $cat_all = array();
-    $q = $db->sql_query('SELECT * FROM ' . table_prefix . 'pages WHERE namespace = \'Category\';');
-    if ( !$q )
-      $db->_die();
-    
-    while ( $row = $db->fetchrow() )
-    {
-      $cat_all[] = Namespace_Default::bake_cdata($row);
-    }
-    
-    // Make $cat_all an associative array, like $paths->pages
-    $sz = sizeof($cat_all);
-    for($i=0;$i<$sz;$i++)
-    {
-      $cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i];
-    }
-    // Now, the "zipper" function - join the list of categories with the list of cats that this page is a part of
-    $cat_info = $cat_all;
-    for($i=0;$i<sizeof($cat_current);$i++)
-    {
-      $un = $cat_current[$i]['category_id'];
-      $cat_info[$un]['member'] = true;
-    }
-    // Now copy the information we just set into the numerically named keys
-    for($i=0;$i<sizeof($cat_info)/2;$i++)
-    {
-      $un = $cat_info[$i]['urlname_nons'];
-      $cat_info[$i] = $cat_info[$un];
-    }
-    
-    echo 'catlist = new Array();'; // Initialize the client-side category list
-    $_ob .= '<h3>' . $lang->get('catedit_title') . '</h3>
-             <form name="mdgCatForm" action="'.makeUrlNS($namespace, $page_id, 'do=catedit').'" method="post">';
-    if ( sizeof($cat_info) < 1 )
-    {
-      $_ob .= '<p>' . $lang->get('catedit_no_categories') . '</p>';
-    }
-    for ( $i = 0; $i < sizeof($cat_info) / 2; $i++ )
-    {
-      // Protection code added 1/3/07
-      // Updated 3/4/07
-      $is_prot = false;
-      $perms = $session->fetch_page_acl($cat_info[$i]['urlname_nons'], 'Category');
-      if ( !$session->get_permissions('edit_cat') || !$perms->get_permissions('edit_cat') ||
-         ( $cat_info[$i]['really_protected'] && !$perms->get_permissions('even_when_protected') ) )
-         $is_prot = true;
-      $prot = ( $is_prot ) ? ' disabled="disabled" ' : '';
-      $prottext = ( $is_prot ) ? ' <img alt="(protected)" width="16" height="16" src="'.scriptPath.'/images/lock16.png" />' : '';
-      echo 'catlist[' . $i . '] = \'' . $cat_info[$i]['urlname_nons'] . '\';';
-      $_ob .= '<span class="catCheck"><input ' . $prot . ' name="' . $cat_info[$i]['urlname_nons'] . '" id="mdgCat_' . $cat_info[$i]['urlname_nons'] . '" type="checkbox"';
-      if(isset($cat_info[$i]['member'])) $_ob .= ' checked="checked"';
-      $_ob .= '/>  <label for="mdgCat_' . $cat_info[$i]['urlname_nons'] . '">' . $cat_info[$i]['name'].$prottext.'</label></span><br />';
-    }
-    
-    $disabled = ( sizeof($cat_info) < 1 ) ? 'disabled="disabled"' : '';
-      
-    $_ob .= '<div style="border-top: 1px solid #CCC; padding-top: 5px; margin-top: 10px;"><input name="__enanoSaveButton" ' . $disabled . ' style="font-weight: bold;" type="submit" onclick="ajaxCatSave(); return false;" value="' . $lang->get('etc_save_changes') . '" /> <input name="__enanoCatCancel" type="submit" onclick="ajaxReset(); return false;" value="' . $lang->get('etc_cancel') . '" /></div></form>';
-    
-    $cont = ob_get_contents();
-    ob_end_clean();
-    return Array($cont, $_ob);
-  }
-  
-  /**
-   * Saves category information
-   * WARNING: If $which_cats is empty, all the category information for the selected page will be nuked!
-   * @param $page_id string the page ID
-   * @param $namespace string the namespace
-   * @param $which_cats array associative array of categories to put the page in
-   * @return string "GOOD" on success, error string on failure
-   */
-  
-  public static function catsave($page_id, $namespace, $which_cats)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if(!$session->get_permissions('edit_cat')) return('Insufficient privileges to change category information');
-    
-    $page_perms = $session->fetch_page_acl($page_id, $namespace);
-    $ns = namespace_factory($page_id, $namespace);
-    $page_data = $ns->get_cdata();
-    
-    $cat_all = array();
-    $q = $db->sql_query('SELECT * FROM ' . table_prefix . 'pages WHERE namespace = \'Category\';');
-    if ( !$q )
-      $db->_die();
-    
-    while ( $row = $db->fetchrow() )
-    {
-      $cat_all[] = Namespace_Default::bake_cdata($row);
-    }
-    
-    // Make $cat_all an associative array, like $paths->pages
-    $sz = sizeof($cat_all);
-    for($i=0;$i<$sz;$i++)
-    {
-      $cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i];
-    }
-    
-    $rowlist = Array();
-    
-    for($i=0;$i<sizeof($cat_all)/2;$i++)
-    {
-      $auth = true;
-      $perms = $session->fetch_page_acl($cat_all[$i]['urlname_nons'], 'Category');
-      if ( !$session->get_permissions('edit_cat') || !$perms->get_permissions('edit_cat') ||
-         ( $cat_all[$i]['really_protected'] && !$perms->get_permissions('even_when_protected') ) ||
-         ( !$page_perms->get_permissions('even_when_protected') && $page_data['protected'] == '1' ) )
-         $auth = false;
-      if(!$auth)
-      {
-        // Find out if the page is currently in the category
-        $q = $db->sql_query('SELECT * FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
-        if(!$q)
-          return 'MySQL error: ' . $db->get_error();
-        if($db->numrows() > 0)
-        {
-          $auth = true;
-          $which_cats[$cat_all[$i]['urlname_nons']] = true; // Force the category to stay in its current state
-        }
-        $db->free_result();
-      }
-      if(isset($which_cats[$cat_all[$i]['urlname_nons']]) && $which_cats[$cat_all[$i]['urlname_nons']] == true /* for clarity ;-) */ && $auth ) $rowlist[] = '(\'' . $page_id . '\', \'' . $namespace . '\', \'' . $cat_all[$i]['urlname_nons'] . '\')';
-    }
-    if(sizeof($rowlist) > 0)
-    {
-      $val = implode(',', $rowlist);
-      $q = 'INSERT INTO ' . table_prefix.'categories(page_id,namespace,category_id) VALUES' . $val . ';';
-      $e = $db->sql_query('DELETE FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
-      if(!$e) $db->_die('The old category data could not be deleted.');
-      $e = $db->sql_query($q);
-      if(!$e) $db->_die('The new category data could not be inserted.');
-      return('GOOD');
-    }
-    else
-    {
-      $e = $db->sql_query('DELETE FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
-      if(!$e) $db->_die('The old category data could not be deleted.');
-      return('GOOD');
-    }
-  }
-  
-  /**
-   * Sets the wiki mode level for a page.
-   * @param $page_id string the page ID
-   * @param $namespace string the namespace
-   * @param $level int 0 for off, 1 for on, 2 for use global setting
-   * @return string "GOOD" on success, error string on failure
-   */
-  
-  public static function setwikimode($page_id, $namespace, $level)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $cache;
-    
-    if(!$session->get_permissions('set_wiki_mode')) return('Insufficient access rights');
-    if ( !isset($level) || ( isset($level) && !preg_match('#^([0-2]){1}$#', (string)$level) ) )
-    {
-      return('Invalid mode string');
-    }
-    $q = $db->sql_query('UPDATE ' . table_prefix.'pages SET wiki_mode=' . $level . ' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
-    if ( !$q )
-    {
-      return('Error during update query: '.$db->get_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace());
-    }
-    
-    $cache->purge('page_meta');
-    return('GOOD');
-  }
-  
-  /**
-   * Sets the access password for a page.
-   * @param $page_id string the page ID
-   * @param $namespace string the namespace
-   * @param $pass string the SHA1 hash of the password - if the password doesn't match the regex ^([0-9a-f]*){40,40}$ it will be sha1'ed
-   * @return string
-   */
-  
-  public static function setpass($page_id, $namespace, $pass)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang, $cache;
-    // Determine permissions
-    $ns = namespace_factory($page_id, $namespace);
-    $cdata = $ns->get_cdata();
-    if ( $cdata['password'] != '' )
-      $a = $session->get_permissions('password_reset');
-    else
-      $a = $session->get_permissions('password_set');
-    if ( !$a )
-      return $lang->get('etc_access_denied');
-    if ( !isset($pass) )
-      return('Password was not set on URL');
-    $p = $pass;
-    if ( !preg_match('#([0-9a-f]){40,40}#', $p) )
-    {
-      $p = sha1($p);
-    }
-    if ( $p == 'da39a3ee5e6b4b0d3255bfef95601890afd80709' )
-      // sha1('') = da39a3ee5e6b4b0d3255bfef95601890afd80709
-      $p = '';
-    $e = $db->sql_query('UPDATE ' . table_prefix.'pages SET password=\'' . $p . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
-    if ( !$e )
-    {
-      die('PageUtils::setpass(): Error during update query: '.$db->get_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace());
-    }
-    // Is the new password blank?
-    if ( $p == '' )
-    {
-      return $lang->get('ajax_password_disable_success');
-    }
-    else
-    {
-      return $lang->get('ajax_password_success');
-    }
-  }
-  
-  /**
-   * Generates some preview HTML
-   * @param $text string the wikitext to use
-   * @return string
-   */
-   
-  public static function genPreview($text)
-  {
-    global $lang;
-    $ret = '<div class="info-box">' . $lang->get('editor_preview_blurb') . '</div><div style="background-color: #F8F8F8; padding: 10px; border: 1px dashed #406080; max-height: 250px; overflow: auto; margin: 10px 0;">';
-    $text = RenderMan::render(RenderMan::preprocess_text($text, false, false));
-    ob_start();
-    eval('?>' . $text);
-    $text = ob_get_contents();
-    ob_end_clean();
-    $ret .= $text;
-    $ret .= '</div>';
-    return $ret;
-  }
-  
-  /**
-   * Makes a scrollable box
-   * @param string $text the inner HTML
-   * @param int $height Optional - the maximum height. Defaults to 250.
-   * @return string
-   */
-   
-  public static function scrollBox($text, $height = 250)
-  {
-    return '<div style="background-color: #F8F8F8; padding: 10px; border: 1px dashed #406080; max-height: '.(string)intval($height).'px; overflow: auto; margin: 1em 0 1em 1em;">' . $text . '</div>';
-  }
-  
-  /**
-   * Generates a diff summary between two page revisions.
-   * @param $page_id the page ID
-   * @param $namespace the namespace
-   * @param $id1 the time ID of the first revision
-   * @param $id2 the time ID of the second revision
-   * @return string XHTML-formatted diff
-   */
-   
-  public static function pagediff($page_id, $namespace, $id1, $id2)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if ( !$session->get_permissions('history_view') )
-      return $lang->get('etc_access_denied');
-    
-    if(!preg_match('#^([0-9]+)$#', (string)$id1) ||
-       !preg_match('#^([0-9]+)$#', (string)$id2  )) return 'SQL injection attempt';
-    // OK we made it through security
-    // Safest way to make sure we don't end up with the revisions in wrong columns is to make 2 queries
-    if ( !$q1 = $db->sql_query('SELECT time_id,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE log_id = ' . $id1 . ' AND log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';')) return 'MySQL error: ' . $db->get_error();
-    if ( !$q2 = $db->sql_query('SELECT time_id,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE log_id = ' . $id2 . ' AND log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';')) return 'MySQL error: ' . $db->get_error();
-    $row1 = $db->fetchrow($q1);
-    $db->free_result($q1);
-    $row2 = $db->fetchrow($q2);
-    $db->free_result($q2);
-    if(sizeof($row1) < 1 || sizeof($row2) < 2)
-    {
-      if ( !$q1 = $db->sql_query('SELECT time_id,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE time_id = ' . $id1 . ' AND log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';')) return 'MySQL error: ' . $db->get_error();
-      if ( !$q2 = $db->sql_query('SELECT time_id,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE time_id = ' . $id2 . ' AND log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';')) return 'MySQL error: ' . $db->get_error();
-      $row1 = $db->fetchrow($q1);
-      $db->free_result($q1);
-      $row2 = $db->fetchrow($q2);
-      $db->free_result($q2);
-      if(sizeof($row1) < 1 || sizeof($row2) < 2)
-        return 'Couldn\'t find any rows that matched the query. The time ID probably doesn\'t exist in the logs table.';
-    }
-    $text1 = $row1['page_text'];
-    $text2 = $row2['page_text'];
-    $time1 = enano_date(ED_DATE | ED_TIME, $row1['time_id']);
-    $time2 = enano_date(ED_DATE | ED_TIME, $row2['time_id']);
-    $_ob = "
-    <p>" . $lang->get('history_lbl_comparingrevisions') . " {$time1} &rarr; {$time2}</p>
-    ";
-    // Free some memory
-    unset($row1, $row2, $q1, $q2);
-    
-    $_ob .= RenderMan::diff($text1, $text2);
-    return $_ob;
-  }
-  
-  /**
-   * Gets ACL information about the selected page for target type X and target ID Y.
-   * @param array $parms What to select. This is an array purely for JSON compatibility. It should be an associative array with keys target_type and target_id.
-   * @return array
-   */
-   
-  public static function acl_editor($parms = Array())
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if(!$session->get_permissions('edit_acl') && ( $session->user_level < USER_LEVEL_ADMIN || !defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL')) )
-    {
-      return Array(
-        'mode' => 'error',
-        'error' => $lang->get('acl_err_access_denied')
-        );
-    }
-    if ( !$session->sid_super )
-    {
-      return Array(
-        'mode' => 'error',
-        'error' => $lang->get('etc_access_denied_need_reauth')
-        );
-    }
-    $parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false;
-    $parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false;
-    $page_id =& $parms['page_id'];
-    $namespace =& $parms['namespace'];
-    $page_where_clause      = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\'' . $db->escape($page_id) . '\' AND a.namespace=\'' . $db->escape($namespace) . '\'';
-    $page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\'';
-    //die(print_r($page_id,true));
-    $template->load_theme();
-    // $perms_obj = $session->fetch_page_acl($page_id, $namespace);
-    $perms_obj =& $session;
-    $return = Array();
-    if ( !file_exists(ENANO_ROOT . '/themes/' . $session->theme . '/acledit.tpl') )
-    {
-      return Array(
-        'mode' => 'error',
-        'error' => $lang->get('acl_err_missing_template'),
-      );
-    }
-    $return['template'] = $template->extract_vars('acledit.tpl');
-    $return['page_id'] = $page_id;
-    $return['namespace'] = $namespace;
-    if(isset($parms['mode']))
-    {
-      switch($parms['mode'])
-      {
-        case 'listgroups':
-          $return['groups'] = Array();
-          $q = $db->sql_query('SELECT group_id,group_name FROM ' . table_prefix.'groups ORDER BY group_name ASC;');
-          while($row = $db->fetchrow())
-          {
-            $return['groups'][] = Array(
-              'id' => $row['group_id'],
-              'name' => $row['group_name'],
-              );
-          }
-          $db->free_result();
-          $return['page_groups'] = Array();
-          $q = $db->sql_query('SELECT pg_id,pg_name FROM ' . table_prefix.'page_groups ORDER BY pg_name ASC;');
-          if ( !$q )
-            return Array(
-              'mode' => 'error',
-              'error' => $db->get_error()
-              );
-          while ( $row = $db->fetchrow() )
-          {
-            $return['page_groups'][] = Array(
-                'id' => $row['pg_id'],
-                'name' => $row['pg_name']
-              );
-          }
-          break;
-        case 'seltarget_id':
-          if ( !is_int($parms['target_id']) )
-          {
-            return Array(
-              'mode' => 'error',
-              'error' => 'Expected parameter target_id type int'
-              );
-          }
-          $q = $db->sql_query('SELECT target_id, target_type, page_id, namespace, rules FROM ' . table_prefix . "acl WHERE rule_id = {$parms['target_id']};");
-          if ( !$q )
-            return Array(
-              'mode' => 'error',
-              'error' => $db->get_error()
-              );
-          if ( $db->numrows() < 1 )
-            return Array(
-              'mode' => 'error',
-              'error' => "No rule with ID {$parms['target_id']} found"
-              );
-            $parms = $db->fetchrow();
-            $db->free_result();
-            
-            // regenerate page selection
-            $parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false;
-            $parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false;
-            $parms['mode'] = 'seltarget_id';
-            $page_id =& $parms['page_id'];
-            $namespace =& $parms['namespace'];
-            $page_where_clause      = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\'' . $db->escape($page_id) . '\' AND a.namespace=\'' . $db->escape($namespace) . '\'';
-            $page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\'';
-            
-            $return['page_id'] = $parms['page_id'];
-            $return['namespace'] = $parms['namespace'];
-            
-            // From here, let the seltarget handler take over
-        case 'seltarget':
-          $return['mode'] = 'seltarget';
-          $return['acl_types'] = $perms_obj->acl_types;
-          $return['acl_deps'] = $perms_obj->acl_deps;
-          $return['acl_descs'] = $perms_obj->acl_descs;
-          $return['target_type'] = $parms['target_type'];
-          $return['target_id'] = $parms['target_id'];
-          switch($parms['target_type'])
-          {
-            case ACL_TYPE_USER:
-              $user_col = ( $parms['mode'] == 'seltarget_id' ) ? 'user_id' : 'username';
-              $q = $db->sql_query('SELECT a.rules,u.user_id,u.username FROM ' . table_prefix.'users AS u
-                  LEFT JOIN ' . table_prefix.'acl AS a
-                    ON a.target_id=u.user_id
-                  WHERE a.target_type='.ACL_TYPE_USER.'
-                    AND u.' . $user_col . ' = \'' . $db->escape($parms['target_id']) . '\'
-                    ' . $page_where_clause . ';');
-              if(!$q)
-                return(Array('mode'=>'error','error'=>$db->get_error()));
-              if($db->numrows() < 1)
-              {
-                $return['type'] = 'new';
-                $q = $db->sql_query('SELECT user_id,username FROM ' . table_prefix.'users WHERE username=\'' . $db->escape($parms['target_id']) . '\';');
-                if(!$q)
-                  return(Array('mode'=>'error','error'=>$db->get_error()));
-                if($db->numrows() < 1)
-                  return Array('mode'=>'error','error'=>$lang->get('acl_err_user_not_found'),'debug' => $db->sql_backtrace());
-                $row = $db->fetchrow();
-                $return['target_name'] = $row['username'];
-                $return['target_id'] = intval($row['user_id']);
-                $return['current_perms'] = array();
-              }
-              else
-              {
-                $return['type'] = 'edit';
-                $row = $db->fetchrow();
-                $return['target_name'] = $row['username'];
-                $return['target_id'] = intval($row['user_id']);
-                $return['current_perms'] = $session->string_to_perm($row['rules']);
-              }
-              $db->free_result();
-              // Eliminate types that don't apply to this namespace
-              if ( $namespace && $namespace != '__PageGroup' )
-              {
-                foreach ( $return['current_perms'] AS $i => $perm )
-                {
-                  if ( ( $page_id != null && $namespace != null ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) )
-                  {
-                    // echo "// SCOPE CONTROL: eliminating: $i\n";
-                    unset($return['current_perms'][$i]);
-                    unset($return['acl_types'][$i]);
-                    unset($return['acl_descs'][$i]);
-                    unset($return['acl_deps'][$i]);
-                  }
-                }
-              }
-              break;
-            case ACL_TYPE_GROUP:
-              $q = $db->sql_query('SELECT a.rules,g.group_name,g.group_id FROM ' . table_prefix.'groups AS g
-                  LEFT JOIN ' . table_prefix.'acl AS a
-                    ON a.target_id=g.group_id
-                  WHERE a.target_type='.ACL_TYPE_GROUP.'
-                    AND g.group_id=\''.intval($parms['target_id']).'\'
-                    ' . $page_where_clause . ';');
-              if(!$q)
-                return(Array('mode'=>'error','error'=>$db->get_error()));
-              if($db->numrows() < 1)
-              {
-                $return['type'] = 'new';
-                $q = $db->sql_query('SELECT group_id,group_name FROM ' . table_prefix.'groups WHERE group_id=\''.intval($parms['target_id']).'\';');
-                if(!$q)
-                  return(Array('mode'=>'error','error'=>$db->get_error()));
-                if($db->numrows() < 1)
-                  return Array('mode'=>'error','error'=>$lang->get('acl_err_bad_group_id'));
-                $row = $db->fetchrow();
-                $return['target_name'] = $row['group_name'];
-                $return['target_id'] = intval($row['group_id']);
-                $return['current_perms'] = array();
-              }
-              else
-              {
-                $return['type'] = 'edit';
-                $row = $db->fetchrow();
-                $return['target_name'] = $row['group_name'];
-                $return['target_id'] = intval($row['group_id']);
-                $return['current_perms'] = $session->string_to_perm($row['rules']);
-              }
-              $db->free_result();
-              // Eliminate types that don't apply to this namespace
-              if ( $namespace && $namespace != '__PageGroup' )
-              {
-                foreach ( $return['current_perms'] AS $i => $perm )
-                {
-                  if ( ( $page_id != false && $namespace != false ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) )
-                  {
-                    // echo "// SCOPE CONTROL: eliminating: $i\n"; //; ".print_r($namespace,true).":".print_r($page_id,true)."\n";
-                    unset($return['current_perms'][$i]);
-                    unset($return['acl_types'][$i]);
-                    unset($return['acl_descs'][$i]);
-                    unset($return['acl_deps'][$i]);
-                  }
-                }
-              }
-              //return Array('mode'=>'debug','text'=>print_r($return, true));
-              break;
-            default:
-              return Array('mode'=>'error','error','Invalid ACL type ID');
-              break;
-          }
-          return $return;
-          break;
-        case 'save_new':
-        case 'save_edit':
-          if ( defined('ENANO_DEMO_MODE') )
-          {
-            return Array('mode'=>'error','error'=>$lang->get('acl_err_demo'));
-          }
-          $q = $db->sql_query('DELETE FROM ' . table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).'
-            ' . $page_where_clause_lite . ';');
-          if(!$q)
-            return Array('mode'=>'error','error'=>$db->get_error());
-          if ( sizeof ( $parms['perms'] ) < 1 )
-          {
-            // As of 1.1.x, this returns success because the rule length is zero if the user selected "inherit" in all columns
-            return Array(
-              'mode' => 'success',
-              'target_type' => $parms['target_type'],
-              'target_id' => $parms['target_id'],
-              'target_name' => $parms['target_name'],
-              'page_id' => $page_id,
-              'namespace' => $namespace,
-            );
-          }
-          $rules = $session->perm_to_string($parms['perms']);
-          $q = ($page_id && $namespace) ? 'INSERT INTO ' . table_prefix.'acl ( target_type, target_id, page_id, namespace, rules )
-                                             VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \'' . $db->escape($page_id) . '\', \'' . $db->escape($namespace) . '\', \'' . $db->escape($rules) . '\' )' :
-                                          'INSERT INTO ' . table_prefix.'acl ( target_type, target_id, rules )
-                                             VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \'' . $db->escape($rules) . '\' )';
-          if(!$db->sql_query($q)) return Array('mode'=>'error','error'=>$db->get_error());
-          return Array(
-              'mode' => 'success',
-              'target_type' => $parms['target_type'],
-              'target_id' => $parms['target_id'],
-              'target_name' => $parms['target_name'],
-              'page_id' => $page_id,
-              'namespace' => $namespace,
-            );
-          break;
-        case 'delete':
-          if ( defined('ENANO_DEMO_MODE') )
-          {
-            return Array('mode'=>'error','error'=>$lang->get('acl_err_demo'));
-          }
-          $sql = 'DELETE FROM ' . table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).'
-            ' . $page_where_clause_lite . ';';
-          $q = $db->sql_query($sql);
-          if(!$q)
-            return Array('mode'=>'error','error'=>$db->get_error());
-          return Array(
-              'mode' => 'delete',
-              'target_type' => $parms['target_type'],
-              'target_id' => $parms['target_id'],
-              'target_name' => $parms['target_name'],
-              'page_id' => $page_id,
-              'namespace' => $namespace,
-            );
-          break;
-        case 'list_existing':
-          
-          $return = array(
-              'mode'  => 'list_existing',
-              'key'   => acl_list_draw_key(),
-              'rules' => array()
-            );
-          
-          $acl_columns = 'a.' . implode(', a.', $db->columns_in(table_prefix . 'acl'));
-          $users_columns = 'u.' . implode(', u.', $db->columns_in(table_prefix . 'users'));
-          $groups_columns = 'g.' . implode(', g.', $db->columns_in(table_prefix . 'groups'));
-          $pg_columns = 'p.' . implode(', p.', array('pg_id', 'pg_type', 'pg_name', 'pg_target'));
-          
-          $q = $db->sql_query("SELECT a.rule_id, u.username, g.group_name, a.target_type, a.target_id, a.page_id, a.namespace, a.rules, p.pg_name\n"
-                  . "  FROM " . table_prefix . "acl AS a\n"
-                  . "  LEFT JOIN " . table_prefix . "users AS u\n"
-                  . "    ON ( (a.target_type = " . ACL_TYPE_USER . " AND a.target_id = u.user_id) OR (u.user_id IS NULL) )\n"
-                  . "  LEFT JOIN " . table_prefix . "groups AS g\n"
-                  . "    ON ( (a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id) OR (g.group_id IS NULL) )\n"
-                  . "  LEFT JOIN " . table_prefix . "page_groups as p\n"
-                  . "    ON ( (a.namespace = '__PageGroup' AND a.page_id = CAST(p.pg_id AS CHAR)) OR (p.pg_id IS NULL) )\n"
-                  . "  WHERE ( a.target_type = " . ACL_TYPE_USER . " OR a.target_type = " . ACL_TYPE_GROUP . " )\n"
-                  . "  GROUP BY a.rule_id, $acl_columns, $users_columns, $groups_columns, $pg_columns\n"
-                  . "  ORDER BY a.target_type ASC, a.rule_id ASC;"
-                );
-          
-          if ( !$q )
-            $db->_die();
-          
-          while ( $row = $db->fetchrow($q) )
-          {
-            if ( $row['target_type'] == ACL_TYPE_USER && empty($row['username']) )
-            {
-              // This is only done if we have an ACL affecting a user that doesn't exist.
-              // Nice little bit of maintenance to have.
-              if ( !$db->sql_query("DELETE FROM " . table_prefix . "acl WHERE rule_id = {$row['rule_id']};") )
-                $db->_die();
-              continue;
-            }
-            $score = get_acl_rule_score($row['rules']);
-            $deep_limit = ACL_SCALE_MINIMAL_SHADE;
-            // Determine background color of cell by score
-            if ( $score > 5 )
-            {
-              // high score, show in green
-              $color = 2.5 * $score;
-              if ( $color > 255 )
-                $color = 255;
-              $color = round($color);
-              // blend with the colordepth limit
-              $color = $deep_limit + ( ( 0xFF - $deep_limit ) - ( ( $color / 0xFF ) * ( 0xFF - $deep_limit ) ) );
-              $color = dechex($color);
-              $color = "{$color}ff{$color}";
-            }
-            else if ( $score < -5 )
-            {
-              // low score, show in red
-              $color = 0 - $score;
-              $color = 2.5 * $color;
-              if ( $color > 255 )
-                $color = 255;
-              $color = round($color);
-              // blend with the colordepth limit
-              $color = $deep_limit + ( ( 0xFF - $deep_limit ) - ( ( $color / 0xFF ) * ( 0xFF - $deep_limit ) ) );
-              $color = dechex($color);
-              $color = "ff{$color}{$color}";
-            }
-            else
-            {
-              $color = 'efefef';
-            }
-            
-            // Rate rule textually based on its score
-            if ( $score >= 70 )
-              $desc = $lang->get('acl_msg_scale_allow');
-            else if ( $score >= 50 )
-              $desc = $lang->get('acl_msg_scale_mostly_allow');
-            else if ( $score >= 25 )
-              $desc = $lang->get('acl_msg_scale_some_allow');
-            else if ( $score >= -25 )
-              $desc = $lang->get('acl_msg_scale_mixed');
-            else if ( $score <= -70 )
-              $desc = $lang->get('acl_msg_scale_deny');
-            else if ( $score <= -50 )
-              $desc = $lang->get('acl_msg_scale_mostly_deny');
-            else if ( $score <= -25 )
-              $desc = $lang->get('acl_msg_scale_some_deny');
-            
-            // group and user target info
-            $info = '';
-            if ( $row['target_type'] == ACL_TYPE_USER )
-              $info = $lang->get('acl_msg_list_user', array( 'username' => $row['username'] )); // "(User: {$row['username']})";
-            else if ( $row['target_type'] == ACL_TYPE_GROUP )
-              $info = $lang->get('acl_msg_list_group', array( 'group' => $row['group_name'] ));
-            
-            // affected pages info
-            if ( $row['page_id'] && $row['namespace'] && $row['namespace'] != '__PageGroup' )
-              $info .= $lang->get('acl_msg_list_on_page', array( 'page_name' => "{$row['namespace']}:{$row['page_id']}" ));
-            else if ( $row['page_id'] && $row['namespace'] && $row['namespace'] == '__PageGroup' )
-              $info .= $lang->get('acl_msg_list_on_page_group', array( 'page_group' => $row['pg_name'] ));
-            else
-              $info .= $lang->get('acl_msg_list_entire_site');
-              
-            $score_string = $lang->get('acl_msg_list_score', array
-              (
-                'score' => $score,
-                'desc'  => $desc,
-                'info'  => $info
-                ));
-            $return['rules'][] = array(
-              'score_string' => $score_string,
-              'rule_id'      => $row['rule_id'],
-              'color'        => $color
-              );
-          }
-          
-          break;
-        case 'list_presets':
-          $presets = array();
-          $q = $db->sql_query('SELECT page_id AS preset_name, rule_id, rules FROM ' . table_prefix . "acl WHERE target_type = " . ACL_TYPE_PRESET . ";");
-          if ( !$q )
-            $db->die_json();
-          
-          while ( $row = $db->fetchrow() )
-          {
-            $row['rules'] = $session->string_to_perm($row['rules']);
-            $presets[] = $row;
-          }
-          
-          return array(
-            'mode' => 'list_existing',
-            'presets' => $presets
-          );
-          break;
-        case 'save_preset':
-          if ( empty($parms['preset_name']) )
-          {
-            return array(
-              'mode' => 'error',
-              'error' => $lang->get('acl_err_preset_name_empty')
-            );
-          }
-          $preset_name = $db->escape($parms['preset_name']);
-          $q = $db->sql_query('DELETE FROM ' . table_prefix . "acl WHERE target_type = " . ACL_TYPE_PRESET . " AND page_id = '$preset_name';");
-          if ( !$q )
-            $db->die_json();
-          
-          $perms = $session->perm_to_string($parms['perms']);
-          if ( !$perms )
-          {
-            return array(
-              'mode' => 'error',
-              'error' => $lang->get('acl_err_preset_is_blank')
-            );
-          }
-          
-          $perms = $db->escape($perms);
-          $q = $db->sql_query('INSERT INTO ' . table_prefix . "acl(page_id, target_type, rules) VALUES\n"
-                            . "  ( '$preset_name', " . ACL_TYPE_PRESET . ", '$perms' );");
-          if ( !$q )
-            $db->die_json();
-          
-          return array(
-              'mode' => 'success'
-            );
-          break;
-        case 'trace':
-          list($targetpid, $targetns) = RenderMan::strToPageID($parms['page']);
-          try
-          {
-            $perms = $session->fetch_page_acl_user($parms['user'], $targetpid, $targetns);
-            $perm_table = array(
-                AUTH_ALLOW => 'acl_lbl_field_allow',
-                AUTH_WIKIMODE => 'acl_lbl_field_wikimode',
-                AUTH_DISALLOW => 'acl_lbl_field_disallow',
-                AUTH_DENY => 'acl_lbl_field_deny'
-              );
-            
-            $return = array(
-              'mode' => 'trace',
-              'perms' => array()
-            );
-            
-            foreach ( $perms->perm_resolve_table as $perm_type => $lookup_data )
-            {
-              if ( !$session->check_acl_scope($perm_type, $targetns) )
-                continue;
-              
-              $src_l10n = $lang->get($session->acl_inherit_lang_table[$lookup_data['src']], $lookup_data);
-              $divclass = preg_replace('/^acl_inherit_/', '', $session->acl_inherit_lang_table[$lookup_data['src']]);
-              $perm_string = $lang->get($perm_table[$perms->perms[$perm_type]]);
-              $perm_name = $lang->get($session->acl_descs[$perm_type]);
-              
-              $return['perms'][$perm_type] = array(
-                  'divclass' => "acl_inherit acl_$divclass",
-                  'perm_type' => $perm_type,
-                  'perm_name' => $perm_name,
-                  'perm_value' => $perm_string,
-                  'perm_src' => $src_l10n,
-                  'rule_id' => intval($lookup_data['rule_id']),
-                  'bad_deps' => $perms->acl_check_deps($perm_type, true)
-                );
-            }
-            
-            // group rules if possible
-            $return['groups'] = array();
-            foreach ( $return['perms'] as $rule )
-            {
-              if ( !isset($return['groups'][$rule['rule_id']]) )
-              {
-                $return['groups'][$rule['rule_id']] = array();
-              }
-              $return['groups'][$rule['rule_id']][] = $rule['perm_type'];
-            }
-          }
-          catch ( Exception $e )
-          {
-            $return = array(
-                'mode' => 'error',
-                'error' => $e->getMessage()
-              );
-          }
-          
-          break;
-        default:
-          return Array('mode'=>'error','error'=>'Hacking attempt');
-          break;
-      }
-    }
-    return $return;
-  }
-  
-  /**
-   * Same as PageUtils::acl_editor(), but the parms are a JSON string instead of an array. This also returns a JSON string.
-   * @param string $parms Same as PageUtils::acl_editor/$parms, but should be a valid JSON string.
-   * @return string
-   */
-   
-  public static function acl_json($parms = '{ }')
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    try
-    {
-      $parms = enano_json_decode($parms);
-    }
-    catch ( Zend_Json_Exception $e )
-    {
-      $parms = array();
-    }
-    $ret = PageUtils::acl_editor($parms);
-    $ret = enano_json_encode($ret);
-    return $ret;
-  }
-  
-  /**
-   * A non-Javascript frontend for the ACL API.
-   * @param array The request data, if any, this should be in the format required by PageUtils::acl_editor()
-   */
-   
-  public static function aclmanager($parms)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    ob_start();
-    // Convenience
-    $formstart = '<form 
-                    action="' . makeUrl($paths->page, 'do=aclmanager', true) . '"
-                    method="post" enctype="multipart/form-data"
-                    onsubmit="if(!submitAuthorized) return false;"
-                    >';
-    $formend   = '</form>';
-    $parms    = PageUtils::acl_preprocess($parms);
-    $response = PageUtils::acl_editor($parms);
-    $response = PageUtils::acl_postprocess($response);
-    
-    //die('<pre>' . htmlspecialchars(print_r($response, true)) . '</pre>');
-    
-    switch($response['mode'])
-    {
-      case 'debug':
-        echo '<pre>' . htmlspecialchars($response['text']) . '</pre>';
-        break;
-      case 'stage1':
-        echo '<h3>' . $lang->get('acl_lbl_welcome_title') . '</h3>
-              <p>' . $lang->get('acl_lbl_welcome_body') . '</p>';
-        echo $formstart;
-        echo '<p><label><input type="radio" name="data[target_type]" value="' . ACL_TYPE_GROUP . '" checked="checked" /> ' . $lang->get('acl_radio_usergroup') . '</label></p>
-              <p><select name="data[target_id_grp]">';
-        foreach ( $response['groups'] as $group )
-        {
-          echo '<option value="' . $group['id'] . '">' . $group['name'] . '</option>';
-        }
-        
-        // page group selector
-        $groupsel = '';
-        if ( count($response['page_groups']) > 0 )
-        {
-          $groupsel = '<p><label><input type="radio" name="data[scope]" value="page_group" /> ' . $lang->get('acl_radio_scope_pagegroup') . '</label></p>
-                       <p><select name="data[pg_id]">';
-          foreach ( $response['page_groups'] as $grp )
-          {
-            $groupsel .= '<option value="' . $grp['id'] . '">' . htmlspecialchars($grp['name']) . '</option>';
-          }
-          $groupsel .= '</select></p>';
-        }
-        
-        echo '</select></p>
-              <p><label><input type="radio" name="data[target_type]" value="' . ACL_TYPE_USER . '" /> ' . $lang->get('acl_radio_user') . '</label></p>
-              <p>' . $template->username_field('data[target_id_user]') . '</p>
-              <p>' . $lang->get('acl_lbl_scope') . '</p>
-              <p><label><input name="data[scope]" value="only_this" type="radio" checked="checked" /> ' . $lang->get('acl_radio_scope_thispage') . '</p>
-              ' . $groupsel . '
-              <p><label><input name="data[scope]" value="entire_site" type="radio" /> ' . $lang->get('acl_radio_scope_wholesite') . '</p>
-              <div style="margin: 0 auto 0 0; text-align: right;">
-                <input name="data[mode]" value="seltarget" type="hidden" />
-                <input type="hidden" name="data[page_id]" value="' . $paths->page_id . '" />
-                <input type="hidden" name="data[namespace]" value="' . $paths->namespace . '" />
-                <input type="submit" value="' . htmlspecialchars($lang->get('etc_wizard_next')) . '" />
-              </div>';
-        echo $formend;
-        break;
-      case 'success':
-        echo '<div class="info-box">
-                <b>' . $lang->get('acl_lbl_save_success_title') . '</b><br />
-                ' . $lang->get('acl_lbl_save_success_body', array( 'target_name' => $response['target_name'] )) . '<br />
-                ' . $formstart . '
-                <input type="hidden" name="data[mode]" value="seltarget" />
-                <input type="hidden" name="data[target_type]" value="' . $response['target_type'] . '" />
-                <input type="hidden" name="data[target_id_user]" value="' . ( ( intval($response['target_type']) == ACL_TYPE_USER ) ? $response['target_name'] : $response['target_id'] ) .'" />
-                <input type="hidden" name="data[target_id_grp]"  value="' . ( ( intval($response['target_type']) == ACL_TYPE_USER ) ? $response['target_name'] : $response['target_id'] ) .'" />
-                <input type="hidden" name="data[scope]" value="' . ( ( $response['page_id'] ) ? 'only_this' : 'entire_site' ) . '" />
-                <input type="hidden" name="data[page_id]" value="' . ( ( $response['page_id'] ) ? $response['page_id'] : 'false' ) . '" />
-                <input type="hidden" name="data[namespace]" value="' . ( ( $response['namespace'] ) ? $response['namespace'] : 'false' ) . '" />
-                <input type="submit" value="' . $lang->get('acl_btn_returnto_editor') . '" /> <input type="submit" name="data[act_go_stage1]" value="' . $lang->get('acl_btn_returnto_userscope') . '" />
-                ' . $formend . '
-              </div>';
-        break;
-      case 'delete':
-        echo '<div class="info-box">
-                <b>' . $lang->get('acl_lbl_delete_success_title') . '</b><br />
-                ' . $lang->get('acl_lbl_delete_success_body', array('target_name' => $response['target_name'])) . '<br />
-                ' . $formstart . '
-                <input type="hidden" name="data[mode]" value="seltarget" />
-                <input type="hidden" name="data[target_type]" value="' . $response['target_type'] . '" />
-                <input type="hidden" name="data[target_id_user]" value="' . ( ( intval($response['target_type']) == ACL_TYPE_USER ) ? $response['target_name'] : $response['target_id'] ) .'" />
-                <input type="hidden" name="data[target_id_grp]"  value="' . ( ( intval($response['target_type']) == ACL_TYPE_USER ) ? $response['target_name'] : $response['target_id'] ) .'" />
-                <input type="hidden" name="data[scope]" value="' . ( ( $response['page_id'] ) ? 'only_this' : 'entire_site' ) . '" />
-                <input type="hidden" name="data[page_id]" value="' . ( ( $response['page_id'] ) ? $response['page_id'] : 'false' ) . '" />
-                <input type="hidden" name="data[namespace]" value="' . ( ( $response['namespace'] ) ? $response['namespace'] : 'false' ) . '" />
-                <input type="submit" value="' . $lang->get('acl_btn_returnto_editor') . '" /> <input type="submit" name="data[act_go_stage1]" value="' . $lang->get('acl_btn_returnto_userscope') . '" />
-                ' . $formend . '
-              </div>';
-        break;
-      case 'seltarget':
-        if ( $response['type'] == 'edit' )
-        {
-          echo '<h3>' . $lang->get('acl_lbl_editwin_title_edit') . '</h3>';
-        }
-        else
-        {
-          echo '<h3>' . $lang->get('acl_lbl_editwin_title_create') . '</h3>';
-        }
-        $type  = ( $response['target_type'] == ACL_TYPE_GROUP ) ? $lang->get('acl_target_type_group') : $lang->get('acl_target_type_user');
-        $scope = ( $response['page_id'] ) ? ( $response['namespace'] == '__PageGroup' ? $lang->get('acl_scope_type_pagegroup') : $lang->get('acl_scope_type_thispage') ) : $lang->get('acl_scope_type_wholesite');
-        $subs = array(
-            'target_type' => $type,
-            'target' => $response['target_name'],
-            'scope_type' => $scope
-          );
-        echo $lang->get('acl_lbl_editwin_body', $subs);
-        echo $formstart;
-        $parser = $template->makeParserText( $response['template']['acl_field_begin'] );
-        echo $parser->run();
-        $parser = $template->makeParserText( $response['template']['acl_field_item'] );
-        $cls = 'row2';
-        foreach ( $response['acl_types'] as $acl_type => $value )
-        {
-          $vars = Array(
-              'FIELD_INHERIT_CHECKED' => '',
-              'FIELD_DENY_CHECKED' => '',
-              'FIELD_DISALLOW_CHECKED' => '',
-              'FIELD_WIKIMODE_CHECKED' => '',
-              'FIELD_ALLOW_CHECKED' => '',
-            );
-          $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-          $vars['ROW_CLASS'] = $cls;
-          
-          switch ( $response['current_perms'][$acl_type] )
-          {
-            case 'i':
-            default:
-              $vars['FIELD_INHERIT_CHECKED'] = 'checked="checked"';
-              break;
-            case AUTH_ALLOW:
-              $vars['FIELD_ALLOW_CHECKED'] = 'checked="checked"';
-              break;
-            case AUTH_WIKIMODE:
-              $vars['FIELD_WIKIMODE_CHECKED'] = 'checked="checked"';
-              break;
-            case AUTH_DISALLOW:
-              $vars['FIELD_DISALLOW_CHECKED'] = 'checked="checked"';
-              break;
-             case AUTH_DENY:
-              $vars['FIELD_DENY_CHECKED'] = 'checked="checked"';
-              break;
-          }
-          $vars['FIELD_NAME'] = 'data[perms][' . $acl_type . ']';
-          if ( preg_match('/^([a-z0-9_]+)$/', $response['acl_descs'][$acl_type]) )
-          {
-            $vars['FIELD_DESC'] = $lang->get($response['acl_descs'][$acl_type]);
-          }
-          else
-          {
-            $vars['FIELD_DESC'] = $response['acl_descs'][$acl_type];
-          }
-          $parser->assign_vars($vars);
-          echo $parser->run();
-        }
-        $parser = $template->makeParserText( $response['template']['acl_field_end'] );
-        echo $parser->run();
-        echo '<div style="margin: 10px auto 0 0; text-align: right;">
-                <input name="data[mode]" value="save_' . $response['type'] . '" type="hidden" />
-                <input type="hidden" name="data[page_id]" value="'   . (( $response['page_id']   ) ? $response['page_id']   : 'false') . '" />
-                <input type="hidden" name="data[namespace]" value="' . (( $response['namespace'] ) ? $response['namespace'] : 'false') . '" />
-                <input type="hidden" name="data[target_type]" value="' . $response['target_type'] . '" />
-                <input type="hidden" name="data[target_id]" value="' . $response['target_id'] . '" />
-                <input type="hidden" name="data[target_name]" value="' . $response['target_name'] . '" />
-                ' . ( ( $response['type'] == 'edit' ) ? '<input type="submit" value="' . $lang->get('etc_save_changes') . '" />&nbsp;&nbsp;<input type="submit" name="data[act_delete_rule]" value="' . $lang->get('acl_btn_deleterule') . '" style="color: #AA0000;" onclick="return confirm(\'' . addslashes($lang->get('acl_msg_deleterule_confirm')) . '\');" />' : '<input type="submit" value="' . $lang->get('acl_btn_createrule') . '" />' ) . '
-              </div>';
-        echo $formend;
-        break;
-      case 'error':
-        ob_end_clean();
-        die_friendly('Error occurred', '<p>Error returned by permissions API:</p><pre>' . htmlspecialchars($response['error']) . '</pre>');
-        break;
-    }
-    $ret = ob_get_contents();
-    ob_end_clean();
-    echo
-      $template->getHeader() .
-      $ret .
-      $template->getFooter();
-  }
-  
-  /**
-   * Preprocessor to turn the form-submitted data from the ACL editor into something the backend can handle
-   * @param array The posted data
-   * @return array
-   * @access private
-   */
-   
-  public static function acl_preprocess($parms)
-  {
-    if ( !isset($parms['mode']) )
-      // Nothing to do
-      return $parms;
-    switch ( $parms['mode'] )
-    {
-      case 'seltarget':
-        
-        // Who's affected?
-        $parms['target_type'] = intval( $parms['target_type'] );
-        $parms['target_id'] = ( $parms['target_type'] == ACL_TYPE_GROUP ) ? $parms['target_id_grp'] : $parms['target_id_user'];
-        
-      case 'save_edit':
-      case 'save_new':
-        if ( isset($parms['act_delete_rule']) )
-        {
-          $parms['mode'] = 'delete';
-        }
-        
-        // Scope (just this page or entire site?)
-        if ( $parms['scope'] == 'entire_site' || ( $parms['page_id'] == 'false' && $parms['namespace'] == 'false' ) )
-        {
-          $parms['page_id']   = false;
-          $parms['namespace'] = false;
-        }
-        else if ( $parms['scope'] == 'page_group' )
-        {
-          $parms['page_id'] = $parms['pg_id'];
-          $parms['namespace'] = '__PageGroup';
-        }
-        
-        break;
-    }
-    
-    if ( isset($parms['act_go_stage1']) )
-    {
-      $parms = array(
-          'mode' => 'listgroups'
-        );
-    }
-    
-    return $parms;
-  }
-  
-  public static function acl_postprocess($response)
-  {
-    if(!isset($response['mode']))
-    {
-      if ( isset($response['groups']) )
-        $response['mode'] = 'stage1';
-      else
-        $response = Array(
-            'mode' => 'error',
-            'error' => 'Invalid action passed by API backend.',
-          );
-    }
-    return $response;
-  }
-   
+	
+	/**
+ 	* Tell if a username is used or not.
+ 	* @param $name the name to check for
+ 	* @return string
+ 	*/
+	
+	public static function checkusername($name)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$name = str_replace('_', ' ', $name);
+		$q = $db->sql_query('SELECT username FROM ' . table_prefix.'users WHERE username=\'' . $db->escape(rawurldecode($name)) . '\'');
+		if ( !$q )
+		{
+			die($db->get_error());
+		}
+		if ( $db->numrows() < 1)
+		{
+			$db->free_result(); return('good');
+		}
+		else
+		{
+			$db->free_result(); return('bad');
+		}
+	}
+	
+	/**
+ 	* Get the wiki formatting source for a page
+ 	* @param $page the full page id (Namespace:Pagename)
+ 	* @return string
+ 	* @todo (DONE) Make it require a password (just for security purposes)
+ 	*/
+ 	
+	public static function getsource($page, $password = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( !isPage($page) )
+		{
+			return '';
+		}
+		
+		list($page_id, $namespace) = RenderMan::strToPageID($page);
+		$ns = namespace_factory($page_id, $namespace);
+		$cdata = $ns->get_cdata();
+		
+		if ( strlen($cdata['password']) == 40 )
+		{
+			if(!$password || ( $password != $cdata['password']))
+			{
+				return 'invalid_password';
+			}
+		}
+		
+		if(!$session->get_permissions('view_source')) // Dependencies handle this for us - this also checks for read privileges
+			return 'access_denied';
+		$pid = RenderMan::strToPageID($page);
+		if($pid[1] == 'Special' || $pid[1] == 'Admin')
+		{
+			die('This type of page (' . $paths->nslist[$pid[1]] . ') cannot be edited because the page source code is not stored in the database.');
+		}
+		
+		$e = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix.'page_text WHERE page_id=\'' . $pid[0] . '\' AND namespace=\'' . $pid[1] . '\'');
+		if ( !$e )
+		{
+			$db->_die('The page text could not be selected.');
+		}
+		if( $db->numrows() < 1 )
+		{
+			return ''; //$db->_die('There were no rows in the text table that matched the page text query.');
+		}
+		
+		$r = $db->fetchrow();
+		$db->free_result();
+		$message = $r['page_text'];
+		
+		return htmlspecialchars($message);
+	}
+	
+	/**
+ 	* DEPRECATED. Previously returned the full rendered contents of a page.
+ 	* @param $page the full page id (Namespace:Pagename)
+ 	* @param $send_headers true if the theme headers should be sent (still dependent on current page settings), false otherwise
+ 	* @return string
+ 	*/
+	
+	public static function getpage($page, $send_headers = false, $hist_id = false)
+	{
+		die('PageUtils->getpage is deprecated.');
+	}
+	
+	/**
+ 	* Writes page data to the database, after verifying permissions and running the XSS filter
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $message the text to save
+ 	* @return string
+ 	*/
+ 	
+	public static function savepage($page_id, $namespace, $message, $summary = 'No edit summary given', $minor = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$page = new PageProcessor($page_id, $namespace);
+		$cdata = $page->ns->get_cdata();
+		return $page->update_page($message, $summary, $minor, $cdata['page_format']);
+	}
+	
+	/**
+ 	* Creates a page, both in memory and in the database.
+ 	* @param string $page_id
+ 	* @param string $namespace
+ 	* @return bool true on success, false on failure
+ 	*/
+	
+	public static function createPage($page_id, $namespace, $name = false, $visible = 1)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if(in_array($namespace, Array('Special', 'Admin')))
+		{
+			// echo '<b>Notice:</b> PageUtils::createPage: You can\'t create a special page in the database<br />';
+			return 'You can\'t create a special page in the database';
+		}
+		
+		if(!isset($paths->nslist[$namespace]))
+		{
+			// echo '<b>Notice:</b> PageUtils::createPage: Couldn\'t look up the namespace<br />';
+			return 'Couldn\'t look up the namespace';
+		}
+		
+		$pname = $paths->nslist[$namespace] . $page_id;
+		if(isPage($pname))
+		{
+			// echo '<b>Notice:</b> PageUtils::createPage: Page already exists<br />';
+			return 'Page already exists';
+		}
+		
+		if(!$session->get_permissions('create_page'))
+		{
+			// echo '<b>Notice:</b> PageUtils::createPage: Not authorized to create pages<br />';
+			return 'Not authorized to create pages';
+		}
+		
+		if($session->user_level < USER_LEVEL_ADMIN && $namespace == 'System')
+		{
+			// echo '<b>Notice:</b> PageUtils::createPage: Not authorized to create system messages<br />';
+			return 'Not authorized to create system messages';
+		}
+		
+		if ( substr($page_id, 0, 8) == 'Project:' )
+		{
+			// echo '<b>Notice:</b> PageUtils::createPage: Prefix "Project:" is reserved<br />';
+			return 'The prefix "Project:" is reserved for a parser shortcut; if a page was created using this prefix, it would not be possible to link to it.';
+		}
+		
+		/*
+		// Dunno why this was here. Enano can handle more flexible names than this...
+		$regex = '#^([A-z0-9 _\-\.\/\!\@\(\)]*)$#is';
+		if(!preg_match($regex, $name))
+		{
+			//echo '<b>Notice:</b> PageUtils::createPage: Name contains invalid characters<br />';
+			return 'Name contains invalid characters';
+		}
+		*/
+		
+		$page_id = dirtify_page_id($page_id);
+		
+		if ( !$name )
+			$name = str_replace('_', ' ', $page_id);
+		
+		$page_id = sanitize_page_id( $page_id );
+		
+		$prot = ( $namespace == 'System' ) ? 1 : 0;
+		
+		$ips = array(
+			'ip' => array(),
+			'u' => array()
+			);
+		
+		$page_data = Array(
+			'name'=>$name,
+			'urlname'=>$page_id,
+			'namespace'=>$namespace,
+			'special'=>0,'visible'=>1,'comments_on'=>0,'protected'=>$prot,'delvotes'=>0,'delvote_ips'=>serialize($ips),'wiki_mode'=>2,
+		);
+		
+		// die('PageUtils::createpage: Creating page with this data:<pre>' . print_r($page_data, true) . '</pre>');
+		
+		$paths->add_page($page_data);
+		
+		$qa = $db->sql_query('INSERT INTO ' . table_prefix.'pages(name,urlname,namespace,visible,protected,delvote_ips) VALUES(\'' . $db->escape($name) . '\', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\', '. ( $visible ? '1' : '0' ) .', ' . $prot . ', \'' . $db->escape(serialize($ips)) . '\');');
+		$qb = $db->sql_query('INSERT INTO ' . table_prefix.'page_text(page_id,namespace) VALUES(\'' . $db->escape($page_id) . '\', \'' . $namespace . '\');');
+		$qc = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,author,author_uid,page_id,namespace) VALUES('.time().', \'DEPRECATED\', \'page\', \'create\', \'' . $session->username . '\', ' . $session->user_id . ', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\');');
+		
+		if($qa && $qb && $qc)
+			return 'good';
+		else
+		{
+			return $db->get_error();
+		}
+	}
+	
+	/**
+ 	* Sets the protection level on a page.
+ 	* @param $page_id string the page ID
+ 	* @param $namespace string the namespace
+ 	* @param $level int level of protection - 0 is off, 1 is full, 2 is semi
+ 	* @param $reason string why the page is being (un)protected
+ 	* @return string - "good" on success, in all other cases, an error string (on query failure, calls $db->_die() )
+ 	*/
+	public static function protect($page_id, $namespace, $level, $reason)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$page = new PageProcessor($page_id, $namespace);
+		return $page->protect_page($level, $reason);
+	}
+	
+	/**
+ 	* Generates an HTML table with history information in it.
+ 	* @param string the page ID
+ 	* @param string the namespace
+ 	* @param string page password
+ 	* @return string
+ 	*/
+	
+	public static function histlist($page_id, $namespace, $password = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if(!$session->get_permissions('history_view'))
+			return 'Access denied';
+		
+		ob_start();
+		
+		$pname = $paths->get_pathskey($page_id, $namespace);
+		$ns = namespace_factory($page_id, $namespace);
+		$cdata = $ns->get_cdata();
+		
+		if ( !isPage($pname) )
+		{
+			return 'DNE';
+		}
+		
+		if ( isPage($pname) )
+		{
+			$password_exists = ( !empty($cdata['password']) && $cdata['password'] !== sha1('') );
+			if ( $password_exists && $password !== $cdata['password'] )
+			{
+				return '<p>' . $lang->get('history_err_wrong_password') . '</p>';
+			}
+		}
+		
+		$wiki = ( ( $cdata['wiki_mode'] == 2 && getConfig('wiki_mode') == '1') || $cdata['wiki_mode'] == 1) ? true : false;
+		$prot = ( ( $cdata['protected'] == 2 && $session->user_logged_in && $session->reg_time + 60*60*24*4 < time() ) || $cdata['protected'] == 1) ? true : false;
+		
+		$q = 'SELECT log_id,time_id,date_string,page_id,namespace,author,author_uid,u.username,edit_summary,minor_edit FROM ' . table_prefix . "logs AS l\n"
+ 			. "  LEFT JOIN " . table_prefix . "users AS u\n"
+ 			. "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
+ 			. "  WHERE log_type='page' AND action='edit' AND page_id='$page_id' AND namespace='$namespace' AND is_draft != 1 ORDER BY time_id DESC;";
+		
+		if ( !($q = $db->sql_query($q)) )
+			$db->_die('The history data for the page "' . $paths->cpage['name'] . '" could not be selected.');
+		
+		echo $lang->get('history_page_subtitle') . '
+					<h3>' . $lang->get('history_heading_edits') . '</h3>';
+		$numrows = $db->numrows();
+		if ( $numrows < 1 )
+		{
+			echo $lang->get('history_no_entries');
+		}
+		else
+		{
+			echo '<form action="'.makeUrlNS($namespace, $page_id, 'do=diff').'" onsubmit="ajaxHistDiff(); return false;" method="get">
+						<input type="submit" value="' . $lang->get('history_btn_compare') . '" />
+						' . ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars($paths->nslist[$namespace] . $page_id) . '" />' : '' ) . '
+						' . ( $session->sid_super ? '<input type="hidden" name="auth"  value="' . $session->sid_super . '" />' : '') . '
+						<input type="hidden" name="do" value="diff" />
+						<br /><span>&nbsp;</span>
+						<div class="tblholder">
+						<table border="0" width="100%" cellspacing="1" cellpadding="4">
+						<tr>
+							<th colspan="2">' . $lang->get('history_col_diff') . '</th>
+							<th>' . $lang->get('history_col_datetime') . '</th>
+							<th>' . $lang->get('history_col_user') . '</th>
+							<th>' . $lang->get('history_col_summary') . '</th>
+							<th>' . $lang->get('history_col_minor') . '</th>
+							<th colspan="3">' . $lang->get('history_col_actions') . '</th>
+						</tr>'."\n"."\n";
+			$cls = 'row2';
+			$ticker = 0;
+			
+			while ( $r = $db->fetchrow($q) )
+			{
+				
+				$ticker++;
+				
+				if($cls == 'row2') $cls = 'row1';
+				else $cls = 'row2';
+				
+				echo '<tr>'."\n";
+				
+				// Diff selection
+				if($ticker == 1)
+				{
+					$s1 = '';
+					$s2 = 'checked="checked" ';
+				}
+				elseif($ticker == 2)
+				{
+					$s1 = 'checked="checked" ';
+					$s2 = '';
+				}
+				else
+				{
+					$s1 = '';
+					$s2 = '';
+				}
+				if($ticker > 1)        echo '<td class="' . $cls . '" style="padding: 0;"><input ' . $s1 . 'name="diff1" type="radio" value="' . $r['time_id'] . '" id="diff1_' . $r['time_id'] . '" class="clsDiff1Radio" onclick="selectDiff1Button(this);" /></td>'."\n"; else echo '<td class="' . $cls . '"></td>';
+				if($ticker < $numrows) echo '<td class="' . $cls . '" style="padding: 0;"><input ' . $s2 . 'name="diff2" type="radio" value="' . $r['time_id'] . '" id="diff2_' . $r['time_id'] . '" class="clsDiff2Radio" onclick="selectDiff2Button(this);" /></td>'."\n"; else echo '<td class="' . $cls . '"></td>';
+				
+				// Date and time
+				echo '<td class="' . $cls . '" style="white-space: nowrap;">' . enano_date(ED_DATE | ED_TIME, intval($r['time_id'])) . '</td class="' . $cls . '">'."\n";
+				
+				// User
+				$real_username = $r['author_uid'] > 1 && !empty($r['username']) ? $r['username'] : $r['author'];
+				$rank_info = $session->get_user_rank($r['author_uid']);
+				if ( $session->get_permissions('mod_misc') && is_valid_ip($r['author']) && $r['author_uid'] == 1 )
+				{
+					$rc = ' style="cursor: pointer;" title="' . $lang->get('history_tip_rdns') . '" onclick="ajaxReverseDNS(this, \'' . $r['author'] . '\');"';
+				}
+				else
+				{
+					$rc = '';
+				}
+				echo '<td class="' . $cls . '"' . $rc . '><a href="'.makeUrlNS('User', sanitize_page_id($real_username)).'" ';
+				if ( !isPage($paths->nslist['User'] . sanitize_page_id($real_username)) )
+				{
+					echo 'class="wikilink-nonexistent"';
+				}
+				echo 'style="' . $rank_info['rank_style'] . '">' . htmlspecialchars($real_username) . '</a></td class="' . $cls . '">'."\n";
+				
+				// Edit summary
+				if ( $r['edit_summary'] == 'Automatic backup created when logs were purged' )
+				{
+					$r['edit_summary'] = $lang->get('history_summary_clearlogs');
+				}
+				echo '<td class="' . $cls . '">' . $r['edit_summary'] . '</td>'."\n";
+				
+				// Minor edit
+				echo '<td class="' . $cls . '" style="text-align: center;">'. (( $r['minor_edit'] ) ? 'M' : '' ) .'</td>'."\n";
+				
+				// Actions!
+				echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'oldid=' . $r['log_id']) . '" onclick="ajaxHistView(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_view') . '</a></td>'."\n";
+				echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrl($paths->nslist['Special'].'Contributions/' . $r['author']) . '">' . $lang->get('history_action_contrib') . '</a></td>'."\n";
+				echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'do=edit&amp;revid=' . $r['log_id']) . '" onclick="ajaxEditor(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_restore') . '</a></td>'."\n";
+				
+				echo '</tr>'."\n"."\n";
+				
+			}
+			echo '</table>
+						</div>
+						<br />
+						<input type="hidden" name="do" value="diff" />
+						<input type="submit" value="' . $lang->get('history_btn_compare') . '" />
+						</form>
+						<script type="text/javascript">if ( !KILL_SWITCH ) { buildDiffList(); }</script>';
+		}
+		$db->free_result();
+		echo '<h3>' . $lang->get('history_heading_other') . '</h3>';
+		
+		$sql = 'SELECT log_id,action,time_id,date_string,page_id,namespace,author,author_uid,u.username,edit_summary,minor_edit FROM ' . table_prefix . "logs AS l\n"
+ 				. "  LEFT JOIN " . table_prefix . "users AS u\n"
+ 				. "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
+ 				. "  WHERE log_type='page' AND action!='edit' AND page_id='$page_id' AND namespace='$namespace' AND is_draft != 1 ORDER BY time_id DESC;";
+		
+		if ( !( $q = $db->sql_query($sql)) )
+		{
+			$db->_die('The history data for the page "' . htmlspecialchars($paths->cpage['name']) . '" could not be selected.');
+		}
+		if ( $db->numrows() < 1 )
+		{
+			echo $lang->get('history_no_entries');
+		}
+		else
+		{
+			
+			echo '<div class="tblholder">
+							<table border="0" width="100%" cellspacing="1" cellpadding="4"><tr>
+								<th>' . $lang->get('history_col_datetime') . '</th>
+								<th>' . $lang->get('history_col_user') . '</th>
+								<th>' . $lang->get('history_col_minor') . '</th>
+								<th>' . $lang->get('history_col_action_taken') . '</th>
+								<th>' . $lang->get('history_col_extra') . '</th>
+								<th colspan="2"></th>
+							</tr>';
+			$cls = 'row2';
+			while($r = $db->fetchrow($q)) {
+				
+				if($cls == 'row2') $cls = 'row1';
+				else $cls = 'row2';
+				
+				echo '<tr>';
+				
+				// Date and time
+				echo '<td class="' . $cls . '">' . enano_date(ED_DATE | ED_TIME, intval($r['time_id'])) . '</td class="' . $cls . '">';
+				
+				// User
+				$real_username = $r['author_uid'] > 1 && !empty($r['username']) ? $r['username'] : $r['author'];
+				$rank_info = $session->get_user_rank($r['author_uid']);
+				if ( $session->get_permissions('mod_misc') && is_valid_ip($r['author']) && $r['author_uid'] == 1 )
+				{
+					$rc = ' style="cursor: pointer;" title="' . $lang->get('history_tip_rdns') . '" onclick="ajaxReverseDNS(this, \'' . $r['author'] . '\');"';
+				}
+				else
+				{
+					$rc = '';
+				}
+				echo '<td class="' . $cls . '"' . $rc . '><a href="'.makeUrlNS('User', sanitize_page_id($real_username)).'" ';
+				if ( !isPage($paths->nslist['User'] . sanitize_page_id($real_username)) )
+				{
+					echo 'class="wikilink-nonexistent"';
+				}
+				echo 'style="' . $rank_info['rank_style'] . '">' . htmlspecialchars($real_username) . '</a></td class="' . $cls . '">'."\n";
+				
+				
+				// Minor edit
+				echo '<td class="' . $cls . '" style="text-align: center;">'. (( $r['minor_edit'] ) ? 'M' : '' ) .'</td>';
+				
+				// Action taken
+				echo '<td class="' . $cls . '">';
+				// Some of these are sanitized at insert-time. Others follow the newer Enano policy of stripping HTML at runtime.
+				if    ($r['action']=='prot')     echo $lang->get('history_log_protect') . '</td><td class="' . $cls . '">'     . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
+				elseif($r['action']=='unprot')   echo $lang->get('history_log_unprotect') . '</td><td class="' . $cls . '">'   . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
+				elseif($r['action']=='semiprot') echo $lang->get('history_log_semiprotect') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__REVERSION__' ? $lang->get('history_extra_protection_reversion') : htmlspecialchars($r['edit_summary']) );
+				elseif($r['action']=='rename')   echo $lang->get('history_log_rename') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_oldtitle') . ' '.htmlspecialchars($r['edit_summary']);
+				elseif($r['action']=='create')   echo $lang->get('history_log_create') . '</td><td class="' . $cls . '">';
+				elseif($r['action']=='delete')   echo $lang->get('history_log_delete') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . $r['edit_summary'];
+				elseif($r['action']=='reupload') echo $lang->get('history_log_uploadnew') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_reason') . ' ' . ( $r['edit_summary'] === '__ROLLBACK__' ? $lang->get('history_extra_upload_reversion') : htmlspecialchars($r['edit_summary']) );
+				elseif($r['action']=='votereset')echo $lang->get('history_log_votereset') . '</td><td class="' . $cls . '">' . $lang->get('history_extra_numvotes') . ' ' . $r['edit_summary'];
+				echo '</td>';
+				
+				// Actions!
+				echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrl($paths->nslist['Special'].'Contributions/' . $r['author']) . '">' . $lang->get('history_action_contrib') . '</a></td>';
+				echo '<td class="' . $cls . '" style="text-align: center;"><a rel="nofollow" href="'.makeUrlNS($namespace, $page_id, 'do=rollback&amp;id=' . $r['log_id']) . '" onclick="ajaxRollback(\'' . $r['log_id'] . '\'); return false;">' . $lang->get('history_action_revert') . '</a></td>';
+				
+				echo '</tr>';
+			}
+			echo '</table></div>';
+		}
+		$db->free_result();
+		$ret = ob_get_contents();
+		ob_end_clean();
+		return $ret;
+	}
+	
+	/**
+ 	* Rolls back a logged action
+ 	* @param $id the time ID, a.k.a. the primary key in the logs table
+ 	* @return string
+ 	*/
+ 	
+	public static function rollback($id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		// placeholder
+		return 'PageUtils->rollback() is deprecated - use PageProcessor instead.';
+	}
+	
+	/**
+ 	* Posts a comment.
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $name the name of the person posting, defaults to current username/IP
+ 	* @param $subject the subject line of the comment
+ 	* @param $text the comment text
+ 	* @return string javascript code
+ 	*/
+ 	
+	public static function addcomment($page_id, $namespace, $name, $subject, $text, $captcha_code = false, $captcha_id = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$_ob = '';
+		if(!$session->get_permissions('post_comments'))
+			return 'Access denied';
+		if(getConfig('comments_need_login') == '2' && !$session->user_logged_in) _die('Access denied to post comments: you need to be logged in first.');
+		if(getConfig('comments_need_login') == '1' && !$session->user_logged_in)
+		{
+			if(!$captcha_code || !$captcha_id) _die('BUG: PageUtils::addcomment: no CAPTCHA data passed to method');
+			$result = $session->get_captcha($captcha_id);
+			if(strtolower($captcha_code) != strtolower($result)) _die('The confirmation code you entered was incorrect.');
+		}
+		$text = RenderMan::preprocess_text($text);
+		$name = $session->user_logged_in ? RenderMan::preprocess_text($session->username) : RenderMan::preprocess_text($name);
+		$subj = RenderMan::preprocess_text($subject);
+		if(getConfig('approve_comments', '0')=='1') $appr = '0'; else $appr = '1';
+		$q = 'INSERT INTO ' . table_prefix.'comments(page_id,namespace,subject,comment_data,name,user_id,approved,time) VALUES(\'' . $page_id . '\',\'' . $namespace . '\',\'' . $subj . '\',\'' . $text . '\',\'' . $name . '\',' . $session->user_id . ',' . $appr . ','.time().')';
+		$e = $db->sql_query($q);
+		if(!$e) die('alert(unescape(\''.rawurlencode('Error inserting comment data: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'))');
+		else $_ob .= '<div class="info-box">Your comment has been posted.</div>';
+		return PageUtils::comments($page_id, $namespace, false, Array(), $_ob);
+	}
+	
+	/**
+ 	* Generates partly-compiled HTML/Javascript code to be eval'ed by the user's browser to display comments
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $action administrative action to perform, default is false
+ 	* @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc.
+ 	* @param $_ob text to prepend to output, used by PageUtils::addcomment
+ 	* @return array
+ 	* @access private
+ 	*/
+ 	
+	public static function comments_raw($page_id, $namespace, $action = false, $flags = Array(), $_ob = '')
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$pname = $paths->nslist[$namespace] . $page_id;
+		$template->init_vars();
+		
+		ob_start();
+		
+		if($action && $session->get_permissions('mod_comments')) // Nip hacking attempts in the bud
+		{
+			switch($action) {
+			case "delete":
+				if(isset($flags['id']))
+				{
+					$q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND comment_id='.intval($flags['id']).' LIMIT 1;';
+				} else {
+					$n = $db->escape($flags['name']);
+					$s = $db->escape($flags['subj']);
+					$t = $db->escape($flags['text']);
+					$q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND name=\'' . $n . '\' AND subject=\'' . $s . '\' AND comment_data=\'' . $t . '\' LIMIT 1;';
+				}
+				$e=$db->sql_query($q);
+				if(!$e) die('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
+				break;
+			case "approve":
+				if(isset($flags['id']))
+				{
+					$where = 'comment_id='.intval($flags['id']);
+				} else {
+					$n = $db->escape($flags['name']);
+					$s = $db->escape($flags['subj']);
+					$t = $db->escape($flags['text']);
+					$where = 'name=\'' . $n . '\' AND subject=\'' . $s . '\' AND comment_data=\'' . $t . '\'';
+				}
+				$q = 'SELECT approved FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND ' . $where . ' LIMIT 1;';
+				$e = $db->sql_query($q);
+				if(!$e) die('alert(unesape(\''.rawurlencode('Error selecting approval status: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
+				$r = $db->fetchrow();
+				$db->free_result();
+				$a = ( $r['approved'] ) ? '0' : '1';
+				$q = 'UPDATE ' . table_prefix.'comments SET approved=' . $a . ' WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND ' . $where . ';';
+				$e=$db->sql_query($q);
+				if(!$e) die('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
+				if($a=='1') $v = $lang->get('comment_btn_mod_unapprove');
+				else $v = $lang->get('comment_btn_mod_approve');
+				echo 'document.getElementById("mdgApproveLink'.intval($_GET['id']).'").innerHTML="' . $v . '";';
+				break;
+			}
+		}
+		
+		if(!defined('ENANO_TEMPLATE_LOADED'))
+		{
+			$template->load_theme($session->theme, $session->style);
+		}
+		
+		$tpl = $template->makeParser('comment.tpl');
+		
+		$e = $db->sql_query('SELECT * FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND approved=0;');
+		if(!$e) $db->_die('The comment text data could not be selected.');
+		$num_unapp = $db->numrows();
+		$db->free_result();
+		$e = $db->sql_query('SELECT * FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND approved=1;');
+		if(!$e) $db->_die('The comment text data could not be selected.');
+		$num_app = $db->numrows();
+		$db->free_result();
+		$lq = $db->sql_query('SELECT c.comment_id,c.subject,c.name,c.comment_data,c.approved,c.time,c.user_id,c.ip_address,u.user_level,u.email,u.signature,u.user_has_avatar,u.avatar_type
+									FROM ' . table_prefix.'comments AS c
+									LEFT JOIN ' . table_prefix.'users AS u
+										ON c.user_id=u.user_id
+									WHERE page_id=\'' . $page_id . '\'
+									AND namespace=\'' . $namespace . '\' ORDER BY c.time ASC;');
+		if(!$lq) _die('The comment text data could not be selected. '.$db->get_error());
+		$_ob .= '<h3>' . $lang->get('comment_heading') . '</h3>';
+		
+		$n = ( $session->get_permissions('mod_comments')) ? $db->numrows() : $num_app;
+		
+		$subst = array(
+				'num_comments' => $n,
+				'page_type' => $template->namespace_string
+			);
+		
+		$_ob .= '<p>';
+		$_ob .= ( $n == 0 ) ? $lang->get('comment_msg_count_zero', $subst) : ( $n == 1 ? $lang->get('comment_msg_count_one', $subst) : $lang->get('comment_msg_count_plural', $subst) );
+		
+		if ( $session->get_permissions('mod_comments') && $num_unapp > 0 )
+		{
+			$_ob .= ' <span style="color: #D84308">' . $lang->get('comment_msg_count_unapp_mod', array( 'num_unapp' => $num_unapp )) . '</span>';
+		}
+		else if ( !$session->get_permissions('mod_comments') && $num_unapp > 0 )
+		{
+			$ls = ( $num_unapp == 1 ) ? 'comment_msg_count_unapp_one' : 'comment_msg_count_unapp_plural';
+			$_ob .= ' <span>' . $lang->get($ls, array( 'num_unapp' => $num_unapp )) . '</span>';
+		}
+		$_ob .= '</p>';
+		$list = 'list = { ';
+		// _die(htmlspecialchars($ttext));
+		$i = -1;
+		while ( $row = $db->fetchrow($lq) )
+		{
+			$i++;
+			$strings = Array();
+			$bool = Array();
+			if ( $session->get_permissions('mod_comments') || $row['approved'] == COMMENT_APPROVED )
+			{
+				$list .= $i . ' : { \'comment\' : unescape(\''.rawurlencode($row['comment_data']).'\'), \'name\' : unescape(\''.rawurlencode($row['name']).'\'), \'subject\' : unescape(\''.rawurlencode($row['subject']).'\'), }, ';
+				
+				// Comment ID (used in the Javascript apps)
+				$strings['ID'] = (string)$i;
+				
+				// Determine the name, and whether to link to the user page or not
+				$name = '';
+				if($row['user_id'] > 1) $name .= '<a href="'.makeUrlNS('User', sanitize_page_id(' ', '_', $row['name'])).'">';
+				$name .= $row['name'];
+				if($row['user_id'] > 1) $name .= '</a>';
+				$strings['NAME'] = $name; unset($name);
+				
+				// Subject
+				$s = $row['subject'];
+				if(!$row['approved']) $s .= ' <span style="color: #D84308">' . $lang->get('comment_msg_note_unapp') . '</span>';
+				$strings['SUBJECT'] = $s;
+				
+				// Date and time
+				$strings['DATETIME'] = enano_date(ED_DATE | ED_TIME, $row['time']);
+				
+				// User level
+				switch($row['user_level'])
+				{
+					default:
+					case USER_LEVEL_GUEST:
+						$l = $lang->get('user_type_guest');
+						break;
+					case USER_LEVEL_MEMBER:
+					case USER_LEVEL_CHPREF:
+						$l = $lang->get('user_type_member');
+						break;
+					case USER_LEVEL_MOD:
+						$l = $lang->get('user_type_mod');
+						break;
+					case USER_LEVEL_ADMIN:
+						$l = $lang->get('user_type_admin');
+						break;
+				}
+				$strings['USER_LEVEL'] = $l; unset($l);
+				
+				// The actual comment data
+				$strings['DATA'] = RenderMan::render($row['comment_data']);
+				
+				if($session->get_permissions('edit_comments'))
+				{
+					// Edit link
+					$strings['EDIT_LINK'] = '<a href="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=editcomment&amp;id=' . $row['comment_id']) . '" id="editbtn_' . $i . '">' . $lang->get('comment_btn_edit') . '</a>';
+				
+					// Delete link
+					$strings['DELETE_LINK'] = '<a href="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=deletecomment&amp;id=' . $row['comment_id']) . '">' . $lang->get('comment_btn_delete') . '</a>';
+				}
+				else
+				{
+					// Edit link
+					$strings['EDIT_LINK'] = '';
+				
+					// Delete link
+					$strings['DELETE_LINK'] = '';
+				}
+				
+				// Send PM link
+				$strings['SEND_PM_LINK'] = ( $session->user_logged_in && $row['user_id'] > 1 ) ? '<a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/To/' . $row['name']) . '">' . $lang->get('comment_btn_send_privmsg') . '</a><br />' : '';
+				
+				// Add Buddy link
+				$strings['ADD_BUDDY_LINK'] = ( $session->user_logged_in && $row['user_id'] > 1 ) ? '<a href="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Add/' . $row['name']) . '">' . $lang->get('comment_btn_add_buddy') . '</a>' : '';
+				
+				// Mod links
+				$applink = '';
+				$applink .= '<a href="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=admin&amp;action=approve&amp;id=' . $row['comment_id']) . '" id="mdgApproveLink' . $i . '">';
+				if($row['approved']) $applink .= $lang->get('comment_btn_mod_unapprove');
+				else $applink .= $lang->get('comment_btn_mod_approve');
+				$applink .= '</a>';
+				$strings['MOD_APPROVE_LINK'] = $applink; unset($applink);
+				$strings['MOD_DELETE_LINK'] = '<a href="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=admin&amp;action=delete&amp;id=' . $row['comment_id']) . '">' . $lang->get('comment_btn_mod_delete') . '</a>';
+				$strings['MOD_IP_LINK'] = '<span style="opacity: 0.5; filter: alpha(opacity=50);">' . ( ( empty($row['ip_address']) ) ? $lang->get('comment_btn_mod_ip_missing') : $lang->get('comment_btn_mod_ip_notimplemented') ) . '</span>';
+				
+				// Signature
+				$strings['SIGNATURE'] = '';
+				if($row['signature'] != '') $strings['SIGNATURE'] = RenderMan::render($row['signature']);
+				
+				// Avatar
+				if ( $row['user_has_avatar'] == 1 )
+				{
+					$bool['user_has_avatar'] = true;
+					$strings['AVATAR_ALT'] = $lang->get('usercp_avatar_image_alt', array('username' => $row['name']));
+					$strings['AVATAR_URL'] = make_avatar_url(intval($row['user_id']), $row['avatar_type'], $row['email']);
+					$strings['USERPAGE_LINK'] = makeUrlNS('User', $row['name']);
+				}
+				
+				$bool['auth_mod'] = ($session->get_permissions('mod_comments')) ? true : false;
+				$bool['can_edit'] = ( ( $session->user_logged_in && $row['name'] == $session->username && $session->get_permissions('edit_comments') ) || $session->get_permissions('mod_comments') ) ? true : false;
+				$bool['signature'] = ( $strings['SIGNATURE'] == '' ) ? false : true;
+				
+				// Done processing and compiling, now let's cook it into HTML
+				$tpl->assign_vars($strings);
+				$tpl->assign_bool($bool);
+				$_ob .= $tpl->run();
+			}
+		}
+		if(getConfig('comments_need_login') != '2' || $session->user_logged_in)
+		{
+			if($session->get_permissions('post_comments'))
+			{
+				$_ob .= '<h3>' . $lang->get('comment_postform_title') . '</h3>';
+				$_ob .= $lang->get('comment_postform_blurb');
+				if(getConfig('approve_comments', '0')=='1') $_ob .= ' ' . $lang->get('comment_postform_blurb_unapp');
+				if(getConfig('comments_need_login') == '1' && !$session->user_logged_in)
+				{
+					$_ob .= ' ' . $lang->get('comment_postform_blurb_captcha');
+				}
+				$sn = $session->user_logged_in ? $session->username . '<input name="name" id="mdgScreenName" type="hidden" value="' . $session->username . '" />' : '<input name="name" id="mdgScreenName" type="text" size="35" />';
+				$_ob .= '  <a href="#" id="mdgCommentFormLink" style="display: none;" onclick="document.getElementById(\'mdgCommentForm\').style.display=\'block\';this.style.display=\'none\';return false;">' . $lang->get('comment_postform_blurb_link') . '</a>
+				<div id="mdgCommentForm">
+				<form action="'.makeUrlNS($namespace, $page_id, 'do=comments&amp;sub=postcomment').'" method="post" style="margin-left: 1em">
+				<table border="0">
+				<tr><td>' . $lang->get('comment_postform_field_name') . '</td><td>' . $sn . '</td></tr>
+				<tr><td>' . $lang->get('comment_postform_field_subject') . '</td><td><input name="subj" id="mdgSubject" type="text" size="35" /></td></tr>';
+				if(getConfig('comments_need_login') == '1' && !$session->user_logged_in)
+				{
+					$session->kill_captcha();
+					$captcha = $session->make_captcha();
+					$_ob .= '<tr><td>' . $lang->get('comment_postform_field_captcha_title') . '<br /><small>' . $lang->get('comment_postform_field_captcha_blurb') . '</small></td><td><img src="'.makeUrlNS('Special', 'Captcha/' . $captcha) . '" alt="Visual confirmation" style="cursor: pointer;" onclick="this.src = \''.makeUrlNS("Special", "Captcha/".$captcha).'/\'+Math.floor(Math.random() * 100000);" /><input name="captcha_id" id="mdgCaptchaID" type="hidden" value="' . $captcha . '" /><br />' . $lang->get('comment_postform_field_captcha_label') . ' <input name="captcha_input" id="mdgCaptchaInput" type="text" size="10" /><br /><small><script type="text/javascript">document.write("' . $lang->get('comment_postform_field_captcha_cantread_js') . '");</script><noscript>' . $lang->get('comment_postform_field_captcha_cantread_nojs') . '</noscript></small></td></tr>';
+				}
+				$_ob .= '
+				<tr><td valign="top">' . $lang->get('comment_postform_field_comment') . '</td><td><textarea name="text" id="mdgCommentArea" rows="10" cols="40"></textarea></td></tr>
+				<tr><td colspan="2" style="text-align: center;"><input type="submit" value="' . $lang->get('comment_postform_btn_submit') . '" /></td></tr>
+				</table>
+				</form>
+				</div>';
+			}
+		} else {
+			// FIXME: l10n
+			$_ob .= '<h3>' . $lang->get('comment_postform_title') . '</h3><p>You need to be logged in to post comments. <a href="'.makeUrlNS('Special', 'Login/' . $pname . '%2523comments').'">Log in</a></p>';
+		}
+		$list .= '};';
+		echo 'document.getElementById(\'ajaxEditContainer\').innerHTML = unescape(\''. rawurlencode($_ob) .'\');
+		' . $list;
+		echo 'Fat.fade_all(); document.getElementById(\'mdgCommentForm\').style.display = \'none\'; document.getElementById(\'mdgCommentFormLink\').style.display="inline";';
+		
+		$ret = ob_get_contents();
+		ob_end_clean();
+		return Array($ret, $_ob);
+		
+	}
+	
+	/**
+ 	* Generates ready-to-execute Javascript code to be eval'ed by the user's browser to display comments
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $action administrative action to perform, default is false
+ 	* @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc.
+ 	* @param $_ob text to prepend to output, used by PageUtils::addcomment
+ 	* @return string
+ 	*/
+ 	
+	public static function comments($page_id, $namespace, $action = false, $id = -1, $_ob = '')
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob);
+		return $r[0];
+	}
+	
+	/**
+ 	* Generates HTML code for comments - used in browser compatibility mode
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $action administrative action to perform, default is false
+ 	* @param $flags additional info for $action, shouldn't be used except when deleting/approving comments, etc.
+ 	* @param $_ob text to prepend to output, used by PageUtils::addcomment
+ 	* @return string
+ 	*/
+	
+	public static function comments_html($page_id, $namespace, $action = false, $id = -1, $_ob = '')
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$r = PageUtils::comments_raw($page_id, $namespace, $action, $id, $_ob);
+		return $r[1];
+	}
+	
+	/**
+ 	* Updates comment data.
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $subject new subject
+ 	* @param $text new text
+ 	* @param $old_subject the old subject, unprocessed and identical to the value in the DB
+ 	* @param $old_text the old text, unprocessed and identical to the value in the DB
+ 	* @param $id the javascript list ID, used internally by the client-side app
+ 	* @return string
+ 	*/
+	
+	public static function savecomment($page_id, $namespace, $subject, $text, $old_subject, $old_text, $id = -1)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if(!$session->get_permissions('edit_comments'))
+			return 'result="BAD";error="Access denied"';
+		// Avoid SQL injection
+		$old_text    = $db->escape($old_text);
+		$old_subject = $db->escape($old_subject);
+		// Safety check - username/login
+		if(!$session->get_permissions('mod_comments')) // allow mods to edit comments
+		{
+			if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.<br /><br />Please log in and try again.');
+			$q = 'SELECT c.name FROM ' . table_prefix.'comments c, ' . table_prefix.'users u WHERE comment_data=\'' . $old_text . '\' AND subject=\'' . $old_subject . '\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND u.user_id=c.user_id;';
+			$s = $db->sql_query($q);
+			if(!$s) _die('SQL error during safety check: '.$db->get_error().'<br /><br />Attempted SQL:<br /><pre>'.htmlspecialchars($q).'</pre>');
+			$r = $db->fetchrow($s);
+			$db->free_result();
+			if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.');
+		}
+		$s = RenderMan::preprocess_text($subject);
+		$t = RenderMan::preprocess_text($text);
+		$sql  = 'UPDATE ' . table_prefix.'comments SET subject=\'' . $s . '\',comment_data=\'' . $t . '\' WHERE comment_data=\'' . $old_text . '\' AND subject=\'' . $old_subject . '\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'';
+		$result = $db->sql_query($sql);
+		if($result)
+		{
+			return 'result="GOOD";
+											list[' . $id . '][\'subject\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $s))))).'\');
+											list[' . $id . '][\'comment\'] = unescape(\''.str_replace('%5Cn', '%0A', rawurlencode(str_replace('{{EnAnO:Newline}}', '\\n', stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t))))).'\'); id = ' . $id . ';
+			s = unescape(\''.rawurlencode($s).'\');
+			t = unescape(\''.str_replace('%5Cn', '<br \\/>', rawurlencode(RenderMan::render(str_replace('{{EnAnO:Newline}}', "\n", stripslashes(str_replace('\\n', '{{EnAnO:Newline}}', $t)))))).'\');';
+		}
+		else
+		{
+			return 'result="BAD"; error=unescape("'.rawurlencode('Enano encountered a problem whilst saving the comment.
+			Performed SQL:
+			' . $sql . '
+		
+			Error returned by MySQL: '.$db->get_error()).'");';
+		}
+	}
+	
+	/**
+ 	* Updates comment data using the comment_id column instead of the old, messy way
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $subject new subject
+ 	* @param $text new text
+ 	* @param $id the comment ID (primary key in enano_comments table)
+ 	* @return string
+ 	*/
+	
+	public static function savecomment_neater($page_id, $namespace, $subject, $text, $id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if(!is_int($id)) die('PageUtils::savecomment: $id is not an integer, aborting for safety');
+		if(!$session->get_permissions('edit_comments'))
+			return 'Access denied';
+		// Safety check - username/login
+		if(!$session->get_permissions('mod_comments')) // allow mods to edit comments
+		{
+			if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.<br /><br />Please log in and try again.');
+			$q = 'SELECT c.name FROM ' . table_prefix.'comments c, ' . table_prefix.'users u WHERE comment_id=' . $id . ' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND u.user_id=c.user_id;';
+			$s = $db->sql_query($q);
+			if(!$s) _die('SQL error during safety check: '.$db->get_error().'<br /><br />Attempted SQL:<br /><pre>'.htmlspecialchars($q).'</pre>');
+			$r = $db->fetchrow($s);
+			if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.');
+			$db->free_result();
+		}
+		$s = RenderMan::preprocess_text($subject);
+		$t = RenderMan::preprocess_text($text);
+		$sql  = 'UPDATE ' . table_prefix.'comments SET subject=\'' . $s . '\',comment_data=\'' . $t . '\' WHERE comment_id=' . $id . ' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'';
+		$result = $db->sql_query($sql);
+		if($result)
+		return 'good';
+		else return 'Enano encountered a problem whilst saving the comment.
+		Performed SQL:
+		' . $sql . '
+		
+		Error returned by MySQL: '.$db->get_error();
+	}
+	
+	/**
+ 	* Deletes a comment.
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $name the name the user posted under
+ 	* @param $subj the subject of the comment to be deleted
+ 	* @param $text the text of the comment to be deleted
+ 	* @param $id the javascript list ID, used internally by the client-side app
+ 	* @return string
+ 	*/
+	
+	public static function deletecomment($page_id, $namespace, $name, $subj, $text, $id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if(!$session->get_permissions('edit_comments'))
+			return 'alert("Access to delete/edit comments is denied");';
+		
+		if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.');
+		$n = $db->escape($name);
+		$s = $db->escape($subj);
+		$t = $db->escape($text);
+		
+		// Safety check - username/login
+		if(!$session->get_permissions('mod_comments')) // allows mods to delete comments
+		{
+			if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.<br /><br />Please log in and try again.');
+			$q = 'SELECT c.name FROM ' . table_prefix.'comments c, ' . table_prefix.'users u WHERE comment_data=\'' . $t . '\' AND subject=\'' . $s . '\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND u.user_id=c.user_id;';
+			$s = $db->sql_query($q);
+			if(!$s) _die('SQL error during safety check: '.$db->get_error().'<br /><br />Attempted SQL:<br /><pre>'.htmlspecialchars($q).'</pre>');
+			$r = $db->fetchrow($s);
+			if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.');
+			$db->free_result();
+		}
+		$q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND name=\'' . $n . '\' AND subject=\'' . $s . '\' AND comment_data=\'' . $t . '\' LIMIT 1;';
+		$e=$db->sql_query($q);
+		if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
+		return('good');
+	}
+	
+	/**
+ 	* Deletes a comment in a cleaner fashion.
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $id the comment ID (primary key)
+ 	* @return string
+ 	*/
+	
+	public static function deletecomment_neater($page_id, $namespace, $id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if(!preg_match('#^([0-9]+)$#', (string)$id)) die('$_GET[id] is improperly formed.');
+		
+		if(!$session->get_permissions('edit_comments'))
+			return 'alert("Access to delete/edit comments is denied");';
+		
+		// Safety check - username/login
+		if(!$session->get_permissions('mod_comments')) // allows mods to delete comments
+		{
+			if(!$session->user_logged_in) _die('AJAX comment save safety check failed because you are not logged in. Sometimes this can happen because you are using a browser that does not send cookies as part of AJAX requests.<br /><br />Please log in and try again.');
+			$q = 'SELECT c.name FROM ' . table_prefix.'comments c, ' . table_prefix.'users u WHERE comment_id=' . $id . ' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND u.user_id=c.user_id;';
+			$s = $db->sql_query($q);
+			if(!$s) _die('SQL error during safety check: '.$db->get_error().'<br /><br />Attempted SQL:<br /><pre>'.htmlspecialchars($q).'</pre>');
+			$r = $db->fetchrow($s);
+			if($db->numrows() < 1 || $r['name'] != $session->username) _die('Safety check failed, probably due to a hacking attempt.');
+			$db->free_result();
+		}
+		$q = 'DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\' AND comment_id=' . $id . ' LIMIT 1;';
+		$e=$db->sql_query($q);
+		if(!$e) return('alert(unesape(\''.rawurlencode('Error during query: '.$db->get_error().'\n\nQuery:\n' . $q) . '\'));');
+		return('good');
+	}
+	
+	/**
+ 	* Renames a page.
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $name the new name for the page
+ 	* @return string error string or success message
+ 	*/
+ 	
+	public static function rename($page_id, $namespace, $name)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$page = new PageProcessor($page_id, $namespace);
+		return $page->rename_page($name);
+	}
+	
+	/**
+ 	* Flushes (clears) the action logs for a given page
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @return string error/success string
+ 	*/
+ 	
+	public static function flushlogs($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		if ( !is_object($lang) && defined('IN_ENANO_INSTALL') )
+		{
+			// This is a special exception for the Enano installer, which doesn't init languages yet.
+			$lang = new Language('eng');
+		}
+		if(!$session->get_permissions('clear_logs') && !defined('IN_ENANO_INSTALL'))
+		{
+			return $lang->get('etc_access_denied');
+		}
+		if ( !$session->sid_super )
+		{
+			return $lang->get('etc_access_denied_need_reauth');
+		}
+		
+		$page_id_db = $db->escape($page_id);
+		$namespace_db = $db->escape($namespace);
+		
+		// If we're flushing a file, also clear all revisions before the current
+		if ( $namespace == 'File' )
+		{
+			$q = $db->sql_query('SELECT file_id FROM ' . table_prefix . "files WHERE page_id='$page_id_db' ORDER BY time_id DESC;");
+			if ( !$q )
+				$db->_die();
+			// discard first row (current revision)
+			$db->fetchrow();
+			$id_list = array();
+			while ( $row = $db->fetchrow() )
+				$id_list[] = $row['file_id'];
+			
+			require_once(ENANO_ROOT . '/includes/namespaces/file.php');
+			
+			// clear out each file
+			foreach ( $id_list as $id )
+				Namespace_File::delete_file($id);
+		}
+		
+		$q = $db->sql_query('DELETE FROM ' . table_prefix . "logs WHERE page_id='$page_id_db' AND namespace='$namespace';");
+		if ( !$q )
+			$db->_die('The log entries could not be deleted.');
+		
+		// If the page exists, make a backup of it in case it gets spammed/vandalized
+		// If not, the admin's probably deleting a trash page
+		if ( isPage($paths->get_pathskey($page_id, $namespace)) )
+		{
+			$q = $db->sql_query('SELECT page_text,char_tag FROM ' . table_prefix . "page_text WHERE page_id='$page_id_db' AND namespace='$namespace_db';");
+			if ( !$q )
+				$db->_die('The current page text could not be selected; as a result, creating the backup of the page failed. Please make a backup copy of the page by clicking Edit this page and then clicking Save Changes.');
+			$row = $db->fetchrow();
+			$db->free_result();
+			$minor_edit = ( ENANO_DBLAYER == 'MYSQL' ) ? 'false' : '0';
+			$username = $db->escape($session->username);
+			$q = 'INSERT INTO ' . table_prefix . "logs ( log_type, action, time_id, date_string, page_id, namespace, page_text, char_tag, author, author_uid, edit_summary, minor_edit ) VALUES\n"
+ 				. "  ('page', 'edit', " . time() . ", 'DEPRECATED', '$page_id', '$namespace', '" . $db->escape($row['page_text']) . "', '', '{$username}', $session->user_id, '" . $lang->get('page_flushlogs_backup_summary') . "', $minor_edit);";
+			if ( !$db->sql_query($q) )
+				$db->_die('The history (log) entry could not be inserted into the logs table.');
+		}
+		
+		return $lang->get('ajax_clearlogs_success');
+	}
+	
+	/**
+ 	* Deletes a page.
+ 	* @param string $page_id the condemned page ID
+ 	* @param string $namespace the condemned namespace
+ 	* @param string The reason for deleting the page in question
+ 	* @return string
+ 	*/
+ 	
+	public static function deletepage($page_id, $namespace, $reason)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $cache;
+		$perms = $session->fetch_page_acl($page_id, $namespace);
+		$x = trim($reason);
+		if ( empty($x) )
+		{
+			return $lang->get('ajax_delete_need_reason');
+		}
+		if(!$perms->get_permissions('delete_page')) return('Administrative privileges are required to delete pages, you loser.');
+		
+		if ( !$session->sid_super )
+		{
+			return $lang->get('etc_access_denied_need_reauth');
+		}
+		
+		$e = $db->sql_query('INSERT INTO ' . table_prefix.'logs(time_id,date_string,log_type,action,page_id,namespace,author,author_uid,edit_summary) VALUES('.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \'page\', \'delete\', \'' . $page_id . '\', \'' . $namespace . '\', \'' . $session->username . '\', ' . $session->user_id . ', \'' . $db->escape(htmlspecialchars($reason)) . '\')');
+		if(!$e) $db->_die('The page log entry could not be inserted.');
+		$e = $db->sql_query('DELETE FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'');
+		if(!$e) $db->_die('The page categorization entries could not be deleted.');
+		$e = $db->sql_query('DELETE FROM ' . table_prefix.'comments WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'');
+		if(!$e) $db->_die('The page comments could not be deleted.');
+		$e = $db->sql_query('DELETE FROM ' . table_prefix.'page_text WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'');
+		if(!$e) $db->_die('The page text entry could not be deleted.');
+		$e = $db->sql_query('DELETE FROM ' . table_prefix.'pages WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'');
+		if(!$e) $db->_die('The page entry could not be deleted.');
+		if ( $namespace == 'File' )
+		{
+			$e = $db->sql_query('DELETE FROM ' . table_prefix.'files WHERE page_id=\'' . $page_id . '\'');
+			if(!$e) $db->_die('The file entry could not be deleted.');
+		}
+		$cache->purge('page_meta');
+		return $lang->get('ajax_delete_success');
+	}
+	
+	/**
+ 	* Deletes files associated with a File page.
+ 	* @param string Page ID
+ 	*/
+	
+	public static function delete_page_files($page_id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$q = $db->sql_query('SELECT file_id, filename, file_key, time_id, file_extension FROM ' . table_prefix . "files WHERE page_id = '{$db->escape($page_id)}';");
+		if ( !$q )
+			$db->_die();
+		
+		while ( $row = $db->fetchrow() )
+		{
+			// wipe original file
+			foreach ( array(
+					ENANO_ROOT . "/files/{$row['file_key']}_{$row['time_id']}{$row['file_extension']}",
+					ENANO_ROOT . "/files/{$row['file_key']}{$row['file_extension']}"
+				) as $orig_file )
+			{
+				if ( file_exists($orig_file) )
+					@unlink($orig_file);
+			}
+			
+			// wipe cached files
+			if ( $dr = @opendir(ENANO_ROOT . '/cache/') )
+			{
+				// lol404.jpg-1217958283-200x320.jpg
+				while ( $dh = @readdir($dr) )
+				{
+					$regexp = ':^' . preg_quote("{$row['filename']}-{$row['time_id']}-") . '[0-9]+x[0-9]+\.' . ltrim($row['file_extension'], '.') . '$:';
+					if ( preg_match($regexp, $dh) )
+					{
+						@unlink(ENANO_ROOT . "/cache/$dh");
+					}
+				}
+				@closedir($dr);
+			}
+		}
+		
+		$q = $db->sql_query('DELETE FROM ' . table_prefix . "files WHERE page_id = '{$db->escape($page_id)}';");
+		if ( !$q )
+			$db->die();
+		
+		return true;
+	}
+	
+	/**
+ 	* Increments the deletion votes for a page by 1, and adds the current username/IP to the list of users that have voted for the page to prevent dual-voting
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @return string
+ 	*/
+ 	
+	public static function delvote($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $cache;
+		
+		if ( !$session->get_permissions('vote_delete') )
+		{
+			return $lang->get('etc_access_denied');
+		}
+		
+		if ( $namespace == 'Admin' || $namespace == 'Special' || $namespace == 'System' )
+		{
+			return 'Special pages and system messages can\'t be voted for deletion.';
+		}
+		
+		$pname = $paths->nslist[$namespace] . sanitize_page_id($page_id);
+		
+		if ( !isPage($pname) )
+		{
+			return 'The page does not exist.';
+		}
+		
+		$ns = namespace_factory($page_id, $namespace);
+		$cdata = $ns->get_cdata();
+		
+		$cv  =& $cdata['delvotes'];
+		$ips =& $cdata['delvote_ips'];
+		
+		if ( empty($ips) )
+		{
+			$ips = array(
+				'ip' => array(),
+				'u' => array()
+				);
+		}
+		else
+		{
+			$ips = @unserialize($ips);
+			if ( !$ips )
+			{
+				$ips = array(
+				'ip' => array(),
+				'u' => array()
+				);
+			}
+		}
+		
+		if ( in_array($session->username, $ips['u']) || in_array($_SERVER['REMOTE_ADDR'], $ips['ip']) )
+		{
+			return $lang->get('ajax_delvote_already_voted');
+		}
+		
+		$ips['u'][] = $session->username;
+		$ips['ip'][] = $_SERVER['REMOTE_ADDR'];
+		$ips = $db->escape( serialize($ips) );
+		
+		$cv++;
+		
+		$q = 'UPDATE ' . table_prefix.'pages SET delvotes=' . $cv . ',delvote_ips=\'' . $ips . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'';
+		$w = $db->sql_query($q);
+		if ( !$w )
+			$db->_die();
+		
+		// all done, flush page cache to mark it up
+		$cache->purge('page_meta');
+		
+		return $lang->get('ajax_delvote_success');
+	}
+	
+	/**
+ 	* Resets the number of votes against a page to 0.
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @return string
+ 	*/
+	
+	public static function resetdelvotes($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $cache;
+		
+		if ( !$session->get_permissions('vote_reset') )
+		{
+			return $lang->get('etc_access_denied');
+		}
+		
+		$page_id = $db->escape($page_id);
+		$namespace = $db->escape($namespace);
+		
+		// pull existing info
+		$q = $db->sql_query('SELECT delvotes, delvote_ips FROM ' . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$namespace'");
+		if ( !$q )
+			$db->_die();
+		if ( $db->numrows() < 1 )
+			return $lang->get('page_err_page_not_exist');
+		
+		list($delvotes, $delvote_ips) = $db->fetchrow_num();
+		$db->free_result();
+		$delvote_ips = $db->escape($delvote_ips);
+		$username = $db->escape($session->username);
+		
+		// log action
+		$time = time();
+		$q = $db->sql_query('INSERT INTO ' . table_prefix . "logs (time_id, log_type, action, edit_summary, page_text, author, author_uid, page_id, namespace) VALUES\n"
+											. "  ( $time, 'page', 'votereset', '$delvotes', '$delvote_ips', '$username', $session->user_id, '$page_id', '$namespace' )");
+		if ( !$q )
+			$db->_die();
+		
+		// reset votes
+		$empty_vote_record = $db->escape(serialize(array('ip'=>array(),'u'=>array())));
+		$q = 'UPDATE ' . table_prefix.'pages SET delvotes=0,delvote_ips=\'' . $empty_vote_record . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\'';
+		$e = $db->sql_query($q);
+		if ( !$e )
+		{
+			$db->_die('The number of delete votes was not reset.');
+		}
+		else
+		{
+			$cache->purge('page_meta');
+			return $lang->get('ajax_delvote_reset_success');
+		}
+	}
+	
+	/**
+ 	* Gets a list of styles for a given theme name. As of Banshee, this returns JSON.
+ 	* @param $id the name of the directory for the theme
+ 	* @return string JSON string with an array containing a list of themes
+ 	*/
+ 	
+	public static function getstyles()
+	{
+		
+		if ( !preg_match('/^([a-z0-9_-]+)$/', $_GET['id']) )
+			return enano_json_encode(false);
+		
+		$dir = './themes/' . $_GET['id'] . '/css/';
+		$list = Array();
+		// Open a known directory, and proceed to read its contents
+		if (is_dir($dir)) {
+			if ($dh = opendir($dir)) {
+				while (($file = readdir($dh)) !== false) {
+					if ( preg_match('#^(.*?)\.css$#is', $file) && $file != '_printable.css' ) // _printable.css should be included with every theme
+					{                                                                         // it should be a copy of the original style, but
+																																										// mostly black and white
+																																										// Note to self: document this
+						$list[] = substr($file, 0, strlen($file)-4);
+					}
+				}
+				closedir($dh);
+			}
+		}
+		else
+		{
+			return(enano_json_encode(Array('mode' => 'error', 'error' => $dir.' is not a dir')));
+		}
+		
+		return enano_json_encode($list);
+	}
+	
+	/**
+ 	* Assembles a Javascript app with category information
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @return string Javascript code
+ 	*/
+ 	
+	public static function catedit($page_id, $namespace)
+	{
+		$d = PageUtils::catedit_raw($page_id, $namespace);
+		return $d[0] . ' /* BEGIN CONTENT */ document.getElementById("ajaxEditContainer").innerHTML = unescape(\''.rawurlencode($d[1]).'\');';
+	}
+	
+	/**
+ 	* Does the actual HTML/javascript generation for cat editing, but returns an array
+ 	* @access private
+ 	*/
+ 	
+	public static function catedit_raw($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		ob_start();
+		$_ob = '';
+		$e = $db->sql_query('SELECT category_id FROM ' . table_prefix.'categories WHERE page_id=\'' . $paths->page_id . '\' AND namespace=\'' . $paths->namespace . '\'');
+		if(!$e) jsdie('Error selecting category information for current page: '.$db->get_error());
+		$cat_current = Array();
+		while($r = $db->fetchrow())
+		{
+			$cat_current[] = $r;
+		}
+		$db->free_result();
+		
+		$cat_all = array();
+		$q = $db->sql_query('SELECT * FROM ' . table_prefix . 'pages WHERE namespace = \'Category\';');
+		if ( !$q )
+			$db->_die();
+		
+		while ( $row = $db->fetchrow() )
+		{
+			$cat_all[] = Namespace_Default::bake_cdata($row);
+		}
+		
+		// Make $cat_all an associative array, like $paths->pages
+		$sz = sizeof($cat_all);
+		for($i=0;$i<$sz;$i++)
+		{
+			$cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i];
+		}
+		// Now, the "zipper" function - join the list of categories with the list of cats that this page is a part of
+		$cat_info = $cat_all;
+		for($i=0;$i<sizeof($cat_current);$i++)
+		{
+			$un = $cat_current[$i]['category_id'];
+			$cat_info[$un]['member'] = true;
+		}
+		// Now copy the information we just set into the numerically named keys
+		for($i=0;$i<sizeof($cat_info)/2;$i++)
+		{
+			$un = $cat_info[$i]['urlname_nons'];
+			$cat_info[$i] = $cat_info[$un];
+		}
+		
+		echo 'catlist = new Array();'; // Initialize the client-side category list
+		$_ob .= '<h3>' . $lang->get('catedit_title') . '</h3>
+ 						<form name="mdgCatForm" action="'.makeUrlNS($namespace, $page_id, 'do=catedit').'" method="post">';
+		if ( sizeof($cat_info) < 1 )
+		{
+			$_ob .= '<p>' . $lang->get('catedit_no_categories') . '</p>';
+		}
+		for ( $i = 0; $i < sizeof($cat_info) / 2; $i++ )
+		{
+			// Protection code added 1/3/07
+			// Updated 3/4/07
+			$is_prot = false;
+			$perms = $session->fetch_page_acl($cat_info[$i]['urlname_nons'], 'Category');
+			if ( !$session->get_permissions('edit_cat') || !$perms->get_permissions('edit_cat') ||
+ 				( $cat_info[$i]['really_protected'] && !$perms->get_permissions('even_when_protected') ) )
+ 				$is_prot = true;
+			$prot = ( $is_prot ) ? ' disabled="disabled" ' : '';
+			$prottext = ( $is_prot ) ? ' <img alt="(protected)" width="16" height="16" src="'.scriptPath.'/images/lock16.png" />' : '';
+			echo 'catlist[' . $i . '] = \'' . $cat_info[$i]['urlname_nons'] . '\';';
+			$_ob .= '<span class="catCheck"><input ' . $prot . ' name="' . $cat_info[$i]['urlname_nons'] . '" id="mdgCat_' . $cat_info[$i]['urlname_nons'] . '" type="checkbox"';
+			if(isset($cat_info[$i]['member'])) $_ob .= ' checked="checked"';
+			$_ob .= '/>  <label for="mdgCat_' . $cat_info[$i]['urlname_nons'] . '">' . $cat_info[$i]['name'].$prottext.'</label></span><br />';
+		}
+		
+		$disabled = ( sizeof($cat_info) < 1 ) ? 'disabled="disabled"' : '';
+			
+		$_ob .= '<div style="border-top: 1px solid #CCC; padding-top: 5px; margin-top: 10px;"><input name="__enanoSaveButton" ' . $disabled . ' style="font-weight: bold;" type="submit" onclick="ajaxCatSave(); return false;" value="' . $lang->get('etc_save_changes') . '" /> <input name="__enanoCatCancel" type="submit" onclick="ajaxReset(); return false;" value="' . $lang->get('etc_cancel') . '" /></div></form>';
+		
+		$cont = ob_get_contents();
+		ob_end_clean();
+		return Array($cont, $_ob);
+	}
+	
+	/**
+ 	* Saves category information
+ 	* WARNING: If $which_cats is empty, all the category information for the selected page will be nuked!
+ 	* @param $page_id string the page ID
+ 	* @param $namespace string the namespace
+ 	* @param $which_cats array associative array of categories to put the page in
+ 	* @return string "GOOD" on success, error string on failure
+ 	*/
+	
+	public static function catsave($page_id, $namespace, $which_cats)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if(!$session->get_permissions('edit_cat')) return('Insufficient privileges to change category information');
+		
+		$page_perms = $session->fetch_page_acl($page_id, $namespace);
+		$ns = namespace_factory($page_id, $namespace);
+		$page_data = $ns->get_cdata();
+		
+		$cat_all = array();
+		$q = $db->sql_query('SELECT * FROM ' . table_prefix . 'pages WHERE namespace = \'Category\';');
+		if ( !$q )
+			$db->_die();
+		
+		while ( $row = $db->fetchrow() )
+		{
+			$cat_all[] = Namespace_Default::bake_cdata($row);
+		}
+		
+		// Make $cat_all an associative array, like $paths->pages
+		$sz = sizeof($cat_all);
+		for($i=0;$i<$sz;$i++)
+		{
+			$cat_all[$cat_all[$i]['urlname_nons']] = $cat_all[$i];
+		}
+		
+		$rowlist = Array();
+		
+		for($i=0;$i<sizeof($cat_all)/2;$i++)
+		{
+			$auth = true;
+			$perms = $session->fetch_page_acl($cat_all[$i]['urlname_nons'], 'Category');
+			if ( !$session->get_permissions('edit_cat') || !$perms->get_permissions('edit_cat') ||
+ 				( $cat_all[$i]['really_protected'] && !$perms->get_permissions('even_when_protected') ) ||
+ 				( !$page_perms->get_permissions('even_when_protected') && $page_data['protected'] == '1' ) )
+ 				$auth = false;
+			if(!$auth)
+			{
+				// Find out if the page is currently in the category
+				$q = $db->sql_query('SELECT * FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
+				if(!$q)
+					return 'MySQL error: ' . $db->get_error();
+				if($db->numrows() > 0)
+				{
+					$auth = true;
+					$which_cats[$cat_all[$i]['urlname_nons']] = true; // Force the category to stay in its current state
+				}
+				$db->free_result();
+			}
+			if(isset($which_cats[$cat_all[$i]['urlname_nons']]) && $which_cats[$cat_all[$i]['urlname_nons']] == true /* for clarity ;-) */ && $auth ) $rowlist[] = '(\'' . $page_id . '\', \'' . $namespace . '\', \'' . $cat_all[$i]['urlname_nons'] . '\')';
+		}
+		if(sizeof($rowlist) > 0)
+		{
+			$val = implode(',', $rowlist);
+			$q = 'INSERT INTO ' . table_prefix.'categories(page_id,namespace,category_id) VALUES' . $val . ';';
+			$e = $db->sql_query('DELETE FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
+			if(!$e) $db->_die('The old category data could not be deleted.');
+			$e = $db->sql_query($q);
+			if(!$e) $db->_die('The new category data could not be inserted.');
+			return('GOOD');
+		}
+		else
+		{
+			$e = $db->sql_query('DELETE FROM ' . table_prefix.'categories WHERE page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
+			if(!$e) $db->_die('The old category data could not be deleted.');
+			return('GOOD');
+		}
+	}
+	
+	/**
+ 	* Sets the wiki mode level for a page.
+ 	* @param $page_id string the page ID
+ 	* @param $namespace string the namespace
+ 	* @param $level int 0 for off, 1 for on, 2 for use global setting
+ 	* @return string "GOOD" on success, error string on failure
+ 	*/
+	
+	public static function setwikimode($page_id, $namespace, $level)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $cache;
+		
+		if(!$session->get_permissions('set_wiki_mode')) return('Insufficient access rights');
+		if ( !isset($level) || ( isset($level) && !preg_match('#^([0-2]){1}$#', (string)$level) ) )
+		{
+			return('Invalid mode string');
+		}
+		$q = $db->sql_query('UPDATE ' . table_prefix.'pages SET wiki_mode=' . $level . ' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
+		if ( !$q )
+		{
+			return('Error during update query: '.$db->get_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace());
+		}
+		
+		$cache->purge('page_meta');
+		return('GOOD');
+	}
+	
+	/**
+ 	* Sets the access password for a page.
+ 	* @param $page_id string the page ID
+ 	* @param $namespace string the namespace
+ 	* @param $pass string the SHA1 hash of the password - if the password doesn't match the regex ^([0-9a-f]*){40,40}$ it will be sha1'ed
+ 	* @return string
+ 	*/
+	
+	public static function setpass($page_id, $namespace, $pass)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang, $cache;
+		// Determine permissions
+		$ns = namespace_factory($page_id, $namespace);
+		$cdata = $ns->get_cdata();
+		if ( $cdata['password'] != '' )
+			$a = $session->get_permissions('password_reset');
+		else
+			$a = $session->get_permissions('password_set');
+		if ( !$a )
+			return $lang->get('etc_access_denied');
+		if ( !isset($pass) )
+			return('Password was not set on URL');
+		$p = $pass;
+		if ( !preg_match('#([0-9a-f]){40,40}#', $p) )
+		{
+			$p = sha1($p);
+		}
+		if ( $p == 'da39a3ee5e6b4b0d3255bfef95601890afd80709' )
+			// sha1('') = da39a3ee5e6b4b0d3255bfef95601890afd80709
+			$p = '';
+		$e = $db->sql_query('UPDATE ' . table_prefix.'pages SET password=\'' . $p . '\' WHERE urlname=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';');
+		if ( !$e )
+		{
+			die('PageUtils::setpass(): Error during update query: '.$db->get_error()."\n\nSQL Backtrace:\n".$db->sql_backtrace());
+		}
+		// Is the new password blank?
+		if ( $p == '' )
+		{
+			return $lang->get('ajax_password_disable_success');
+		}
+		else
+		{
+			return $lang->get('ajax_password_success');
+		}
+	}
+	
+	/**
+ 	* Generates some preview HTML
+ 	* @param $text string the wikitext to use
+ 	* @return string
+ 	*/
+ 	
+	public static function genPreview($text)
+	{
+		global $lang;
+		$ret = '<div class="info-box">' . $lang->get('editor_preview_blurb') . '</div><div style="background-color: #F8F8F8; padding: 10px; border: 1px dashed #406080; max-height: 250px; overflow: auto; margin: 10px 0;">';
+		$text = RenderMan::render(RenderMan::preprocess_text($text, false, false));
+		ob_start();
+		eval('?>' . $text);
+		$text = ob_get_contents();
+		ob_end_clean();
+		$ret .= $text;
+		$ret .= '</div>';
+		return $ret;
+	}
+	
+	/**
+ 	* Makes a scrollable box
+ 	* @param string $text the inner HTML
+ 	* @param int $height Optional - the maximum height. Defaults to 250.
+ 	* @return string
+ 	*/
+ 	
+	public static function scrollBox($text, $height = 250)
+	{
+		return '<div style="background-color: #F8F8F8; padding: 10px; border: 1px dashed #406080; max-height: '.(string)intval($height).'px; overflow: auto; margin: 1em 0 1em 1em;">' . $text . '</div>';
+	}
+	
+	/**
+ 	* Generates a diff summary between two page revisions.
+ 	* @param $page_id the page ID
+ 	* @param $namespace the namespace
+ 	* @param $id1 the time ID of the first revision
+ 	* @param $id2 the time ID of the second revision
+ 	* @return string XHTML-formatted diff
+ 	*/
+ 	
+	public static function pagediff($page_id, $namespace, $id1, $id2)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if ( !$session->get_permissions('history_view') )
+			return $lang->get('etc_access_denied');
+		
+		if(!preg_match('#^([0-9]+)$#', (string)$id1) ||
+ 			!preg_match('#^([0-9]+)$#', (string)$id2  )) return 'SQL injection attempt';
+		// OK we made it through security
+		// Safest way to make sure we don't end up with the revisions in wrong columns is to make 2 queries
+		if ( !$q1 = $db->sql_query('SELECT time_id,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE log_id = ' . $id1 . ' AND log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';')) return 'MySQL error: ' . $db->get_error();
+		if ( !$q2 = $db->sql_query('SELECT time_id,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE log_id = ' . $id2 . ' AND log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';')) return 'MySQL error: ' . $db->get_error();
+		$row1 = $db->fetchrow($q1);
+		$db->free_result($q1);
+		$row2 = $db->fetchrow($q2);
+		$db->free_result($q2);
+		if(sizeof($row1) < 1 || sizeof($row2) < 2)
+		{
+			if ( !$q1 = $db->sql_query('SELECT time_id,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE time_id = ' . $id1 . ' AND log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';')) return 'MySQL error: ' . $db->get_error();
+			if ( !$q2 = $db->sql_query('SELECT time_id,page_text,char_tag,author,edit_summary FROM ' . table_prefix.'logs WHERE time_id = ' . $id2 . ' AND log_type=\'page\' AND action=\'edit\' AND page_id=\'' . $page_id . '\' AND namespace=\'' . $namespace . '\';')) return 'MySQL error: ' . $db->get_error();
+			$row1 = $db->fetchrow($q1);
+			$db->free_result($q1);
+			$row2 = $db->fetchrow($q2);
+			$db->free_result($q2);
+			if(sizeof($row1) < 1 || sizeof($row2) < 2)
+				return 'Couldn\'t find any rows that matched the query. The time ID probably doesn\'t exist in the logs table.';
+		}
+		$text1 = $row1['page_text'];
+		$text2 = $row2['page_text'];
+		$time1 = enano_date(ED_DATE | ED_TIME, $row1['time_id']);
+		$time2 = enano_date(ED_DATE | ED_TIME, $row2['time_id']);
+		$_ob = "
+		<p>" . $lang->get('history_lbl_comparingrevisions') . " {$time1} &rarr; {$time2}</p>
+		";
+		// Free some memory
+		unset($row1, $row2, $q1, $q2);
+		
+		$_ob .= RenderMan::diff($text1, $text2);
+		return $_ob;
+	}
+	
+	/**
+ 	* Gets ACL information about the selected page for target type X and target ID Y.
+ 	* @param array $parms What to select. This is an array purely for JSON compatibility. It should be an associative array with keys target_type and target_id.
+ 	* @return array
+ 	*/
+ 	
+	public static function acl_editor($parms = Array())
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if(!$session->get_permissions('edit_acl') && ( $session->user_level < USER_LEVEL_ADMIN || !defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL')) )
+		{
+			return Array(
+				'mode' => 'error',
+				'error' => $lang->get('acl_err_access_denied')
+				);
+		}
+		if ( !$session->sid_super )
+		{
+			return Array(
+				'mode' => 'error',
+				'error' => $lang->get('etc_access_denied_need_reauth')
+				);
+		}
+		$parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false;
+		$parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false;
+		$page_id =& $parms['page_id'];
+		$namespace =& $parms['namespace'];
+		$page_where_clause      = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\'' . $db->escape($page_id) . '\' AND a.namespace=\'' . $db->escape($namespace) . '\'';
+		$page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\'';
+		//die(print_r($page_id,true));
+		$template->load_theme();
+		// $perms_obj = $session->fetch_page_acl($page_id, $namespace);
+		$perms_obj =& $session;
+		$return = Array();
+		if ( !file_exists(ENANO_ROOT . '/themes/' . $session->theme . '/acledit.tpl') )
+		{
+			return Array(
+				'mode' => 'error',
+				'error' => $lang->get('acl_err_missing_template'),
+			);
+		}
+		$return['template'] = $template->extract_vars('acledit.tpl');
+		$return['page_id'] = $page_id;
+		$return['namespace'] = $namespace;
+		if(isset($parms['mode']))
+		{
+			switch($parms['mode'])
+			{
+				case 'listgroups':
+					$return['groups'] = Array();
+					$q = $db->sql_query('SELECT group_id,group_name FROM ' . table_prefix.'groups ORDER BY group_name ASC;');
+					while($row = $db->fetchrow())
+					{
+						$return['groups'][] = Array(
+							'id' => $row['group_id'],
+							'name' => $row['group_name'],
+							);
+					}
+					$db->free_result();
+					$return['page_groups'] = Array();
+					$q = $db->sql_query('SELECT pg_id,pg_name FROM ' . table_prefix.'page_groups ORDER BY pg_name ASC;');
+					if ( !$q )
+						return Array(
+							'mode' => 'error',
+							'error' => $db->get_error()
+							);
+					while ( $row = $db->fetchrow() )
+					{
+						$return['page_groups'][] = Array(
+								'id' => $row['pg_id'],
+								'name' => $row['pg_name']
+							);
+					}
+					break;
+				case 'seltarget_id':
+					if ( !is_int($parms['target_id']) )
+					{
+						return Array(
+							'mode' => 'error',
+							'error' => 'Expected parameter target_id type int'
+							);
+					}
+					$q = $db->sql_query('SELECT target_id, target_type, page_id, namespace, rules FROM ' . table_prefix . "acl WHERE rule_id = {$parms['target_id']};");
+					if ( !$q )
+						return Array(
+							'mode' => 'error',
+							'error' => $db->get_error()
+							);
+					if ( $db->numrows() < 1 )
+						return Array(
+							'mode' => 'error',
+							'error' => "No rule with ID {$parms['target_id']} found"
+							);
+						$parms = $db->fetchrow();
+						$db->free_result();
+						
+						// regenerate page selection
+						$parms['page_id'] = ( isset($parms['page_id']) ) ? $parms['page_id'] : false;
+						$parms['namespace'] = ( isset($parms['namespace']) ) ? $parms['namespace'] : false;
+						$parms['mode'] = 'seltarget_id';
+						$page_id =& $parms['page_id'];
+						$namespace =& $parms['namespace'];
+						$page_where_clause      = ( empty($page_id) || empty($namespace) ) ? 'AND a.page_id IS NULL AND a.namespace IS NULL' : 'AND a.page_id=\'' . $db->escape($page_id) . '\' AND a.namespace=\'' . $db->escape($namespace) . '\'';
+						$page_where_clause_lite = ( empty($page_id) || empty($namespace) ) ? 'AND page_id IS NULL AND namespace IS NULL' : 'AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $db->escape($namespace) . '\'';
+						
+						$return['page_id'] = $parms['page_id'];
+						$return['namespace'] = $parms['namespace'];
+						
+						// From here, let the seltarget handler take over
+				case 'seltarget':
+					$return['mode'] = 'seltarget';
+					$return['acl_types'] = $perms_obj->acl_types;
+					$return['acl_deps'] = $perms_obj->acl_deps;
+					$return['acl_descs'] = $perms_obj->acl_descs;
+					$return['target_type'] = $parms['target_type'];
+					$return['target_id'] = $parms['target_id'];
+					switch($parms['target_type'])
+					{
+						case ACL_TYPE_USER:
+							$user_col = ( $parms['mode'] == 'seltarget_id' ) ? 'user_id' : 'username';
+							$q = $db->sql_query('SELECT a.rules,u.user_id,u.username FROM ' . table_prefix.'users AS u
+									LEFT JOIN ' . table_prefix.'acl AS a
+										ON a.target_id=u.user_id
+									WHERE a.target_type='.ACL_TYPE_USER.'
+										AND u.' . $user_col . ' = \'' . $db->escape($parms['target_id']) . '\'
+										' . $page_where_clause . ';');
+							if(!$q)
+								return(Array('mode'=>'error','error'=>$db->get_error()));
+							if($db->numrows() < 1)
+							{
+								$return['type'] = 'new';
+								$q = $db->sql_query('SELECT user_id,username FROM ' . table_prefix.'users WHERE username=\'' . $db->escape($parms['target_id']) . '\';');
+								if(!$q)
+									return(Array('mode'=>'error','error'=>$db->get_error()));
+								if($db->numrows() < 1)
+									return Array('mode'=>'error','error'=>$lang->get('acl_err_user_not_found'),'debug' => $db->sql_backtrace());
+								$row = $db->fetchrow();
+								$return['target_name'] = $row['username'];
+								$return['target_id'] = intval($row['user_id']);
+								$return['current_perms'] = array();
+							}
+							else
+							{
+								$return['type'] = 'edit';
+								$row = $db->fetchrow();
+								$return['target_name'] = $row['username'];
+								$return['target_id'] = intval($row['user_id']);
+								$return['current_perms'] = $session->string_to_perm($row['rules']);
+							}
+							$db->free_result();
+							// Eliminate types that don't apply to this namespace
+							if ( $namespace && $namespace != '__PageGroup' )
+							{
+								foreach ( $return['current_perms'] AS $i => $perm )
+								{
+									if ( ( $page_id != null && $namespace != null ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) )
+									{
+										// echo "// SCOPE CONTROL: eliminating: $i\n";
+										unset($return['current_perms'][$i]);
+										unset($return['acl_types'][$i]);
+										unset($return['acl_descs'][$i]);
+										unset($return['acl_deps'][$i]);
+									}
+								}
+							}
+							break;
+						case ACL_TYPE_GROUP:
+							$q = $db->sql_query('SELECT a.rules,g.group_name,g.group_id FROM ' . table_prefix.'groups AS g
+									LEFT JOIN ' . table_prefix.'acl AS a
+										ON a.target_id=g.group_id
+									WHERE a.target_type='.ACL_TYPE_GROUP.'
+										AND g.group_id=\''.intval($parms['target_id']).'\'
+										' . $page_where_clause . ';');
+							if(!$q)
+								return(Array('mode'=>'error','error'=>$db->get_error()));
+							if($db->numrows() < 1)
+							{
+								$return['type'] = 'new';
+								$q = $db->sql_query('SELECT group_id,group_name FROM ' . table_prefix.'groups WHERE group_id=\''.intval($parms['target_id']).'\';');
+								if(!$q)
+									return(Array('mode'=>'error','error'=>$db->get_error()));
+								if($db->numrows() < 1)
+									return Array('mode'=>'error','error'=>$lang->get('acl_err_bad_group_id'));
+								$row = $db->fetchrow();
+								$return['target_name'] = $row['group_name'];
+								$return['target_id'] = intval($row['group_id']);
+								$return['current_perms'] = array();
+							}
+							else
+							{
+								$return['type'] = 'edit';
+								$row = $db->fetchrow();
+								$return['target_name'] = $row['group_name'];
+								$return['target_id'] = intval($row['group_id']);
+								$return['current_perms'] = $session->string_to_perm($row['rules']);
+							}
+							$db->free_result();
+							// Eliminate types that don't apply to this namespace
+							if ( $namespace && $namespace != '__PageGroup' )
+							{
+								foreach ( $return['current_perms'] AS $i => $perm )
+								{
+									if ( ( $page_id != false && $namespace != false ) && ( !in_array ( $namespace, $session->acl_scope[$i] ) && !in_array('All', $session->acl_scope[$i]) ) )
+									{
+										// echo "// SCOPE CONTROL: eliminating: $i\n"; //; ".print_r($namespace,true).":".print_r($page_id,true)."\n";
+										unset($return['current_perms'][$i]);
+										unset($return['acl_types'][$i]);
+										unset($return['acl_descs'][$i]);
+										unset($return['acl_deps'][$i]);
+									}
+								}
+							}
+							//return Array('mode'=>'debug','text'=>print_r($return, true));
+							break;
+						default:
+							return Array('mode'=>'error','error','Invalid ACL type ID');
+							break;
+					}
+					return $return;
+					break;
+				case 'save_new':
+				case 'save_edit':
+					if ( defined('ENANO_DEMO_MODE') )
+					{
+						return Array('mode'=>'error','error'=>$lang->get('acl_err_demo'));
+					}
+					$q = $db->sql_query('DELETE FROM ' . table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).'
+						' . $page_where_clause_lite . ';');
+					if(!$q)
+						return Array('mode'=>'error','error'=>$db->get_error());
+					if ( sizeof ( $parms['perms'] ) < 1 )
+					{
+						// As of 1.1.x, this returns success because the rule length is zero if the user selected "inherit" in all columns
+						return Array(
+							'mode' => 'success',
+							'target_type' => $parms['target_type'],
+							'target_id' => $parms['target_id'],
+							'target_name' => $parms['target_name'],
+							'page_id' => $page_id,
+							'namespace' => $namespace,
+						);
+					}
+					$rules = $session->perm_to_string($parms['perms']);
+					$q = ($page_id && $namespace) ? 'INSERT INTO ' . table_prefix.'acl ( target_type, target_id, page_id, namespace, rules )
+ 																						VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \'' . $db->escape($page_id) . '\', \'' . $db->escape($namespace) . '\', \'' . $db->escape($rules) . '\' )' :
+																					'INSERT INTO ' . table_prefix.'acl ( target_type, target_id, rules )
+ 																						VALUES( '.intval($parms['target_type']).', '.intval($parms['target_id']).', \'' . $db->escape($rules) . '\' )';
+					if(!$db->sql_query($q)) return Array('mode'=>'error','error'=>$db->get_error());
+					return Array(
+							'mode' => 'success',
+							'target_type' => $parms['target_type'],
+							'target_id' => $parms['target_id'],
+							'target_name' => $parms['target_name'],
+							'page_id' => $page_id,
+							'namespace' => $namespace,
+						);
+					break;
+				case 'delete':
+					if ( defined('ENANO_DEMO_MODE') )
+					{
+						return Array('mode'=>'error','error'=>$lang->get('acl_err_demo'));
+					}
+					$sql = 'DELETE FROM ' . table_prefix.'acl WHERE target_type='.intval($parms['target_type']).' AND target_id='.intval($parms['target_id']).'
+						' . $page_where_clause_lite . ';';
+					$q = $db->sql_query($sql);
+					if(!$q)
+						return Array('mode'=>'error','error'=>$db->get_error());
+					return Array(
+							'mode' => 'delete',
+							'target_type' => $parms['target_type'],
+							'target_id' => $parms['target_id'],
+							'target_name' => $parms['target_name'],
+							'page_id' => $page_id,
+							'namespace' => $namespace,
+						);
+					break;
+				case 'list_existing':
+					
+					$return = array(
+							'mode'  => 'list_existing',
+							'key'   => acl_list_draw_key(),
+							'rules' => array()
+						);
+					
+					$acl_columns = 'a.' . implode(', a.', $db->columns_in(table_prefix . 'acl'));
+					$users_columns = 'u.' . implode(', u.', $db->columns_in(table_prefix . 'users'));
+					$groups_columns = 'g.' . implode(', g.', $db->columns_in(table_prefix . 'groups'));
+					$pg_columns = 'p.' . implode(', p.', array('pg_id', 'pg_type', 'pg_name', 'pg_target'));
+					
+					$q = $db->sql_query("SELECT a.rule_id, u.username, g.group_name, a.target_type, a.target_id, a.page_id, a.namespace, a.rules, p.pg_name\n"
+									. "  FROM " . table_prefix . "acl AS a\n"
+									. "  LEFT JOIN " . table_prefix . "users AS u\n"
+									. "    ON ( (a.target_type = " . ACL_TYPE_USER . " AND a.target_id = u.user_id) OR (u.user_id IS NULL) )\n"
+									. "  LEFT JOIN " . table_prefix . "groups AS g\n"
+									. "    ON ( (a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id) OR (g.group_id IS NULL) )\n"
+									. "  LEFT JOIN " . table_prefix . "page_groups as p\n"
+									. "    ON ( (a.namespace = '__PageGroup' AND a.page_id = CAST(p.pg_id AS CHAR)) OR (p.pg_id IS NULL) )\n"
+									. "  WHERE ( a.target_type = " . ACL_TYPE_USER . " OR a.target_type = " . ACL_TYPE_GROUP . " )\n"
+									. "  GROUP BY a.rule_id, $acl_columns, $users_columns, $groups_columns, $pg_columns\n"
+									. "  ORDER BY a.target_type ASC, a.rule_id ASC;"
+								);
+					
+					if ( !$q )
+						$db->_die();
+					
+					while ( $row = $db->fetchrow($q) )
+					{
+						if ( $row['target_type'] == ACL_TYPE_USER && empty($row['username']) )
+						{
+							// This is only done if we have an ACL affecting a user that doesn't exist.
+							// Nice little bit of maintenance to have.
+							if ( !$db->sql_query("DELETE FROM " . table_prefix . "acl WHERE rule_id = {$row['rule_id']};") )
+								$db->_die();
+							continue;
+						}
+						$score = get_acl_rule_score($row['rules']);
+						$deep_limit = ACL_SCALE_MINIMAL_SHADE;
+						// Determine background color of cell by score
+						if ( $score > 5 )
+						{
+							// high score, show in green
+							$color = 2.5 * $score;
+							if ( $color > 255 )
+								$color = 255;
+							$color = round($color);
+							// blend with the colordepth limit
+							$color = $deep_limit + ( ( 0xFF - $deep_limit ) - ( ( $color / 0xFF ) * ( 0xFF - $deep_limit ) ) );
+							$color = dechex($color);
+							$color = "{$color}ff{$color}";
+						}
+						else if ( $score < -5 )
+						{
+							// low score, show in red
+							$color = 0 - $score;
+							$color = 2.5 * $color;
+							if ( $color > 255 )
+								$color = 255;
+							$color = round($color);
+							// blend with the colordepth limit
+							$color = $deep_limit + ( ( 0xFF - $deep_limit ) - ( ( $color / 0xFF ) * ( 0xFF - $deep_limit ) ) );
+							$color = dechex($color);
+							$color = "ff{$color}{$color}";
+						}
+						else
+						{
+							$color = 'efefef';
+						}
+						
+						// Rate rule textually based on its score
+						if ( $score >= 70 )
+							$desc = $lang->get('acl_msg_scale_allow');
+						else if ( $score >= 50 )
+							$desc = $lang->get('acl_msg_scale_mostly_allow');
+						else if ( $score >= 25 )
+							$desc = $lang->get('acl_msg_scale_some_allow');
+						else if ( $score >= -25 )
+							$desc = $lang->get('acl_msg_scale_mixed');
+						else if ( $score <= -70 )
+							$desc = $lang->get('acl_msg_scale_deny');
+						else if ( $score <= -50 )
+							$desc = $lang->get('acl_msg_scale_mostly_deny');
+						else if ( $score <= -25 )
+							$desc = $lang->get('acl_msg_scale_some_deny');
+						
+						// group and user target info
+						$info = '';
+						if ( $row['target_type'] == ACL_TYPE_USER )
+							$info = $lang->get('acl_msg_list_user', array( 'username' => $row['username'] )); // "(User: {$row['username']})";
+						else if ( $row['target_type'] == ACL_TYPE_GROUP )
+							$info = $lang->get('acl_msg_list_group', array( 'group' => $row['group_name'] ));
+						
+						// affected pages info
+						if ( $row['page_id'] && $row['namespace'] && $row['namespace'] != '__PageGroup' )
+							$info .= $lang->get('acl_msg_list_on_page', array( 'page_name' => "{$row['namespace']}:{$row['page_id']}" ));
+						else if ( $row['page_id'] && $row['namespace'] && $row['namespace'] == '__PageGroup' )
+							$info .= $lang->get('acl_msg_list_on_page_group', array( 'page_group' => $row['pg_name'] ));
+						else
+							$info .= $lang->get('acl_msg_list_entire_site');
+							
+						$score_string = $lang->get('acl_msg_list_score', array
+							(
+								'score' => $score,
+								'desc'  => $desc,
+								'info'  => $info
+								));
+						$return['rules'][] = array(
+							'score_string' => $score_string,
+							'rule_id'      => $row['rule_id'],
+							'color'        => $color
+							);
+					}
+					
+					break;
+				case 'list_presets':
+					$presets = array();
+					$q = $db->sql_query('SELECT page_id AS preset_name, rule_id, rules FROM ' . table_prefix . "acl WHERE target_type = " . ACL_TYPE_PRESET . ";");
+					if ( !$q )
+						$db->die_json();
+					
+					while ( $row = $db->fetchrow() )
+					{
+						$row['rules'] = $session->string_to_perm($row['rules']);
+						$presets[] = $row;
+					}
+					
+					return array(
+						'mode' => 'list_existing',
+						'presets' => $presets
+					);
+					break;
+				case 'save_preset':
+					if ( empty($parms['preset_name']) )
+					{
+						return array(
+							'mode' => 'error',
+							'error' => $lang->get('acl_err_preset_name_empty')
+						);
+					}
+					$preset_name = $db->escape($parms['preset_name']);
+					$q = $db->sql_query('DELETE FROM ' . table_prefix . "acl WHERE target_type = " . ACL_TYPE_PRESET . " AND page_id = '$preset_name';");
+					if ( !$q )
+						$db->die_json();
+					
+					$perms = $session->perm_to_string($parms['perms']);
+					if ( !$perms )
+					{
+						return array(
+							'mode' => 'error',
+							'error' => $lang->get('acl_err_preset_is_blank')
+						);
+					}
+					
+					$perms = $db->escape($perms);
+					$q = $db->sql_query('INSERT INTO ' . table_prefix . "acl(page_id, target_type, rules) VALUES\n"
+														. "  ( '$preset_name', " . ACL_TYPE_PRESET . ", '$perms' );");
+					if ( !$q )
+						$db->die_json();
+					
+					return array(
+							'mode' => 'success'
+						);
+					break;
+				case 'trace':
+					list($targetpid, $targetns) = RenderMan::strToPageID($parms['page']);
+					try
+					{
+						$perms = $session->fetch_page_acl_user($parms['user'], $targetpid, $targetns);
+						$perm_table = array(
+								AUTH_ALLOW => 'acl_lbl_field_allow',
+								AUTH_WIKIMODE => 'acl_lbl_field_wikimode',
+								AUTH_DISALLOW => 'acl_lbl_field_disallow',
+								AUTH_DENY => 'acl_lbl_field_deny'
+							);
+						
+						$return = array(
+							'mode' => 'trace',
+							'perms' => array()
+						);
+						
+						foreach ( $perms->perm_resolve_table as $perm_type => $lookup_data )
+						{
+							if ( !$session->check_acl_scope($perm_type, $targetns) )
+								continue;
+							
+							$src_l10n = $lang->get($session->acl_inherit_lang_table[$lookup_data['src']], $lookup_data);
+							$divclass = preg_replace('/^acl_inherit_/', '', $session->acl_inherit_lang_table[$lookup_data['src']]);
+							$perm_string = $lang->get($perm_table[$perms->perms[$perm_type]]);
+							$perm_name = $lang->get($session->acl_descs[$perm_type]);
+							
+							$return['perms'][$perm_type] = array(
+									'divclass' => "acl_inherit acl_$divclass",
+									'perm_type' => $perm_type,
+									'perm_name' => $perm_name,
+									'perm_value' => $perm_string,
+									'perm_src' => $src_l10n,
+									'rule_id' => intval($lookup_data['rule_id']),
+									'bad_deps' => $perms->acl_check_deps($perm_type, true)
+								);
+						}
+						
+						// group rules if possible
+						$return['groups'] = array();
+						foreach ( $return['perms'] as $rule )
+						{
+							if ( !isset($return['groups'][$rule['rule_id']]) )
+							{
+								$return['groups'][$rule['rule_id']] = array();
+							}
+							$return['groups'][$rule['rule_id']][] = $rule['perm_type'];
+						}
+					}
+					catch ( Exception $e )
+					{
+						$return = array(
+								'mode' => 'error',
+								'error' => $e->getMessage()
+							);
+					}
+					
+					break;
+				default:
+					return Array('mode'=>'error','error'=>'Hacking attempt');
+					break;
+			}
+		}
+		return $return;
+	}
+	
+	/**
+ 	* Same as PageUtils::acl_editor(), but the parms are a JSON string instead of an array. This also returns a JSON string.
+ 	* @param string $parms Same as PageUtils::acl_editor/$parms, but should be a valid JSON string.
+ 	* @return string
+ 	*/
+ 	
+	public static function acl_json($parms = '{ }')
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		try
+		{
+			$parms = enano_json_decode($parms);
+		}
+		catch ( Zend_Json_Exception $e )
+		{
+			$parms = array();
+		}
+		$ret = PageUtils::acl_editor($parms);
+		$ret = enano_json_encode($ret);
+		return $ret;
+	}
+	
+	/**
+ 	* A non-Javascript frontend for the ACL API.
+ 	* @param array The request data, if any, this should be in the format required by PageUtils::acl_editor()
+ 	*/
+ 	
+	public static function aclmanager($parms)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		ob_start();
+		// Convenience
+		$formstart = '<form 
+										action="' . makeUrl($paths->page, 'do=aclmanager', true) . '"
+										method="post" enctype="multipart/form-data"
+										onsubmit="if(!submitAuthorized) return false;"
+										>';
+		$formend   = '</form>';
+		$parms    = PageUtils::acl_preprocess($parms);
+		$response = PageUtils::acl_editor($parms);
+		$response = PageUtils::acl_postprocess($response);
+		
+		//die('<pre>' . htmlspecialchars(print_r($response, true)) . '</pre>');
+		
+		switch($response['mode'])
+		{
+			case 'debug':
+				echo '<pre>' . htmlspecialchars($response['text']) . '</pre>';
+				break;
+			case 'stage1':
+				echo '<h3>' . $lang->get('acl_lbl_welcome_title') . '</h3>
+							<p>' . $lang->get('acl_lbl_welcome_body') . '</p>';
+				echo $formstart;
+				echo '<p><label><input type="radio" name="data[target_type]" value="' . ACL_TYPE_GROUP . '" checked="checked" /> ' . $lang->get('acl_radio_usergroup') . '</label></p>
+							<p><select name="data[target_id_grp]">';
+				foreach ( $response['groups'] as $group )
+				{
+					echo '<option value="' . $group['id'] . '">' . $group['name'] . '</option>';
+				}
+				
+				// page group selector
+				$groupsel = '';
+				if ( count($response['page_groups']) > 0 )
+				{
+					$groupsel = '<p><label><input type="radio" name="data[scope]" value="page_group" /> ' . $lang->get('acl_radio_scope_pagegroup') . '</label></p>
+ 											<p><select name="data[pg_id]">';
+					foreach ( $response['page_groups'] as $grp )
+					{
+						$groupsel .= '<option value="' . $grp['id'] . '">' . htmlspecialchars($grp['name']) . '</option>';
+					}
+					$groupsel .= '</select></p>';
+				}
+				
+				echo '</select></p>
+							<p><label><input type="radio" name="data[target_type]" value="' . ACL_TYPE_USER . '" /> ' . $lang->get('acl_radio_user') . '</label></p>
+							<p>' . $template->username_field('data[target_id_user]') . '</p>
+							<p>' . $lang->get('acl_lbl_scope') . '</p>
+							<p><label><input name="data[scope]" value="only_this" type="radio" checked="checked" /> ' . $lang->get('acl_radio_scope_thispage') . '</p>
+							' . $groupsel . '
+							<p><label><input name="data[scope]" value="entire_site" type="radio" /> ' . $lang->get('acl_radio_scope_wholesite') . '</p>
+							<div style="margin: 0 auto 0 0; text-align: right;">
+								<input name="data[mode]" value="seltarget" type="hidden" />
+								<input type="hidden" name="data[page_id]" value="' . $paths->page_id . '" />
+								<input type="hidden" name="data[namespace]" value="' . $paths->namespace . '" />
+								<input type="submit" value="' . htmlspecialchars($lang->get('etc_wizard_next')) . '" />
+							</div>';
+				echo $formend;
+				break;
+			case 'success':
+				echo '<div class="info-box">
+								<b>' . $lang->get('acl_lbl_save_success_title') . '</b><br />
+								' . $lang->get('acl_lbl_save_success_body', array( 'target_name' => $response['target_name'] )) . '<br />
+								' . $formstart . '
+								<input type="hidden" name="data[mode]" value="seltarget" />
+								<input type="hidden" name="data[target_type]" value="' . $response['target_type'] . '" />
+								<input type="hidden" name="data[target_id_user]" value="' . ( ( intval($response['target_type']) == ACL_TYPE_USER ) ? $response['target_name'] : $response['target_id'] ) .'" />
+								<input type="hidden" name="data[target_id_grp]"  value="' . ( ( intval($response['target_type']) == ACL_TYPE_USER ) ? $response['target_name'] : $response['target_id'] ) .'" />
+								<input type="hidden" name="data[scope]" value="' . ( ( $response['page_id'] ) ? 'only_this' : 'entire_site' ) . '" />
+								<input type="hidden" name="data[page_id]" value="' . ( ( $response['page_id'] ) ? $response['page_id'] : 'false' ) . '" />
+								<input type="hidden" name="data[namespace]" value="' . ( ( $response['namespace'] ) ? $response['namespace'] : 'false' ) . '" />
+								<input type="submit" value="' . $lang->get('acl_btn_returnto_editor') . '" /> <input type="submit" name="data[act_go_stage1]" value="' . $lang->get('acl_btn_returnto_userscope') . '" />
+								' . $formend . '
+							</div>';
+				break;
+			case 'delete':
+				echo '<div class="info-box">
+								<b>' . $lang->get('acl_lbl_delete_success_title') . '</b><br />
+								' . $lang->get('acl_lbl_delete_success_body', array('target_name' => $response['target_name'])) . '<br />
+								' . $formstart . '
+								<input type="hidden" name="data[mode]" value="seltarget" />
+								<input type="hidden" name="data[target_type]" value="' . $response['target_type'] . '" />
+								<input type="hidden" name="data[target_id_user]" value="' . ( ( intval($response['target_type']) == ACL_TYPE_USER ) ? $response['target_name'] : $response['target_id'] ) .'" />
+								<input type="hidden" name="data[target_id_grp]"  value="' . ( ( intval($response['target_type']) == ACL_TYPE_USER ) ? $response['target_name'] : $response['target_id'] ) .'" />
+								<input type="hidden" name="data[scope]" value="' . ( ( $response['page_id'] ) ? 'only_this' : 'entire_site' ) . '" />
+								<input type="hidden" name="data[page_id]" value="' . ( ( $response['page_id'] ) ? $response['page_id'] : 'false' ) . '" />
+								<input type="hidden" name="data[namespace]" value="' . ( ( $response['namespace'] ) ? $response['namespace'] : 'false' ) . '" />
+								<input type="submit" value="' . $lang->get('acl_btn_returnto_editor') . '" /> <input type="submit" name="data[act_go_stage1]" value="' . $lang->get('acl_btn_returnto_userscope') . '" />
+								' . $formend . '
+							</div>';
+				break;
+			case 'seltarget':
+				if ( $response['type'] == 'edit' )
+				{
+					echo '<h3>' . $lang->get('acl_lbl_editwin_title_edit') . '</h3>';
+				}
+				else
+				{
+					echo '<h3>' . $lang->get('acl_lbl_editwin_title_create') . '</h3>';
+				}
+				$type  = ( $response['target_type'] == ACL_TYPE_GROUP ) ? $lang->get('acl_target_type_group') : $lang->get('acl_target_type_user');
+				$scope = ( $response['page_id'] ) ? ( $response['namespace'] == '__PageGroup' ? $lang->get('acl_scope_type_pagegroup') : $lang->get('acl_scope_type_thispage') ) : $lang->get('acl_scope_type_wholesite');
+				$subs = array(
+						'target_type' => $type,
+						'target' => $response['target_name'],
+						'scope_type' => $scope
+					);
+				echo $lang->get('acl_lbl_editwin_body', $subs);
+				echo $formstart;
+				$parser = $template->makeParserText( $response['template']['acl_field_begin'] );
+				echo $parser->run();
+				$parser = $template->makeParserText( $response['template']['acl_field_item'] );
+				$cls = 'row2';
+				foreach ( $response['acl_types'] as $acl_type => $value )
+				{
+					$vars = Array(
+							'FIELD_INHERIT_CHECKED' => '',
+							'FIELD_DENY_CHECKED' => '',
+							'FIELD_DISALLOW_CHECKED' => '',
+							'FIELD_WIKIMODE_CHECKED' => '',
+							'FIELD_ALLOW_CHECKED' => '',
+						);
+					$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+					$vars['ROW_CLASS'] = $cls;
+					
+					switch ( $response['current_perms'][$acl_type] )
+					{
+						case 'i':
+						default:
+							$vars['FIELD_INHERIT_CHECKED'] = 'checked="checked"';
+							break;
+						case AUTH_ALLOW:
+							$vars['FIELD_ALLOW_CHECKED'] = 'checked="checked"';
+							break;
+						case AUTH_WIKIMODE:
+							$vars['FIELD_WIKIMODE_CHECKED'] = 'checked="checked"';
+							break;
+						case AUTH_DISALLOW:
+							$vars['FIELD_DISALLOW_CHECKED'] = 'checked="checked"';
+							break;
+ 						case AUTH_DENY:
+							$vars['FIELD_DENY_CHECKED'] = 'checked="checked"';
+							break;
+					}
+					$vars['FIELD_NAME'] = 'data[perms][' . $acl_type . ']';
+					if ( preg_match('/^([a-z0-9_]+)$/', $response['acl_descs'][$acl_type]) )
+					{
+						$vars['FIELD_DESC'] = $lang->get($response['acl_descs'][$acl_type]);
+					}
+					else
+					{
+						$vars['FIELD_DESC'] = $response['acl_descs'][$acl_type];
+					}
+					$parser->assign_vars($vars);
+					echo $parser->run();
+				}
+				$parser = $template->makeParserText( $response['template']['acl_field_end'] );
+				echo $parser->run();
+				echo '<div style="margin: 10px auto 0 0; text-align: right;">
+								<input name="data[mode]" value="save_' . $response['type'] . '" type="hidden" />
+								<input type="hidden" name="data[page_id]" value="'   . (( $response['page_id']   ) ? $response['page_id']   : 'false') . '" />
+								<input type="hidden" name="data[namespace]" value="' . (( $response['namespace'] ) ? $response['namespace'] : 'false') . '" />
+								<input type="hidden" name="data[target_type]" value="' . $response['target_type'] . '" />
+								<input type="hidden" name="data[target_id]" value="' . $response['target_id'] . '" />
+								<input type="hidden" name="data[target_name]" value="' . $response['target_name'] . '" />
+								' . ( ( $response['type'] == 'edit' ) ? '<input type="submit" value="' . $lang->get('etc_save_changes') . '" />&nbsp;&nbsp;<input type="submit" name="data[act_delete_rule]" value="' . $lang->get('acl_btn_deleterule') . '" style="color: #AA0000;" onclick="return confirm(\'' . addslashes($lang->get('acl_msg_deleterule_confirm')) . '\');" />' : '<input type="submit" value="' . $lang->get('acl_btn_createrule') . '" />' ) . '
+							</div>';
+				echo $formend;
+				break;
+			case 'error':
+				ob_end_clean();
+				die_friendly('Error occurred', '<p>Error returned by permissions API:</p><pre>' . htmlspecialchars($response['error']) . '</pre>');
+				break;
+		}
+		$ret = ob_get_contents();
+		ob_end_clean();
+		echo
+			$template->getHeader() .
+			$ret .
+			$template->getFooter();
+	}
+	
+	/**
+ 	* Preprocessor to turn the form-submitted data from the ACL editor into something the backend can handle
+ 	* @param array The posted data
+ 	* @return array
+ 	* @access private
+ 	*/
+ 	
+	public static function acl_preprocess($parms)
+	{
+		if ( !isset($parms['mode']) )
+			// Nothing to do
+			return $parms;
+		switch ( $parms['mode'] )
+		{
+			case 'seltarget':
+				
+				// Who's affected?
+				$parms['target_type'] = intval( $parms['target_type'] );
+				$parms['target_id'] = ( $parms['target_type'] == ACL_TYPE_GROUP ) ? $parms['target_id_grp'] : $parms['target_id_user'];
+				
+			case 'save_edit':
+			case 'save_new':
+				if ( isset($parms['act_delete_rule']) )
+				{
+					$parms['mode'] = 'delete';
+				}
+				
+				// Scope (just this page or entire site?)
+				if ( $parms['scope'] == 'entire_site' || ( $parms['page_id'] == 'false' && $parms['namespace'] == 'false' ) )
+				{
+					$parms['page_id']   = false;
+					$parms['namespace'] = false;
+				}
+				else if ( $parms['scope'] == 'page_group' )
+				{
+					$parms['page_id'] = $parms['pg_id'];
+					$parms['namespace'] = '__PageGroup';
+				}
+				
+				break;
+		}
+		
+		if ( isset($parms['act_go_stage1']) )
+		{
+			$parms = array(
+					'mode' => 'listgroups'
+				);
+		}
+		
+		return $parms;
+	}
+	
+	public static function acl_postprocess($response)
+	{
+		if(!isset($response['mode']))
+		{
+			if ( isset($response['groups']) )
+				$response['mode'] = 'stage1';
+			else
+				$response = Array(
+						'mode' => 'error',
+						'error' => 'Invalid action passed by API backend.',
+					);
+		}
+		return $response;
+	}
+ 	
 }
 
 /**
@@ -2462,32 +2462,32 @@
 
 function acl_list_draw_key()
 {
-  $out  = '<div style="width: 460px; margin: 0 auto; text-align: center; margin-bottom: 10px;">';
-  $out .= '<div style="float: left;">&larr; Deny</div>';
-  $out .= '<div style="float: right;">Allow &rarr;</div>';
-  $out .= 'Neutral';
-  $out .= '<div style="clear: both;"></div>';
-  // 11 boxes on each side of the center
-  $inc = ceil ( ( 0xFF - ACL_SCALE_MINIMAL_SHADE ) / 11 );
-  for ( $i = ACL_SCALE_MINIMAL_SHADE; $i <= 0xFF; $i+= $inc )
-  {
-    $octet = dechex($i);
-    $color = "ff$octet$octet";
-    $out .= '<div style="background-color: #' . $color . '; float: left; width: 20px;">&nbsp;</div>';
-  }
-  $out .= '<div style="background-color: #efefef; float: left; width: 20px;">&nbsp;</div>';
-  for ( $i = 0xFF; $i >= ACL_SCALE_MINIMAL_SHADE; $i-= $inc )
-  {
-    $octet = dechex($i);
-    $color = "{$octet}ff{$octet}";
-    $out .= '<div style="background-color: #' . $color . '; float: left; width: 20px;">&nbsp;</div>';
-  }
-  $out .= '<div style="clear: both;"></div>';
-  $out .= '<div style="float: left;">-100</div>';
-  $out .= '<div style="float: right;">+100</div>';
-  $out .= '0';
-  $out .= '</div>';
-  return $out;
+	$out  = '<div style="width: 460px; margin: 0 auto; text-align: center; margin-bottom: 10px;">';
+	$out .= '<div style="float: left;">&larr; Deny</div>';
+	$out .= '<div style="float: right;">Allow &rarr;</div>';
+	$out .= 'Neutral';
+	$out .= '<div style="clear: both;"></div>';
+	// 11 boxes on each side of the center
+	$inc = ceil ( ( 0xFF - ACL_SCALE_MINIMAL_SHADE ) / 11 );
+	for ( $i = ACL_SCALE_MINIMAL_SHADE; $i <= 0xFF; $i+= $inc )
+	{
+		$octet = dechex($i);
+		$color = "ff$octet$octet";
+		$out .= '<div style="background-color: #' . $color . '; float: left; width: 20px;">&nbsp;</div>';
+	}
+	$out .= '<div style="background-color: #efefef; float: left; width: 20px;">&nbsp;</div>';
+	for ( $i = 0xFF; $i >= ACL_SCALE_MINIMAL_SHADE; $i-= $inc )
+	{
+		$octet = dechex($i);
+		$color = "{$octet}ff{$octet}";
+		$out .= '<div style="background-color: #' . $color . '; float: left; width: 20px;">&nbsp;</div>';
+	}
+	$out .= '<div style="clear: both;"></div>';
+	$out .= '<div style="float: left;">-100</div>';
+	$out .= '<div style="float: right;">+100</div>';
+	$out .= '0';
+	$out .= '</div>';
+	return $out;
 }
 
 /**
@@ -2496,43 +2496,43 @@
 
 function get_acl_rule_score($perms)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if ( is_string($perms) )
-    $perms = $session->string_to_perm($perms);
-  else if ( !is_array($perms) )
-    return false;
-  $score = 0;
-  foreach ( $perms as $item )
-  {
-    switch ( $item )
-    {
-      case AUTH_ALLOW :
-        $inc = 2;
-        break;
-      case AUTH_WIKIMODE:
-        $inc = 1;
-        break;
-      case AUTH_DISALLOW:
-        $inc = -1;
-        break;
-      case AUTH_DENY:
-        $inc = -2;
-        break;
-      default:
-        $inc = 0;
-        break;
-    }
-    $score += $inc;
-  }
-  // this is different from the beta; calculate highest score and
-  // get percentage to be fairer to smaller/less broad rules
-  $divisor = count($perms) * 2;
-  if ( $divisor == 0 )
-  {
-    return 0;
-  }
-  $score = 100 * ( $score / $divisor );
-  return round($score);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	if ( is_string($perms) )
+		$perms = $session->string_to_perm($perms);
+	else if ( !is_array($perms) )
+		return false;
+	$score = 0;
+	foreach ( $perms as $item )
+	{
+		switch ( $item )
+		{
+			case AUTH_ALLOW :
+				$inc = 2;
+				break;
+			case AUTH_WIKIMODE:
+				$inc = 1;
+				break;
+			case AUTH_DISALLOW:
+				$inc = -1;
+				break;
+			case AUTH_DENY:
+				$inc = -2;
+				break;
+			default:
+				$inc = 0;
+				break;
+		}
+		$score += $inc;
+	}
+	// this is different from the beta; calculate highest score and
+	// get percentage to be fairer to smaller/less broad rules
+	$divisor = count($perms) * 2;
+	if ( $divisor == 0 )
+	{
+		return 0;
+	}
+	$score = 100 * ( $score / $divisor );
+	return round($score);
 }
 
 ?>
--- a/includes/paths.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/paths.php	Sun Mar 28 23:10:46 2010 -0400
@@ -18,1083 +18,1083 @@
  
 class pathManager
 {
-  public $title, $pages, $custom_page, $cpage, $page, $fullpage, $page_exists, $page_id, $namespace, $nslist, $admin_tree, $wiki_mode, $page_protected, $template_cache, $external_api_page;
-  
-  /**
-   * List of custom processing functions for namespaces. This is protected so trying to do anything with it will throw an error.
-   * @access private
-   * @var array
-   */
-  
-  protected $namespace_processors;
-  
-  function __construct()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $GLOBALS['paths'] =& $this;
-    $this->pages = Array();
-    
-    // DEFINE NAMESPACES HERE
-    // The key names should NOT EVER be changed, or Enano will be very broken
-    $namespace_delimiter = ( defined('WINDOWS_MOD_REWRITE_WORKAROUNDS') ) ? '.' : ':';
-    $this->nslist = Array(
-      'Article'  => '',
-      'User'     => 'User' . $namespace_delimiter,
-      'File'     => 'File' . $namespace_delimiter,
-      'Help'     => 'Help' . $namespace_delimiter,
-      'Admin'    => 'Admin' . $namespace_delimiter,
-      'Special'  => 'Special' . $namespace_delimiter,
-      'System'   => 'Enano' . $namespace_delimiter,
-      'Template' => 'Template' . $namespace_delimiter,
-      'Category' => 'Category' . $namespace_delimiter,
-      'API'      => 'SystemAPI' . $namespace_delimiter,
-      'Project'  => sanitize_page_id(getConfig('site_name')) . $namespace_delimiter,
-      );
-    
-    // ACL types
-    // These can also be added from within plugins
-    
-    $session->register_acl_type('read',                   AUTH_ALLOW,    'perm_read');
-    $session->register_acl_type('post_comments',          AUTH_ALLOW,    'perm_post_comments',          Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('edit_comments',          AUTH_ALLOW,    'perm_edit_comments',          Array('post_comments'),                                   'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('edit_page',              AUTH_WIKIMODE, 'perm_edit_page',              Array('view_source'),                                     'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('edit_wysiwyg',           AUTH_ALLOW,    'perm_edit_wysiwyg',           Array(),                                                  'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('view_source',            AUTH_WIKIMODE, 'perm_view_source',            Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category'); // Only used if the page is protected
-    $session->register_acl_type('mod_comments',           AUTH_DISALLOW, 'perm_mod_comments',           Array('edit_comments'),                                   'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('history_view',           AUTH_WIKIMODE, 'perm_history_view',           Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('history_rollback',       AUTH_DISALLOW, 'perm_history_rollback',       Array('history_view'),                                    'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('history_rollback_extra', AUTH_DISALLOW, 'perm_history_rollback_extra', Array('history_rollback'),                                'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('protect',                AUTH_DISALLOW, 'perm_protect',                Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('rename',                 AUTH_WIKIMODE, 'perm_rename',                 Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('clear_logs',             AUTH_DISALLOW, 'perm_clear_logs',             Array('read', 'protect', 'even_when_protected'),          'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('vote_delete',            AUTH_ALLOW,    'perm_vote_delete',            Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('vote_reset',             AUTH_DISALLOW, 'perm_vote_reset',             Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('delete_page',            AUTH_DISALLOW, 'perm_delete_page',            Array(),                                                  'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('tag_create',             AUTH_ALLOW,    'perm_tag_create',             Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('tag_delete_own',         AUTH_ALLOW,    'perm_tag_delete_own',         Array('read', 'tag_create'),                              'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('tag_delete_other',       AUTH_DISALLOW, 'perm_tag_delete_other',       Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('set_wiki_mode',          AUTH_DISALLOW, 'perm_set_wiki_mode',          Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('password_set',           AUTH_DISALLOW, 'perm_password_set',           Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('password_reset',         AUTH_DISALLOW, 'perm_password_reset',         Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('mod_misc',               AUTH_DISALLOW, 'perm_mod_misc',               Array(),                                                  'All');
-    $session->register_acl_type('edit_cat',               AUTH_WIKIMODE, 'perm_edit_cat',               Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('even_when_protected',    AUTH_DISALLOW, 'perm_even_when_protected',    Array('edit_page', 'rename', 'mod_comments', 'edit_cat'), 'Article|User|Project|Template|File|Help|System|Category');
-    $session->register_acl_type('create_page',            AUTH_WIKIMODE, 'perm_create_page',            Array(),                                                  'All');
-    $session->register_acl_type('upload_files',           AUTH_DISALLOW, 'perm_upload_files',           Array('create_page'),                                     'All');
-    $session->register_acl_type('upload_new_version',     AUTH_WIKIMODE, 'perm_upload_new_version',     Array('upload_files'),                                    'All');
-    $session->register_acl_type('html_in_pages',          AUTH_DISALLOW, 'perm_html_in_pages',          Array('edit_page'),                                       'Article|User|Project|Template|File|Help|System|Category|Admin');
-    $session->register_acl_type('php_in_pages',           AUTH_DISALLOW, 'perm_php_in_pages',           Array('edit_page', 'html_in_pages'),                      'Article|User|Project|Template|File|Help|System|Category|Admin');
-    $session->register_acl_type('custom_user_title',      AUTH_DISALLOW, 'perm_custom_user_title',      Array(),                                                  'User|Special');
-    $session->register_acl_type('edit_acl',               AUTH_DISALLOW, 'perm_edit_acl',               Array());
-    
-    // DO NOT add new admin pages here! Use a plugin to call $paths->addAdminNode();
-    $this->addAdminNode('adm_cat_general',    'adm_page_general_config', 'GeneralConfig',          array(2, 2));
-    $this->addAdminNode('adm_cat_general',    'adm_page_file_uploads',   'UploadConfig',           array(2, 5));
-    $this->addAdminNode('adm_cat_general',    'adm_page_file_types',     'UploadAllowedMimeTypes', array(1, 5));
-    $this->addAdminNode('adm_cat_content',    'adm_page_manager',        'PageManager',            array(1, 4));
-    $this->addAdminNode('adm_cat_content',    'adm_page_editor',         'PageEditor',             array(3, 3));
-    $this->addAdminNode('adm_cat_content',    'adm_page_pg_groups',      'PageGroups',             array(4, 3));
-    $this->addAdminNode('adm_cat_appearance', 'adm_page_themes',         'ThemeManager',           array(4, 4));
-    $this->addAdminNode('adm_cat_appearance', 'adm_page_plugins',        'PluginManager',          array(2, 4));
-    $this->addAdminNode('adm_cat_appearance', 'adm_page_db_backup',      'DBBackup',               array(1, 2));
-    $this->addAdminNode('adm_cat_appearance', 'adm_page_lang_manager',   'LangManager',            array(1, 3));
-    $this->addAdminNode('adm_cat_appearance', 'adm_page_cache_manager',  'CacheManager',           array(3, 1));
-    $this->addAdminNode('adm_cat_users',      'adm_page_users',          'UserManager',            array(3, 5));
-    $this->addAdminNode('adm_cat_users',      'adm_page_user_groups',    'GroupManager',           array(3, 2));
-    $this->addAdminNode('adm_cat_users',      'adm_page_coppa',          'COPPA',                  array(4, 1));
-    $this->addAdminNode('adm_cat_users',      'adm_page_mass_email',     'MassEmail',              array(2, 3));
-    $this->addAdminNode('adm_cat_users',      'adm_page_user_ranks',     'UserRanks',              array(4, 5));
-    $this->addAdminNode('adm_cat_security',   'adm_page_security_log',   'SecurityLog',            array(3, 4));
-    $this->addAdminNode('adm_cat_security',   'adm_page_ban_control',    'BanControl',             array(2, 1));
-    
-    $code = $plugins->setHook('acl_rule_init');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    $this->wiki_mode = ( getConfig('wiki_mode') == '1' ) ? 1 : 0;
-    $this->template_cache = Array();
-  }
-  
-  function init($title)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $cache;
-    
-    $code = $plugins->setHook('paths_init_before');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    if ( defined('ENANO_INTERFACE_INDEX') || defined('ENANO_INTERFACE_AJAX') || defined('IN_ENANO_UPGRADE') )
-    {
-      if ( empty($title) )
-        $title = get_title();
-      
-      if ( empty($title) && !have_blank_urlname_page() )
-      {
-        $this->main_page();
-      }
-      if ( strstr($title, ' ') || strstr($title, '+') || strstr($title, '%20') )
-      {
-        $title = sanitize_page_id($title);
-        redirect(makeUrl($title), '', '', 0);
-      }
-      $title = sanitize_page_id($title);
-      // We've got the title, pull the namespace from it
-      $namespace = 'Article';
-      $page_id = $title;
-      foreach ( $this->nslist as $ns => $prefix )
-      {
-        $prefix_len = strlen($prefix);
-        if ( substr($title, 0, $prefix_len) == $prefix )
-        {
-          $page_id = substr($title, $prefix_len);
-          $namespace = $ns;
-        }
-      }
-      $this->namespace = $namespace;
-      $this->fullpage = $title;
-      if ( $namespace == 'Special' || $namespace == 'Admin' )
-      {
-        list($page_id) = explode('/', $page_id);
-      }
-      $this->page = $this->nslist[$namespace] . $page_id;
-      $this->page_id = $page_id;
-      // die("All done setting parameters. What we've got:<br/>namespace: $namespace<br/>fullpage: $this->fullpage<br/>page: $this->page<br/>page_id: $this->page_id");
-    }
-    else
-    {
-      // Starting up Enano with the API from a page that wants to do its own thing. Generate
-      // metadata for an anonymous page and avoid redirection at all costs.
-      if ( isset($GLOBALS['title']) )
-      {
-        $title =& $GLOBALS['title'];
-      }
-      else
-      {
-        $title = basename($_SERVER['SCRIPT_NAME']);
-      }
-      $base_uri = scriptPath == '' ? ltrim($_SERVER['SCRIPT_NAME'], '/') : str_replace( scriptPath . '/', '', $_SERVER['SCRIPT_NAME'] );
-      
-      $this->page = $this->nslist['API'] . sanitize_page_id($base_uri);
-      $this->fullpage = $this->nslist['API'] . sanitize_page_id($base_uri);
-      $this->namespace = 'API';
-      $this->cpage = array(
-          'name' => $title,
-          'urlname' => sanitize_page_id($base_uri),
-          'namespace' => 'API',
-          'special' => 1,
-          'visible' => 1,
-          'comments_on' => 1,
-          'protected' => 1,
-          'delvotes' => 0,
-          'delvote_ips' => '',
-          'page_format' => getConfig('default_page_format', 'wikitext')
-        );
-      $this->external_api_page = true;
-      $code = $plugins->setHook('paths_external_api_page');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-    }
-    
-    $this->page = sanitize_page_id($this->page);
-    $this->fullpage = sanitize_page_id($this->fullpage);
-    
-    if(isset($this->pages[$this->page]))
-    {
-      $this->page_exists = true;
-      $this->cpage = $this->pages[$this->page];
-      $this->page_id =& $this->cpage['urlname_nons'];
-      $this->namespace = $this->cpage['namespace'];
-      if(!isset($this->cpage['wiki_mode'])) $this->cpage['wiki_mode'] = 2;
-      
-      // Determine the wiki mode for this page, now that we have this->cpage established
-      if($this->cpage['wiki_mode'] == 2)
-      {
-        $this->wiki_mode = (int)getConfig('wiki_mode');
-      }
-      else
-      {
-        $this->wiki_mode = $this->cpage['wiki_mode'];
-      }
-      // Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
-      if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username))
-      {
-        $this->wiki_mode = true;
-      }
-      // And above all, if the site requires wiki mode to be off for non-logged-in users, disable it now
-      if(getConfig('wiki_mode_require_login')=='1' && !$session->user_logged_in)
-      {
-        $this->wiki_mode = false;
-      }
-      if($this->cpage['protected'] == 2)
-      {
-        // The page is semi-protected, determine permissions
-        if($session->user_logged_in && $session->reg_time + 60*60*24*4 < time()) 
-        {
-          $this->page_protected = 0;
-        }
-        else
-        {
-          $this->page_protected = 1;
-        }
-      }
-      else
-      {
-        $this->page_protected = $this->cpage['protected'];
-      }
-    }
-    else
-    {
-      $this->page_exists = false;
-      $page_name = dirtify_page_id($this->page);
-      $page_name = str_replace('_', ' ', $page_name);
-      
-      $pid_cleaned = sanitize_page_id($this->page);
-      if ( $pid_cleaned != $this->page )
-      {
-        redirect(makeUrl($pid_cleaned), 'Sanitizer message', 'page id sanitized', 0);
-      }
-      
-      if ( !is_array($this->cpage) )
-      {
-        $this->cpage = Array(
-          'name' => $page_name,
-          'urlname' => $this->page,
-          'namespace' => 'Article',
-          'special' => 0,
-          'visible' => 0,
-          'comments_on' => 1,
-          'protected' => 0,
-          'delvotes' => 0,
-          'delvote_ips' => '',
-          'wiki_mode' => 2,
-          'page_format' => getConfig('default_page_format', 'wikitext')
-          );
-      }
-      // Look for a namespace prefix in the urlname, and assign a different namespace, if necessary
-      $k = array_keys($this->nslist);
-      for($i=0;$i<sizeof($this->nslist);$i++)
-      {
-        $ln = strlen($this->nslist[$k[$i]]);
-        if( substr($this->page, 0, $ln) == $this->nslist[$k[$i]] )
-        {
-          $this->cpage['namespace'] = $k[$i];
-          $this->cpage['urlname_nons'] = substr($this->page, strlen($this->nslist[$this->cpage['namespace']]), strlen($this->page));
-          if(!isset($this->cpage['wiki_mode'])) 
-          {
-            $this->cpage['wiki_mode'] = 2;
-          }
-        }
-      }
-      $this->namespace = $this->cpage['namespace'];
-      $this->page_id =& $this->cpage['urlname_nons'];
-      
-      if($this->namespace=='System') 
-      {
-        $this->cpage['protected'] = 1;
-      }
-      if($this->namespace == 'Special' && !$this->external_api_page)
-      {
-        // Can't load nonexistent pages
-        if( is_string(get_main_page()) )
-        {
-          $main_page = makeUrl(get_main_page());
-        }
-        else
-        {
-          $main_page = makeUrl($this->pages[0]['urlname']);
-        }
-        redirect($main_page, $lang->get('page_msg_special_404_title'), $lang->get('page_msg_special_404_body', array('sp_link' => makeUrlNS('Special', 'SpecialPages'))), 15);
-        exit;
-      }
-      // Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
-      if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username)) 
-      {
-        $this->wiki_mode = true;
-      }
-    }
-    // This is used in the admin panel to keep track of form submission targets
-    $this->cpage['module'] = $this->cpage['urlname'];
-    
-    $this->cpage['require_admin'] = ( $this->cpage['namespace'] === 'Admin' );
-    
-    // Page is set up, call any hooks
-    $code = $plugins->setHook('page_set');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-   
-    profiler_log('Paths and CMS core initted');
-    $session->init_permissions();
-  }
-  
-  /**
-   * Fetch cdata (metadata) for a page.
-   * @param string Page ID
-   * @param string Namespace
-   * @return array
-   */
-  
-  function get_cdata($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $pathskey = $this->get_pathskey($page_id, $namespace);
-    if ( isset($this->pages[$pathskey]) )
-      return $this->pages[$pathskey];
-    
-    $page = namespace_factory($page_id, $namespace);
-    $cdata = $page->get_cdata();
-    
-    $this->pages[$pathskey] = $cdata;
-    return $cdata;
-  }
-  
-  /**
-   * For a given page ID and namespace, generate a flat string that can be used to access $paths->pages.
-   * @param string Page ID
-   * @param string Namespace
-   */
-  
-  function get_pathskey($page_id, $namespace)
-  {
-    return ( isset($this->nslist[$namespace]) ) ? "{$this->nslist[$namespace]}{$page_id}" : "{$namespace}:{$page_id}";
-  }
-  
-  function add_page($flags)
-  {
-    global $lang;
-    $flags['urlname_nons'] = $flags['urlname'];
-    $flags['urlname'] = $this->nslist[$flags['namespace']] . $flags['urlname']; // Applies the User:/File:/etc prefixes to the URL names
-    
-    if ( is_object($lang) )
-    {
-      if ( preg_match('/^[a-z0-9]+_[a-z0-9_]+$/', $flags['name']) )
-        $flags['name'] = $lang->get($flags['name']);
-    }
-    
-    $flags['require_admin'] = ( $flags['namespace'] === 'Admin' );
-    $flags['page_exists'] = true;
-    
-    $this->pages[$flags['urlname']] = $flags;
-  }
-  
-  function main_page()
-  {
-    if( is_string(get_main_page()) )
-    {
-      $main_page = makeUrl(get_main_page());
-    }
-    else
-    {
-      $main_page = makeUrl($this->pages[0]['urlname']);
-    }
-    redirect($main_page, 'Redirecting...', 'Invalid request, redirecting to main page', 0);
-    exit;
-  }
-  
-  function sysmsg($n)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    static $sm_cache = array();
-    
-    if ( isset($sm_cache[$n]) )
-      return $sm_cache[$n];
-    
-    // sometimes this gets called during die_semicritical()...
-    if ( !is_object($db) )
-      return false;
-    
-    if ( !@$db->_conn )
-      return false;
-    
-    $q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$db->escape(sanitize_page_id($n)).'\' AND namespace=\'System\'');
-    if( !$q )
-    {
-      $db->_die('Error during generic selection of system page data.');
-    }
-    if($db->numrows() < 1)
-    {
-      return false;
-      //$db->_die('Error during generic selection of system page data: there were no rows in the text table that matched the page text query.');
-    }
-    $r = $db->fetchrow();
-    $db->free_result();
-    $message = $r['page_text'];
-    
-    $message = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $message);
-    $message = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $message);
-    
-    $sm_cache[$n] = $message;
-    
-    return $message;
-  }
-  function get_pageid_from_url()
-  {
-    return get_title(true, true);
-  }
-  // Parses a (very carefully formed) array into Javascript code compatible with the Tigra Tree Menu used in the admin menu
-  function parseAdminTree() 
-  {
-    global $lang;
-    
-    $k = array_keys($this->admin_tree);
-    $i = 0;
-    $ret = '';
-    $icon = $this->make_sprite_icon(4, 2);
-    $ret .= "var TREE_ITEMS = ";
-    $tree = array(
-        array($icon . $lang->get('adm_btn_home'), "javascript:ajaxPage('{$this->nslist['Admin']}Home');")
-      );
-    
-    $root =& $tree[0];
-    
-    foreach($k as $key)
-    {
-      $i++;
-      $name = $lang->get($key);
-      $group = array($name, "javascript:trees[0].toggle($i)");
-      
-      foreach($this->admin_tree[$key] as $nodeid => $c)
-      {
-        $i++;
-        $name = $lang->get($c['name']);
-        if ( $c['icon'] && $c['icon'] != cdnPath . '/images/spacer.gif' )
-        {
-          if ( is_array($c['icon']) )
-          {
-            // this is a sprite reference
-            list($ix, $iy) = $c['icon'];
-            $icon = $this->make_sprite_icon($ix, $iy);
-          }
-          else
-          {
-            $icon = "<img alt=\"\" src=\"{$c['icon']}\" style=\"border-width: 0; margin-right: 3px;\" /> ";
-          }
-        }
-        else
-        {
-          $icon = '';
-        }
-        $group[] = array("$icon$name", "javascript:ajaxPage('{$this->nslist['Admin']}{$c['pageid']}');");
-      }
-      
-      $root[] = $group;
-    }
-    $icon = $this->make_sprite_icon(1, 1);
-    $root[] = array(
-        $icon . $lang->get('adm_btn_logout'),
-        "javascript:ajaxPage('{$this->nslist['Admin']}AdminLogout');"
-      );
-    $root[] = array(
-        "<span id=\"keepalivestat\">" . $lang->get('adm_btn_keepalive_loading') . "</span>",
-        "javascript:ajaxToggleKeepalive();",
-        array(
-            $lang->get('adm_btn_keepalive_about'),
-            "javascript:aboutKeepAlive();"
-          )
-      );
-    
-    $ret .= enano_json_encode($tree) . ';';
-    
-    // I used this while I painstakingly wrote the Runt code that auto-expands certain nodes based on the value of a bitfield stored in a cookie. *shudders*
-    // $ret .= "    ['(debug) Clear menu bitfield', 'javascript:createCookie(\\'admin_menu_state\\', \\'1\\', 365);'],\n";
-    return $ret;
-  }
-  
-  /**
-   * Internal function to generate HTML code for an icon in the admin panel tree which is sprited.
-   * @param int X index of icon
-   * @param int Y index of icon
-   * @return string
-   */
-  
-  function make_sprite_icon($ix, $iy)
-  {
-    $xpos = 16 * ( $ix - 1 );
-    $ypos = 16 * ( $iy - 1 );
-    return "<img alt=\"\" src=\"" . cdnPath . "/images/spacer.gif\" class=\"adminiconsprite\" style=\"border-width: 0; margin-right: 3px; background-position: -{$xpos}px -{$ypos}px;\" /> ";
-  }
-  
-  /**
-   * Creates a new entry in the administration panel's navigation tree.
-   * @param string Section name - if this is a language string identifier, it will be sent through $lang->get()
-   * @param string The title of the page, also may be a language string identifier
-   * @param string The page ID of the admin page, the namespace Admin is assumed
-   * @param string Optional. The path to a 16x16 image that will be displayed as the icon for this admin page
-   */
-  
-  function addAdminNode($section, $page_title, $url, $icon = false)
-  {
-    if ( !$icon )
-    {
-      $icon = cdnPath . '/images/spacer.gif';
-    }
-    if(!isset($this->admin_tree[$section]))
-    {
-      $this->admin_tree[$section] = Array();
-    }
-    $this->admin_tree[$section][] = Array(
-        'name' => $page_title,
-        'pageid' => $url,
-        'icon' => $icon
-      );
-  }
-  function getParam($id = 0)
-  {
-    $title = $this->fullpage;
-    list(, $ns) = RenderMan::strToPageID($title);
-    $title = substr($title, strlen($this->nslist[$ns]));
-    $regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$ns])) . '\\/?/';
-    $title = preg_replace($regex, '', $title);
-    $title = explode('/', $title);
-    $id = $id + 1;
-    return ( isset($title[$id]) ) ? $title[$id] : false;
-  }
-  
-  function getAllParams()
-  {
-    $title = $this->fullpage;
-    $regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$this->namespace])) . '\\/?/';
-    $title = preg_replace($regex, '', $title);
-    $title = explode('/', $title);
-    unset($title[0]);
-    return implode('/', $title);
-  }
-  
-  /**
-   * Creates a new namespace in memory
-   * @param string $id the namespace ID
-   * @param string $prefix the URL prefix, must not be blank or already used
-   * @return bool true on success false on failure
-   */
-  
-  function create_namespace($id, $prefix)
-  {
-    if(in_array($prefix, $this->nslist))
-    {
-      // echo '<b>Warning:</b> pathManager::create_namespace: Prefix "'.$prefix.'" is already taken<br />';
-      return false;
-    }
-    if( isset($this->nslist[$id]) )
-    {
-      // echo '<b>Warning:</b> pathManager::create_namespace: Namespace ID "'.$prefix.'" is already taken<br />';
-      return false;
-    }
-    $this->nslist[$id] = $prefix;
-  }
-  
-  /**
-   * Deprecated.
-   */
-  
-  function update_metadata_cache()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    return false;
-  }
-  
-  /**
-   * Takes a result row from the pages table and calculates correct values for it.
-   * @param array
-   * @return array
-   */
-  
-  function calculate_metadata_from_row($r)
-  {
-    return Namespace_Default::bake_cdata($r);
-  }
-  
-  /**
-   * Registers a handler to manually process a namespace instead of the default PageProcessor behavior.
-   * The first and only parameter passed to the processing function will be the PageProcessor instance.
-   * @param string Namespace to process
-   * @param mixed Function address. Either a function name or an array of the form array(0 => mixed (string:class name or object), 1 => string:method)
-   */
-  
-  function register_namespace_processor($namespace, $function)
-  {
-    if ( isset($this->namespace_processors[$namespace]) )
-    {
-      $processorname = ( is_string($this->namespace_processors[$namespace]) ) ?
-        $this->namespace_processors[$namespace] :
-        ( is_object($this->namespace_processors[$namespace][0]) ? get_class($this->namespace_processors[$namespace][0]) : $this->namespace_processors[$namespace][0] ) . '::' .
-          $this->namespace_processors[$namespace][1];
-          
-      trigger_error("Namespace \"$namespace\" is already being processed by $processorname - replacing caller", E_USER_WARNING);
-    }
-    if ( !is_string($function) )
-    {
-      if ( !is_array($function) )
-        return false;
-      if ( count($function) != 2 )
-        return false;
-      if ( !is_string($function[0]) && !is_object($function[0]) )
-        return false;
-      if ( !is_string($function[1]) )
-        return false;
-    }
-    
-    // security: don't allow Special or Admin namespaces to be overridden
-    if ( $namespace == 'Special' || $namespace == 'Admin' )
-    {
-      trigger_error("Security manager denied attempt to override processor for $namespace", E_USER_ERROR);
-    }
-    
-    $this->namespace_processors[$namespace] = $function;
-  }
-  
-  /**
-   * Returns a namespace processor if one exists, otherwise returns false.
-   * @param string Namespace
-   * @return mixed
-   */
-  
-  function get_namespace_processor($namespace)
-  {
-    return ( isset($this->namespace_processors[$namespace]) ) ? $this->namespace_processors[$namespace] : false;
-  }
-  
-  /**
-   * Fetches the page texts for searching
-   */
-   
-  function fetch_page_search_texts()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $texts = Array();
-    $q = $db->sql_query('SELECT t.page_id,t.namespace,t.page_text,t.char_tag FROM '.table_prefix.'page_text AS t
-                           LEFT JOIN '.table_prefix.'pages AS p
-                             ON t.page_id=p.urlname
-                           WHERE p.namespace=t.namespace
-                             AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' )
-                             AND p.visible=1;'); // Only indexes "visible" pages
-    
-    if( !$q )
-    {
-      return false;
-    }
-    while($row = $db->fetchrow())
-    {
-      $pid = $this->nslist[$row['namespace']] . $row['page_id'];
-      $texts[$pid] = $row['page_text'];
-    }
-    $db->free_result();
-    
-    return $texts;
-  }
-  
-  /**
-   * Generates an SQL query to grab all of the text
-   */
-   
-  function fetch_page_search_resource()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    // sha1('') returns "da39a3ee5e6b4b0d3255bfef95601890afd80709"
-    
-    $concat_column = ( ENANO_DBLAYER == 'MYSQL' ) ?
-      'CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id)' :
-      "'ns=' || t.namespace || ';pid=' || t.page_id";
-    
-    $texts = 'SELECT t.page_text, ' . $concat_column . ' AS page_idstring, t.page_id, t.namespace FROM '.table_prefix.'page_text AS t
-                           LEFT JOIN '.table_prefix.'pages AS p
-                             ON ( t.page_id=p.urlname AND t.namespace=p.namespace )
-                           WHERE p.namespace=t.namespace
-                             AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' )
-                             AND p.visible=1;'; // Only indexes "visible" pages
-    return $texts;
-  }
-  
-  /**
-   * Builds a word list for search indexing.
-   * @param string Text to index
-   * @param string Page ID of the page being indexed
-   * @param string Title of the page being indexed
-   * @return array List of words
-   */
-  
-  function calculate_word_list($text, $page_id, $page_name)
-  {
-    $page_id = dirtify_page_id($page_id);
-    $text = preg_replace('/[^a-z0-9\']/i', ' ', $text);
-    $page_id = preg_replace('/[^a-z0-9\']/i', ' ', $page_id);
-    $page_name = preg_replace('/[^a-z0-9\']/i', ' ', $page_name);
-    $text .= " $page_id $page_name";
-    $text = explode(' ', $text);
-    foreach ( $text as $i => &$word )
-    {
-      if ( strstr($word, "''") )
-        $word = preg_replace("/[']{2,}/", '', $word);
-      if ( strlen($word) < 2 )
-        unset($text[$i]);
-    }
-    $text = array_unique(array_values($text));
-    // for debugging purposes (usually XSS safe because of character stripping)
-    // echo ' ' . implode(' ', $text) . '<br />';
-    return $text;
-  }
-  
-  /**
-   * Rebuilds the site's entire search index. Considerably more exciting if run from the command line.
-   * @param bool If true, verbose output.
-   * @param bool If true, verbose + debugging output.
-   */
-  
-  function rebuild_search_index($verbose = false, $debug = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    require_once(ENANO_ROOT . '/includes/search.php');
-    
-    $progress = false;
-    if ( class_exists('ProgressBar') )
-    {
-      // CLI only.
-      $progress = new ProgressBar('Rebuilding search index: [', ']', 'Initializing...', 'green', 'blue', 'white', 'yellow');
-      $verbose = false;
-      $debug = false;
-      $progress->start();
-    }
-    
-    @set_time_limit(0);
-    
-    $q = $db->sql_query('DELETE FROM ' . table_prefix . 'search_index;');
-    if ( !$q )
-      $db->_die();
-    
-    $sha1_blank = sha1('');
-    
-    //
-    // Index $pages_in_batch pages at a time
-    //
-    $pages_in_batch = 15;
-    
-    // First find out how many pages there are
-    $q = $db->sql_query('SELECT COUNT(p.urlname) AS num_pages FROM ' . table_prefix . "page_text AS t\n"
-                      . "  LEFT JOIN " . table_prefix . "pages AS p\n"
-                      . "    ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
-                      . "  WHERE ( p.password = '' OR p.password = '$sha1_blank' )\n"
-                      . "    AND ( p.visible = 1 );");
-    if ( !$q )
-      $db->_die();
-    
-    list($num_pages) = $db->fetchrow_num();
-    $num_pages = intval($num_pages);
-    $loops = ceil($num_pages / $pages_in_batch);
-    $master_word_list = array();
-    $stopwords = get_stopwords();
-    
-    for ( $j = 0; $j < $loops; )
-    {
-      $offset = $j * $pages_in_batch;
-      
-      $j++;
-      
-      if ( $verbose && $debug )
-      {
-        echo "Running indexing round $j of $loops (offset $offset)\n" . ( isset($_SERVER['REQUEST_URI']) ? '<br />' : '' );
-      }
-      
-      // this is friendly to both MySQL and PostgreSQL.
-      $texts = $db->sql_query('SELECT p.name, p.visible, t.page_id, t.namespace, t.page_text FROM ' . table_prefix . "page_text AS t\n"
-                            . "  LEFT JOIN " . table_prefix . "pages AS p\n"
-                            . "    ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
-                            . "  WHERE ( p.password = '' OR p.password = '$sha1_blank' )\n"
-                            . "    AND ( p.visible = 1 )\n"
-                            . "  LIMIT $pages_in_batch OFFSET $offset;", false);
-      if ( !$texts )
-        $db->_die();
-      
-      $k = $offset;
-      
-      if ( $row = $db->fetchrow($texts) )
-      {
-        do
-        {
-          $k++;
-          if ( $verbose )
-          {
-            $mu = memory_get_usage();
-            echo "  Indexing page $k of $num_pages: {$row['namespace']}:{$row['page_id']}";
-            if ( $debug )
-              echo ", mem = $mu...";
-            flush();
-          }
-          else if ( is_object($progress) )
-          {
-            $progress->update_text_quiet("$k/$num_pages {$row['namespace']}:{$row['page_id']}");
-            $progress->set($k, $num_pages);
-          }
-          
-          // skip this page if it's not supposed to be indexed
-          if ( $row['visible'] == 0 )
-          {
-            if ( $verbose )
-            {
-              echo "skipped";
-              if ( isset($_SERVER['REQUEST_URI']) )
-                echo '<br />';
-              echo "\n";
-            }
-            continue;
-          }
-          
-          // Indexing identifier for the page in the DB
-          $page_uniqid = "ns={$row['namespace']};pid=" . sanitize_page_id($row['page_id']);
-          $page_uniqid = $db->escape($page_uniqid);
-          
-          // List of words on the page
-          $wordlist = $this->calculate_word_list($row['page_text'], $row['page_id'], $row['name']);
-          
-          // Index calculation complete -- run inserts
-          $inserts = array();
-          foreach ( $wordlist as $word )
-          {
-            if ( in_array($word, $stopwords) || strval(intval($word)) === $word || strlen($word) < 3 )
-              continue;
-            $word_db = $db->escape($word);
-            $word_db_lc = $db->escape(strtolower($word));
-            if ( !in_array($word, $master_word_list) )
-            {
-              $inserts[] = "( '$word_db', '$word_db_lc', '$page_uniqid' )";
-            }
-            else
-            {
-              if ( $verbose && $debug )
-                echo '.';
-              $pid_col = ( ENANO_DBLAYER == 'MYSQL' ) ?
-                          "CONCAT( page_names, ',$page_uniqid' )":
-                          "page_names || ',$page_uniqid'";
-              $q = $db->sql_query('UPDATE ' . table_prefix . "search_index SET page_names = $pid_col WHERE word = '$word_db';", false);
-              if ( !$q )
-                $db->_die();
-            }
-          }
-          if ( count($inserts) > 0 )
-          {
-            if ( $verbose && $debug )
-              echo 'i';
-            $inserts = implode(",\n  ", $inserts);
-            $q = $db->sql_query('INSERT INTO ' . table_prefix . "search_index(word, word_lcase, page_names) VALUES\n  $inserts;", false);
-            if ( !$q )
-              $db->_die();
-          }
-          
-          $master_word_list = array_unique(array_merge($master_word_list, $wordlist));
-          if ( $verbose )
-          {
-            if ( isset($_SERVER['REQUEST_URI']) )
-              echo '<br />';
-            echo "\n";
-          }
-          unset($inserts, $wordlist, $page_uniqid, $word_db, $q, $word, $row);
-        }
-        while ( $row = $db->fetchrow($texts) );
-      }
-      $db->free_result($texts);
-    }
-    if ( $verbose )
-    {
-      echo "Indexing complete.";
-      if ( isset($_SERVER['REQUEST_URI']) )
-        echo '<br />';
-      echo "\n";
-    }
-    else if ( is_object($progress) )
-    {
-      $progress->update_text('Complete.');
-      $progress->end();
-    }
-    return true;
-  }
-  
-  /**
-   * Partially rebuilds the search index, removing/inserting entries only for the current page
-   * @param string $page_id
-   * @param string $namespace
-   */
-  
-  function rebuild_page_index($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    require_once(ENANO_ROOT . '/includes/search.php');
-    
-    if(!$db->sql_query('SELECT page_text FROM '.table_prefix.'page_text
-      WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';'))
-    {
-      return $db->get_error();
-    }
-    if ( $db->numrows() < 1 )
-      return 'E: No rows';
-    $idstring = $this->nslist[$namespace] . sanitize_page_id($page_id);
-    if ( !isset($this->pages[$idstring]) )
-    {
-      return 'E: Can\'t find page metadata';
-    }
-    $row = $db->fetchrow();
-    $db->free_result();
-    $search = new Searcher();
-    
-    // if the page shouldn't be indexed, send a blank set of strings to the indexing engine
-    if ( $this->pages[$idstring]['visible'] == 0 )
-    {
-      $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>''));
-    }
-    else
-    {
-      $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>$row['page_text'] . ' ' . $this->pages[$idstring]['name']));
-    }
-    
-    $new_index = $search->index;
-    
-    if ( count($search->index) == 0 )
-      // o_O
-      // nothing indexed.
-      return true;
-    
-    if ( ENANO_DBLAYER == 'MYSQL' )
-    {
-      $keys = array_keys($search->index);
-      foreach($keys as $i => $k)
-      {
-        $c =& $keys[$i];
-        $c = hexencode($c, '', '');
-      }
-      $keys = "word=0x" . implode ( " OR word=0x", $keys ) . "";
-    }
-    else
-    {
-      $keys = array_keys($search->index);
-      foreach($keys as $i => $k)
-      {
-        $c =& $keys[$i];
-        $c = $db->escape($c);
-      }
-      $keys = "word='" . implode ( "' OR word='", $keys ) . "'";
-    }
-    
-    $query = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index WHERE '.$keys.';');
-    
-    while($row = $db->fetchrow())
-    {
-      $row['word'] = rtrim($row['word'], "\0");
-      $new_index[ $row['word'] ] = $row['page_names'] . ',' . $search->index[ $row['word'] ];
-    }
-    $db->free_result();
-    
-    $db->sql_query('DELETE FROM '.table_prefix.'search_index WHERE '.$keys.';');
-    
-    $secs = Array();
-    $q = 'INSERT INTO '.table_prefix.'search_index(word,word_lcase,page_names) VALUES';
-    foreach($new_index as $word => $pages)
-    {
-      $secs[] = '(\''.$db->escape($word).'\', \'' . $db->escape(strtolower($word)) . '\', \''.$db->escape($pages).'\')';
-    }
-    $q .= implode(',', $secs);
-    unset($secs);
-    $q .= ';';
-    if(!$db->check_query($q))
-    {
-      die('BUG: PathManager::rebuild_page_index: Query rejected by SQL parser:<pre>'.$q.'</pre>');
-    }
-    $result = $db->sql_query($q);
-    if($result)
-      return true;
-    else
-      $db->_die('The search index was trying to rebuild itself when the error occured.');
-    
-  }
-  
-  /**
-   * Returns a list of groups that a given page is a member of.
-   * @param string Page ID
-   * @param string Namespace
-   * @return array
-   */
-  
-  function get_page_groups($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    static $cache = array();
-    
-    if ( count($cache) == 0 )
-    {
-      foreach ( $this->nslist as $key => $_ )
-      {
-        $cache[$key] = array();
-      }
-    }
-    
-    if ( !isset($this->nslist[$namespace]) )
-      die('$paths->get_page_groups(): HACKING ATTEMPT: namespace "'. htmlspecialchars($namespace) .'" doesn\'t exist');
-    
-    $page_id_unescaped = $paths->nslist[$namespace] .
-                         dirtify_page_id($page_id);
-    $page_id_str       = $paths->nslist[$namespace] .
-                         sanitize_page_id($page_id);
-    
-    $page_id = $db->escape(sanitize_page_id($page_id));
-    
-    if ( isset($cache[$namespace][$page_id]) )
-    {
-      return $cache[$namespace][$page_id];
-    }
-    
-    $group_list = array();
-    
-    // What linked categories have this page?
-    $q = $db->sql_unbuffered_query('SELECT g.pg_id, g.pg_type, g.pg_target FROM '.table_prefix.'page_groups AS g
-  LEFT JOIN '.table_prefix.'categories AS c
-    ON ( ( c.category_id = g.pg_target AND g.pg_type = ' . PAGE_GRP_CATLINK . ' ) OR c.category_id IS NULL )
-  LEFT JOIN '.table_prefix.'page_group_members AS m
-    ON ( ( g.pg_id = m.pg_id AND g.pg_type = ' . PAGE_GRP_NORMAL . ' ) OR ( m.pg_id IS NULL ) )
-  LEFT JOIN '.table_prefix.'tags AS t
-    ON ( ( t.tag_name = g.pg_target AND pg_type = ' . PAGE_GRP_TAGGED . ' ) OR t.tag_name IS NULL )
-  WHERE
-    ( c.page_id=\'' . $page_id . '\' AND c.namespace=\'' . $namespace . '\' ) OR
-    ( t.page_id=\'' . $page_id . '\' AND t.namespace=\'' . $namespace . '\' ) OR
-    ( m.page_id=\'' . $page_id . '\' AND m.namespace=\'' . $namespace . '\' ) OR
-    ( g.pg_type = ' . PAGE_GRP_REGEX . ' );');
-    if ( !$q )
-      $db->_die();
-    
-    while ( $row = $db->fetchrow() )
-    {
-      if ( $row['pg_type'] == PAGE_GRP_REGEX )
-      {
-        //echo "&lt;debug&gt; matching page " . htmlspecialchars($page_id_unescaped) . " against regex <tt>" . htmlspecialchars($row['pg_target']) . "</tt>.";
-        if ( @preg_match($row['pg_target'], $page_id_unescaped) || @preg_match($row['pg_target'], $page_id_str) )
-        {
-          //echo "..matched";
-          $group_list[] = $row['pg_id'];
-        }
-        //echo "<br />";
-      }
-      else
-      {
-        $group_list[] = $row['pg_id'];
-      }
-    }
-    
-    $db->free_result();
-    
-    $cache[$namespace][$page_id] = $group_list;
-    
-    return $group_list;
-    
-  }
-  
+	public $title, $pages, $custom_page, $cpage, $page, $fullpage, $page_exists, $page_id, $namespace, $nslist, $admin_tree, $wiki_mode, $page_protected, $template_cache, $external_api_page;
+	
+	/**
+ 	* List of custom processing functions for namespaces. This is protected so trying to do anything with it will throw an error.
+ 	* @access private
+ 	* @var array
+ 	*/
+	
+	protected $namespace_processors;
+	
+	function __construct()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$GLOBALS['paths'] =& $this;
+		$this->pages = Array();
+		
+		// DEFINE NAMESPACES HERE
+		// The key names should NOT EVER be changed, or Enano will be very broken
+		$namespace_delimiter = ( defined('WINDOWS_MOD_REWRITE_WORKAROUNDS') ) ? '.' : ':';
+		$this->nslist = Array(
+			'Article'  => '',
+			'User'     => 'User' . $namespace_delimiter,
+			'File'     => 'File' . $namespace_delimiter,
+			'Help'     => 'Help' . $namespace_delimiter,
+			'Admin'    => 'Admin' . $namespace_delimiter,
+			'Special'  => 'Special' . $namespace_delimiter,
+			'System'   => 'Enano' . $namespace_delimiter,
+			'Template' => 'Template' . $namespace_delimiter,
+			'Category' => 'Category' . $namespace_delimiter,
+			'API'      => 'SystemAPI' . $namespace_delimiter,
+			'Project'  => sanitize_page_id(getConfig('site_name')) . $namespace_delimiter,
+			);
+		
+		// ACL types
+		// These can also be added from within plugins
+		
+		$session->register_acl_type('read',                   AUTH_ALLOW,    'perm_read');
+		$session->register_acl_type('post_comments',          AUTH_ALLOW,    'perm_post_comments',          Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('edit_comments',          AUTH_ALLOW,    'perm_edit_comments',          Array('post_comments'),                                   'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('edit_page',              AUTH_WIKIMODE, 'perm_edit_page',              Array('view_source'),                                     'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('edit_wysiwyg',           AUTH_ALLOW,    'perm_edit_wysiwyg',           Array(),                                                  'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('view_source',            AUTH_WIKIMODE, 'perm_view_source',            Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category'); // Only used if the page is protected
+		$session->register_acl_type('mod_comments',           AUTH_DISALLOW, 'perm_mod_comments',           Array('edit_comments'),                                   'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('history_view',           AUTH_WIKIMODE, 'perm_history_view',           Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('history_rollback',       AUTH_DISALLOW, 'perm_history_rollback',       Array('history_view'),                                    'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('history_rollback_extra', AUTH_DISALLOW, 'perm_history_rollback_extra', Array('history_rollback'),                                'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('protect',                AUTH_DISALLOW, 'perm_protect',                Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('rename',                 AUTH_WIKIMODE, 'perm_rename',                 Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('clear_logs',             AUTH_DISALLOW, 'perm_clear_logs',             Array('read', 'protect', 'even_when_protected'),          'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('vote_delete',            AUTH_ALLOW,    'perm_vote_delete',            Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('vote_reset',             AUTH_DISALLOW, 'perm_vote_reset',             Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('delete_page',            AUTH_DISALLOW, 'perm_delete_page',            Array(),                                                  'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('tag_create',             AUTH_ALLOW,    'perm_tag_create',             Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('tag_delete_own',         AUTH_ALLOW,    'perm_tag_delete_own',         Array('read', 'tag_create'),                              'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('tag_delete_other',       AUTH_DISALLOW, 'perm_tag_delete_other',       Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('set_wiki_mode',          AUTH_DISALLOW, 'perm_set_wiki_mode',          Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('password_set',           AUTH_DISALLOW, 'perm_password_set',           Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('password_reset',         AUTH_DISALLOW, 'perm_password_reset',         Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('mod_misc',               AUTH_DISALLOW, 'perm_mod_misc',               Array(),                                                  'All');
+		$session->register_acl_type('edit_cat',               AUTH_WIKIMODE, 'perm_edit_cat',               Array('read'),                                            'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('even_when_protected',    AUTH_DISALLOW, 'perm_even_when_protected',    Array('edit_page', 'rename', 'mod_comments', 'edit_cat'), 'Article|User|Project|Template|File|Help|System|Category');
+		$session->register_acl_type('create_page',            AUTH_WIKIMODE, 'perm_create_page',            Array(),                                                  'All');
+		$session->register_acl_type('upload_files',           AUTH_DISALLOW, 'perm_upload_files',           Array('create_page'),                                     'All');
+		$session->register_acl_type('upload_new_version',     AUTH_WIKIMODE, 'perm_upload_new_version',     Array('upload_files'),                                    'All');
+		$session->register_acl_type('html_in_pages',          AUTH_DISALLOW, 'perm_html_in_pages',          Array('edit_page'),                                       'Article|User|Project|Template|File|Help|System|Category|Admin');
+		$session->register_acl_type('php_in_pages',           AUTH_DISALLOW, 'perm_php_in_pages',           Array('edit_page', 'html_in_pages'),                      'Article|User|Project|Template|File|Help|System|Category|Admin');
+		$session->register_acl_type('custom_user_title',      AUTH_DISALLOW, 'perm_custom_user_title',      Array(),                                                  'User|Special');
+		$session->register_acl_type('edit_acl',               AUTH_DISALLOW, 'perm_edit_acl',               Array());
+		
+		// DO NOT add new admin pages here! Use a plugin to call $paths->addAdminNode();
+		$this->addAdminNode('adm_cat_general',    'adm_page_general_config', 'GeneralConfig',          array(2, 2));
+		$this->addAdminNode('adm_cat_general',    'adm_page_file_uploads',   'UploadConfig',           array(2, 5));
+		$this->addAdminNode('adm_cat_general',    'adm_page_file_types',     'UploadAllowedMimeTypes', array(1, 5));
+		$this->addAdminNode('adm_cat_content',    'adm_page_manager',        'PageManager',            array(1, 4));
+		$this->addAdminNode('adm_cat_content',    'adm_page_editor',         'PageEditor',             array(3, 3));
+		$this->addAdminNode('adm_cat_content',    'adm_page_pg_groups',      'PageGroups',             array(4, 3));
+		$this->addAdminNode('adm_cat_appearance', 'adm_page_themes',         'ThemeManager',           array(4, 4));
+		$this->addAdminNode('adm_cat_appearance', 'adm_page_plugins',        'PluginManager',          array(2, 4));
+		$this->addAdminNode('adm_cat_appearance', 'adm_page_db_backup',      'DBBackup',               array(1, 2));
+		$this->addAdminNode('adm_cat_appearance', 'adm_page_lang_manager',   'LangManager',            array(1, 3));
+		$this->addAdminNode('adm_cat_appearance', 'adm_page_cache_manager',  'CacheManager',           array(3, 1));
+		$this->addAdminNode('adm_cat_users',      'adm_page_users',          'UserManager',            array(3, 5));
+		$this->addAdminNode('adm_cat_users',      'adm_page_user_groups',    'GroupManager',           array(3, 2));
+		$this->addAdminNode('adm_cat_users',      'adm_page_coppa',          'COPPA',                  array(4, 1));
+		$this->addAdminNode('adm_cat_users',      'adm_page_mass_email',     'MassEmail',              array(2, 3));
+		$this->addAdminNode('adm_cat_users',      'adm_page_user_ranks',     'UserRanks',              array(4, 5));
+		$this->addAdminNode('adm_cat_security',   'adm_page_security_log',   'SecurityLog',            array(3, 4));
+		$this->addAdminNode('adm_cat_security',   'adm_page_ban_control',    'BanControl',             array(2, 1));
+		
+		$code = $plugins->setHook('acl_rule_init');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		$this->wiki_mode = ( getConfig('wiki_mode') == '1' ) ? 1 : 0;
+		$this->template_cache = Array();
+	}
+	
+	function init($title)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $cache;
+		
+		$code = $plugins->setHook('paths_init_before');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		if ( defined('ENANO_INTERFACE_INDEX') || defined('ENANO_INTERFACE_AJAX') || defined('IN_ENANO_UPGRADE') )
+		{
+			if ( empty($title) )
+				$title = get_title();
+			
+			if ( empty($title) && !have_blank_urlname_page() )
+			{
+				$this->main_page();
+			}
+			if ( strstr($title, ' ') || strstr($title, '+') || strstr($title, '%20') )
+			{
+				$title = sanitize_page_id($title);
+				redirect(makeUrl($title), '', '', 0);
+			}
+			$title = sanitize_page_id($title);
+			// We've got the title, pull the namespace from it
+			$namespace = 'Article';
+			$page_id = $title;
+			foreach ( $this->nslist as $ns => $prefix )
+			{
+				$prefix_len = strlen($prefix);
+				if ( substr($title, 0, $prefix_len) == $prefix )
+				{
+					$page_id = substr($title, $prefix_len);
+					$namespace = $ns;
+				}
+			}
+			$this->namespace = $namespace;
+			$this->fullpage = $title;
+			if ( $namespace == 'Special' || $namespace == 'Admin' )
+			{
+				list($page_id) = explode('/', $page_id);
+			}
+			$this->page = $this->nslist[$namespace] . $page_id;
+			$this->page_id = $page_id;
+			// die("All done setting parameters. What we've got:<br/>namespace: $namespace<br/>fullpage: $this->fullpage<br/>page: $this->page<br/>page_id: $this->page_id");
+		}
+		else
+		{
+			// Starting up Enano with the API from a page that wants to do its own thing. Generate
+			// metadata for an anonymous page and avoid redirection at all costs.
+			if ( isset($GLOBALS['title']) )
+			{
+				$title =& $GLOBALS['title'];
+			}
+			else
+			{
+				$title = basename($_SERVER['SCRIPT_NAME']);
+			}
+			$base_uri = scriptPath == '' ? ltrim($_SERVER['SCRIPT_NAME'], '/') : str_replace( scriptPath . '/', '', $_SERVER['SCRIPT_NAME'] );
+			
+			$this->page = $this->nslist['API'] . sanitize_page_id($base_uri);
+			$this->fullpage = $this->nslist['API'] . sanitize_page_id($base_uri);
+			$this->namespace = 'API';
+			$this->cpage = array(
+					'name' => $title,
+					'urlname' => sanitize_page_id($base_uri),
+					'namespace' => 'API',
+					'special' => 1,
+					'visible' => 1,
+					'comments_on' => 1,
+					'protected' => 1,
+					'delvotes' => 0,
+					'delvote_ips' => '',
+					'page_format' => getConfig('default_page_format', 'wikitext')
+				);
+			$this->external_api_page = true;
+			$code = $plugins->setHook('paths_external_api_page');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+		}
+		
+		$this->page = sanitize_page_id($this->page);
+		$this->fullpage = sanitize_page_id($this->fullpage);
+		
+		if(isset($this->pages[$this->page]))
+		{
+			$this->page_exists = true;
+			$this->cpage = $this->pages[$this->page];
+			$this->page_id =& $this->cpage['urlname_nons'];
+			$this->namespace = $this->cpage['namespace'];
+			if(!isset($this->cpage['wiki_mode'])) $this->cpage['wiki_mode'] = 2;
+			
+			// Determine the wiki mode for this page, now that we have this->cpage established
+			if($this->cpage['wiki_mode'] == 2)
+			{
+				$this->wiki_mode = (int)getConfig('wiki_mode');
+			}
+			else
+			{
+				$this->wiki_mode = $this->cpage['wiki_mode'];
+			}
+			// Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
+			if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username))
+			{
+				$this->wiki_mode = true;
+			}
+			// And above all, if the site requires wiki mode to be off for non-logged-in users, disable it now
+			if(getConfig('wiki_mode_require_login')=='1' && !$session->user_logged_in)
+			{
+				$this->wiki_mode = false;
+			}
+			if($this->cpage['protected'] == 2)
+			{
+				// The page is semi-protected, determine permissions
+				if($session->user_logged_in && $session->reg_time + 60*60*24*4 < time()) 
+				{
+					$this->page_protected = 0;
+				}
+				else
+				{
+					$this->page_protected = 1;
+				}
+			}
+			else
+			{
+				$this->page_protected = $this->cpage['protected'];
+			}
+		}
+		else
+		{
+			$this->page_exists = false;
+			$page_name = dirtify_page_id($this->page);
+			$page_name = str_replace('_', ' ', $page_name);
+			
+			$pid_cleaned = sanitize_page_id($this->page);
+			if ( $pid_cleaned != $this->page )
+			{
+				redirect(makeUrl($pid_cleaned), 'Sanitizer message', 'page id sanitized', 0);
+			}
+			
+			if ( !is_array($this->cpage) )
+			{
+				$this->cpage = Array(
+					'name' => $page_name,
+					'urlname' => $this->page,
+					'namespace' => 'Article',
+					'special' => 0,
+					'visible' => 0,
+					'comments_on' => 1,
+					'protected' => 0,
+					'delvotes' => 0,
+					'delvote_ips' => '',
+					'wiki_mode' => 2,
+					'page_format' => getConfig('default_page_format', 'wikitext')
+					);
+			}
+			// Look for a namespace prefix in the urlname, and assign a different namespace, if necessary
+			$k = array_keys($this->nslist);
+			for($i=0;$i<sizeof($this->nslist);$i++)
+			{
+				$ln = strlen($this->nslist[$k[$i]]);
+				if( substr($this->page, 0, $ln) == $this->nslist[$k[$i]] )
+				{
+					$this->cpage['namespace'] = $k[$i];
+					$this->cpage['urlname_nons'] = substr($this->page, strlen($this->nslist[$this->cpage['namespace']]), strlen($this->page));
+					if(!isset($this->cpage['wiki_mode'])) 
+					{
+						$this->cpage['wiki_mode'] = 2;
+					}
+				}
+			}
+			$this->namespace = $this->cpage['namespace'];
+			$this->page_id =& $this->cpage['urlname_nons'];
+			
+			if($this->namespace=='System') 
+			{
+				$this->cpage['protected'] = 1;
+			}
+			if($this->namespace == 'Special' && !$this->external_api_page)
+			{
+				// Can't load nonexistent pages
+				if( is_string(get_main_page()) )
+				{
+					$main_page = makeUrl(get_main_page());
+				}
+				else
+				{
+					$main_page = makeUrl($this->pages[0]['urlname']);
+				}
+				redirect($main_page, $lang->get('page_msg_special_404_title'), $lang->get('page_msg_special_404_body', array('sp_link' => makeUrlNS('Special', 'SpecialPages'))), 15);
+				exit;
+			}
+			// Allow the user to create/modify his user page uncondtionally (admins can still protect the page)
+			if($this->page == $this->nslist['User'].str_replace(' ', '_', $session->username)) 
+			{
+				$this->wiki_mode = true;
+			}
+		}
+		// This is used in the admin panel to keep track of form submission targets
+		$this->cpage['module'] = $this->cpage['urlname'];
+		
+		$this->cpage['require_admin'] = ( $this->cpage['namespace'] === 'Admin' );
+		
+		// Page is set up, call any hooks
+		$code = $plugins->setHook('page_set');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+ 	
+		profiler_log('Paths and CMS core initted');
+		$session->init_permissions();
+	}
+	
+	/**
+ 	* Fetch cdata (metadata) for a page.
+ 	* @param string Page ID
+ 	* @param string Namespace
+ 	* @return array
+ 	*/
+	
+	function get_cdata($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$pathskey = $this->get_pathskey($page_id, $namespace);
+		if ( isset($this->pages[$pathskey]) )
+			return $this->pages[$pathskey];
+		
+		$page = namespace_factory($page_id, $namespace);
+		$cdata = $page->get_cdata();
+		
+		$this->pages[$pathskey] = $cdata;
+		return $cdata;
+	}
+	
+	/**
+ 	* For a given page ID and namespace, generate a flat string that can be used to access $paths->pages.
+ 	* @param string Page ID
+ 	* @param string Namespace
+ 	*/
+	
+	function get_pathskey($page_id, $namespace)
+	{
+		return ( isset($this->nslist[$namespace]) ) ? "{$this->nslist[$namespace]}{$page_id}" : "{$namespace}:{$page_id}";
+	}
+	
+	function add_page($flags)
+	{
+		global $lang;
+		$flags['urlname_nons'] = $flags['urlname'];
+		$flags['urlname'] = $this->nslist[$flags['namespace']] . $flags['urlname']; // Applies the User:/File:/etc prefixes to the URL names
+		
+		if ( is_object($lang) )
+		{
+			if ( preg_match('/^[a-z0-9]+_[a-z0-9_]+$/', $flags['name']) )
+				$flags['name'] = $lang->get($flags['name']);
+		}
+		
+		$flags['require_admin'] = ( $flags['namespace'] === 'Admin' );
+		$flags['page_exists'] = true;
+		
+		$this->pages[$flags['urlname']] = $flags;
+	}
+	
+	function main_page()
+	{
+		if( is_string(get_main_page()) )
+		{
+			$main_page = makeUrl(get_main_page());
+		}
+		else
+		{
+			$main_page = makeUrl($this->pages[0]['urlname']);
+		}
+		redirect($main_page, 'Redirecting...', 'Invalid request, redirecting to main page', 0);
+		exit;
+	}
+	
+	function sysmsg($n)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		static $sm_cache = array();
+		
+		if ( isset($sm_cache[$n]) )
+			return $sm_cache[$n];
+		
+		// sometimes this gets called during die_semicritical()...
+		if ( !is_object($db) )
+			return false;
+		
+		if ( !@$db->_conn )
+			return false;
+		
+		$q = $db->sql_query('SELECT page_text, char_tag FROM '.table_prefix.'page_text WHERE page_id=\''.$db->escape(sanitize_page_id($n)).'\' AND namespace=\'System\'');
+		if( !$q )
+		{
+			$db->_die('Error during generic selection of system page data.');
+		}
+		if($db->numrows() < 1)
+		{
+			return false;
+			//$db->_die('Error during generic selection of system page data: there were no rows in the text table that matched the page text query.');
+		}
+		$r = $db->fetchrow();
+		$db->free_result();
+		$message = $r['page_text'];
+		
+		$message = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $message);
+		$message = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $message);
+		
+		$sm_cache[$n] = $message;
+		
+		return $message;
+	}
+	function get_pageid_from_url()
+	{
+		return get_title(true, true);
+	}
+	// Parses a (very carefully formed) array into Javascript code compatible with the Tigra Tree Menu used in the admin menu
+	function parseAdminTree() 
+	{
+		global $lang;
+		
+		$k = array_keys($this->admin_tree);
+		$i = 0;
+		$ret = '';
+		$icon = $this->make_sprite_icon(4, 2);
+		$ret .= "var TREE_ITEMS = ";
+		$tree = array(
+				array($icon . $lang->get('adm_btn_home'), "javascript:ajaxPage('{$this->nslist['Admin']}Home');")
+			);
+		
+		$root =& $tree[0];
+		
+		foreach($k as $key)
+		{
+			$i++;
+			$name = $lang->get($key);
+			$group = array($name, "javascript:trees[0].toggle($i)");
+			
+			foreach($this->admin_tree[$key] as $nodeid => $c)
+			{
+				$i++;
+				$name = $lang->get($c['name']);
+				if ( $c['icon'] && $c['icon'] != cdnPath . '/images/spacer.gif' )
+				{
+					if ( is_array($c['icon']) )
+					{
+						// this is a sprite reference
+						list($ix, $iy) = $c['icon'];
+						$icon = $this->make_sprite_icon($ix, $iy);
+					}
+					else
+					{
+						$icon = "<img alt=\"\" src=\"{$c['icon']}\" style=\"border-width: 0; margin-right: 3px;\" /> ";
+					}
+				}
+				else
+				{
+					$icon = '';
+				}
+				$group[] = array("$icon$name", "javascript:ajaxPage('{$this->nslist['Admin']}{$c['pageid']}');");
+			}
+			
+			$root[] = $group;
+		}
+		$icon = $this->make_sprite_icon(1, 1);
+		$root[] = array(
+				$icon . $lang->get('adm_btn_logout'),
+				"javascript:ajaxPage('{$this->nslist['Admin']}AdminLogout');"
+			);
+		$root[] = array(
+				"<span id=\"keepalivestat\">" . $lang->get('adm_btn_keepalive_loading') . "</span>",
+				"javascript:ajaxToggleKeepalive();",
+				array(
+						$lang->get('adm_btn_keepalive_about'),
+						"javascript:aboutKeepAlive();"
+					)
+			);
+		
+		$ret .= enano_json_encode($tree) . ';';
+		
+		// I used this while I painstakingly wrote the Runt code that auto-expands certain nodes based on the value of a bitfield stored in a cookie. *shudders*
+		// $ret .= "    ['(debug) Clear menu bitfield', 'javascript:createCookie(\\'admin_menu_state\\', \\'1\\', 365);'],\n";
+		return $ret;
+	}
+	
+	/**
+ 	* Internal function to generate HTML code for an icon in the admin panel tree which is sprited.
+ 	* @param int X index of icon
+ 	* @param int Y index of icon
+ 	* @return string
+ 	*/
+	
+	function make_sprite_icon($ix, $iy)
+	{
+		$xpos = 16 * ( $ix - 1 );
+		$ypos = 16 * ( $iy - 1 );
+		return "<img alt=\"\" src=\"" . cdnPath . "/images/spacer.gif\" class=\"adminiconsprite\" style=\"border-width: 0; margin-right: 3px; background-position: -{$xpos}px -{$ypos}px;\" /> ";
+	}
+	
+	/**
+ 	* Creates a new entry in the administration panel's navigation tree.
+ 	* @param string Section name - if this is a language string identifier, it will be sent through $lang->get()
+ 	* @param string The title of the page, also may be a language string identifier
+ 	* @param string The page ID of the admin page, the namespace Admin is assumed
+ 	* @param string Optional. The path to a 16x16 image that will be displayed as the icon for this admin page
+ 	*/
+	
+	function addAdminNode($section, $page_title, $url, $icon = false)
+	{
+		if ( !$icon )
+		{
+			$icon = cdnPath . '/images/spacer.gif';
+		}
+		if(!isset($this->admin_tree[$section]))
+		{
+			$this->admin_tree[$section] = Array();
+		}
+		$this->admin_tree[$section][] = Array(
+				'name' => $page_title,
+				'pageid' => $url,
+				'icon' => $icon
+			);
+	}
+	function getParam($id = 0)
+	{
+		$title = $this->fullpage;
+		list(, $ns) = RenderMan::strToPageID($title);
+		$title = substr($title, strlen($this->nslist[$ns]));
+		$regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$ns])) . '\\/?/';
+		$title = preg_replace($regex, '', $title);
+		$title = explode('/', $title);
+		$id = $id + 1;
+		return ( isset($title[$id]) ) ? $title[$id] : false;
+	}
+	
+	function getAllParams()
+	{
+		$title = $this->fullpage;
+		$regex = '/^' . str_replace('/', '\\/', preg_quote($this->nslist[$this->namespace])) . '\\/?/';
+		$title = preg_replace($regex, '', $title);
+		$title = explode('/', $title);
+		unset($title[0]);
+		return implode('/', $title);
+	}
+	
+	/**
+ 	* Creates a new namespace in memory
+ 	* @param string $id the namespace ID
+ 	* @param string $prefix the URL prefix, must not be blank or already used
+ 	* @return bool true on success false on failure
+ 	*/
+	
+	function create_namespace($id, $prefix)
+	{
+		if(in_array($prefix, $this->nslist))
+		{
+			// echo '<b>Warning:</b> pathManager::create_namespace: Prefix "'.$prefix.'" is already taken<br />';
+			return false;
+		}
+		if( isset($this->nslist[$id]) )
+		{
+			// echo '<b>Warning:</b> pathManager::create_namespace: Namespace ID "'.$prefix.'" is already taken<br />';
+			return false;
+		}
+		$this->nslist[$id] = $prefix;
+	}
+	
+	/**
+ 	* Deprecated.
+ 	*/
+	
+	function update_metadata_cache()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		return false;
+	}
+	
+	/**
+ 	* Takes a result row from the pages table and calculates correct values for it.
+ 	* @param array
+ 	* @return array
+ 	*/
+	
+	function calculate_metadata_from_row($r)
+	{
+		return Namespace_Default::bake_cdata($r);
+	}
+	
+	/**
+ 	* Registers a handler to manually process a namespace instead of the default PageProcessor behavior.
+ 	* The first and only parameter passed to the processing function will be the PageProcessor instance.
+ 	* @param string Namespace to process
+ 	* @param mixed Function address. Either a function name or an array of the form array(0 => mixed (string:class name or object), 1 => string:method)
+ 	*/
+	
+	function register_namespace_processor($namespace, $function)
+	{
+		if ( isset($this->namespace_processors[$namespace]) )
+		{
+			$processorname = ( is_string($this->namespace_processors[$namespace]) ) ?
+				$this->namespace_processors[$namespace] :
+				( is_object($this->namespace_processors[$namespace][0]) ? get_class($this->namespace_processors[$namespace][0]) : $this->namespace_processors[$namespace][0] ) . '::' .
+					$this->namespace_processors[$namespace][1];
+					
+			trigger_error("Namespace \"$namespace\" is already being processed by $processorname - replacing caller", E_USER_WARNING);
+		}
+		if ( !is_string($function) )
+		{
+			if ( !is_array($function) )
+				return false;
+			if ( count($function) != 2 )
+				return false;
+			if ( !is_string($function[0]) && !is_object($function[0]) )
+				return false;
+			if ( !is_string($function[1]) )
+				return false;
+		}
+		
+		// security: don't allow Special or Admin namespaces to be overridden
+		if ( $namespace == 'Special' || $namespace == 'Admin' )
+		{
+			trigger_error("Security manager denied attempt to override processor for $namespace", E_USER_ERROR);
+		}
+		
+		$this->namespace_processors[$namespace] = $function;
+	}
+	
+	/**
+ 	* Returns a namespace processor if one exists, otherwise returns false.
+ 	* @param string Namespace
+ 	* @return mixed
+ 	*/
+	
+	function get_namespace_processor($namespace)
+	{
+		return ( isset($this->namespace_processors[$namespace]) ) ? $this->namespace_processors[$namespace] : false;
+	}
+	
+	/**
+ 	* Fetches the page texts for searching
+ 	*/
+ 	
+	function fetch_page_search_texts()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$texts = Array();
+		$q = $db->sql_query('SELECT t.page_id,t.namespace,t.page_text,t.char_tag FROM '.table_prefix.'page_text AS t
+ 													LEFT JOIN '.table_prefix.'pages AS p
+ 														ON t.page_id=p.urlname
+ 													WHERE p.namespace=t.namespace
+ 														AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' )
+ 														AND p.visible=1;'); // Only indexes "visible" pages
+		
+		if( !$q )
+		{
+			return false;
+		}
+		while($row = $db->fetchrow())
+		{
+			$pid = $this->nslist[$row['namespace']] . $row['page_id'];
+			$texts[$pid] = $row['page_text'];
+		}
+		$db->free_result();
+		
+		return $texts;
+	}
+	
+	/**
+ 	* Generates an SQL query to grab all of the text
+ 	*/
+ 	
+	function fetch_page_search_resource()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		// sha1('') returns "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+		
+		$concat_column = ( ENANO_DBLAYER == 'MYSQL' ) ?
+			'CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id)' :
+			"'ns=' || t.namespace || ';pid=' || t.page_id";
+		
+		$texts = 'SELECT t.page_text, ' . $concat_column . ' AS page_idstring, t.page_id, t.namespace FROM '.table_prefix.'page_text AS t
+ 													LEFT JOIN '.table_prefix.'pages AS p
+ 														ON ( t.page_id=p.urlname AND t.namespace=p.namespace )
+ 													WHERE p.namespace=t.namespace
+ 														AND ( p.password=\'\' OR p.password=\'da39a3ee5e6b4b0d3255bfef95601890afd80709\' )
+ 														AND p.visible=1;'; // Only indexes "visible" pages
+		return $texts;
+	}
+	
+	/**
+ 	* Builds a word list for search indexing.
+ 	* @param string Text to index
+ 	* @param string Page ID of the page being indexed
+ 	* @param string Title of the page being indexed
+ 	* @return array List of words
+ 	*/
+	
+	function calculate_word_list($text, $page_id, $page_name)
+	{
+		$page_id = dirtify_page_id($page_id);
+		$text = preg_replace('/[^a-z0-9\']/i', ' ', $text);
+		$page_id = preg_replace('/[^a-z0-9\']/i', ' ', $page_id);
+		$page_name = preg_replace('/[^a-z0-9\']/i', ' ', $page_name);
+		$text .= " $page_id $page_name";
+		$text = explode(' ', $text);
+		foreach ( $text as $i => &$word )
+		{
+			if ( strstr($word, "''") )
+				$word = preg_replace("/[']{2,}/", '', $word);
+			if ( strlen($word) < 2 )
+				unset($text[$i]);
+		}
+		$text = array_unique(array_values($text));
+		// for debugging purposes (usually XSS safe because of character stripping)
+		// echo ' ' . implode(' ', $text) . '<br />';
+		return $text;
+	}
+	
+	/**
+ 	* Rebuilds the site's entire search index. Considerably more exciting if run from the command line.
+ 	* @param bool If true, verbose output.
+ 	* @param bool If true, verbose + debugging output.
+ 	*/
+	
+	function rebuild_search_index($verbose = false, $debug = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		require_once(ENANO_ROOT . '/includes/search.php');
+		
+		$progress = false;
+		if ( class_exists('ProgressBar') )
+		{
+			// CLI only.
+			$progress = new ProgressBar('Rebuilding search index: [', ']', 'Initializing...', 'green', 'blue', 'white', 'yellow');
+			$verbose = false;
+			$debug = false;
+			$progress->start();
+		}
+		
+		@set_time_limit(0);
+		
+		$q = $db->sql_query('DELETE FROM ' . table_prefix . 'search_index;');
+		if ( !$q )
+			$db->_die();
+		
+		$sha1_blank = sha1('');
+		
+		//
+		// Index $pages_in_batch pages at a time
+		//
+		$pages_in_batch = 15;
+		
+		// First find out how many pages there are
+		$q = $db->sql_query('SELECT COUNT(p.urlname) AS num_pages FROM ' . table_prefix . "page_text AS t\n"
+											. "  LEFT JOIN " . table_prefix . "pages AS p\n"
+											. "    ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
+											. "  WHERE ( p.password = '' OR p.password = '$sha1_blank' )\n"
+											. "    AND ( p.visible = 1 );");
+		if ( !$q )
+			$db->_die();
+		
+		list($num_pages) = $db->fetchrow_num();
+		$num_pages = intval($num_pages);
+		$loops = ceil($num_pages / $pages_in_batch);
+		$master_word_list = array();
+		$stopwords = get_stopwords();
+		
+		for ( $j = 0; $j < $loops; )
+		{
+			$offset = $j * $pages_in_batch;
+			
+			$j++;
+			
+			if ( $verbose && $debug )
+			{
+				echo "Running indexing round $j of $loops (offset $offset)\n" . ( isset($_SERVER['REQUEST_URI']) ? '<br />' : '' );
+			}
+			
+			// this is friendly to both MySQL and PostgreSQL.
+			$texts = $db->sql_query('SELECT p.name, p.visible, t.page_id, t.namespace, t.page_text FROM ' . table_prefix . "page_text AS t\n"
+														. "  LEFT JOIN " . table_prefix . "pages AS p\n"
+														. "    ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
+														. "  WHERE ( p.password = '' OR p.password = '$sha1_blank' )\n"
+														. "    AND ( p.visible = 1 )\n"
+														. "  LIMIT $pages_in_batch OFFSET $offset;", false);
+			if ( !$texts )
+				$db->_die();
+			
+			$k = $offset;
+			
+			if ( $row = $db->fetchrow($texts) )
+			{
+				do
+				{
+					$k++;
+					if ( $verbose )
+					{
+						$mu = memory_get_usage();
+						echo "  Indexing page $k of $num_pages: {$row['namespace']}:{$row['page_id']}";
+						if ( $debug )
+							echo ", mem = $mu...";
+						flush();
+					}
+					else if ( is_object($progress) )
+					{
+						$progress->update_text_quiet("$k/$num_pages {$row['namespace']}:{$row['page_id']}");
+						$progress->set($k, $num_pages);
+					}
+					
+					// skip this page if it's not supposed to be indexed
+					if ( $row['visible'] == 0 )
+					{
+						if ( $verbose )
+						{
+							echo "skipped";
+							if ( isset($_SERVER['REQUEST_URI']) )
+								echo '<br />';
+							echo "\n";
+						}
+						continue;
+					}
+					
+					// Indexing identifier for the page in the DB
+					$page_uniqid = "ns={$row['namespace']};pid=" . sanitize_page_id($row['page_id']);
+					$page_uniqid = $db->escape($page_uniqid);
+					
+					// List of words on the page
+					$wordlist = $this->calculate_word_list($row['page_text'], $row['page_id'], $row['name']);
+					
+					// Index calculation complete -- run inserts
+					$inserts = array();
+					foreach ( $wordlist as $word )
+					{
+						if ( in_array($word, $stopwords) || strval(intval($word)) === $word || strlen($word) < 3 )
+							continue;
+						$word_db = $db->escape($word);
+						$word_db_lc = $db->escape(strtolower($word));
+						if ( !in_array($word, $master_word_list) )
+						{
+							$inserts[] = "( '$word_db', '$word_db_lc', '$page_uniqid' )";
+						}
+						else
+						{
+							if ( $verbose && $debug )
+								echo '.';
+							$pid_col = ( ENANO_DBLAYER == 'MYSQL' ) ?
+													"CONCAT( page_names, ',$page_uniqid' )":
+													"page_names || ',$page_uniqid'";
+							$q = $db->sql_query('UPDATE ' . table_prefix . "search_index SET page_names = $pid_col WHERE word = '$word_db';", false);
+							if ( !$q )
+								$db->_die();
+						}
+					}
+					if ( count($inserts) > 0 )
+					{
+						if ( $verbose && $debug )
+							echo 'i';
+						$inserts = implode(",\n  ", $inserts);
+						$q = $db->sql_query('INSERT INTO ' . table_prefix . "search_index(word, word_lcase, page_names) VALUES\n  $inserts;", false);
+						if ( !$q )
+							$db->_die();
+					}
+					
+					$master_word_list = array_unique(array_merge($master_word_list, $wordlist));
+					if ( $verbose )
+					{
+						if ( isset($_SERVER['REQUEST_URI']) )
+							echo '<br />';
+						echo "\n";
+					}
+					unset($inserts, $wordlist, $page_uniqid, $word_db, $q, $word, $row);
+				}
+				while ( $row = $db->fetchrow($texts) );
+			}
+			$db->free_result($texts);
+		}
+		if ( $verbose )
+		{
+			echo "Indexing complete.";
+			if ( isset($_SERVER['REQUEST_URI']) )
+				echo '<br />';
+			echo "\n";
+		}
+		else if ( is_object($progress) )
+		{
+			$progress->update_text('Complete.');
+			$progress->end();
+		}
+		return true;
+	}
+	
+	/**
+ 	* Partially rebuilds the search index, removing/inserting entries only for the current page
+ 	* @param string $page_id
+ 	* @param string $namespace
+ 	*/
+	
+	function rebuild_page_index($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		require_once(ENANO_ROOT . '/includes/search.php');
+		
+		if(!$db->sql_query('SELECT page_text FROM '.table_prefix.'page_text
+			WHERE page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\';'))
+		{
+			return $db->get_error();
+		}
+		if ( $db->numrows() < 1 )
+			return 'E: No rows';
+		$idstring = $this->nslist[$namespace] . sanitize_page_id($page_id);
+		if ( !isset($this->pages[$idstring]) )
+		{
+			return 'E: Can\'t find page metadata';
+		}
+		$row = $db->fetchrow();
+		$db->free_result();
+		$search = new Searcher();
+		
+		// if the page shouldn't be indexed, send a blank set of strings to the indexing engine
+		if ( $this->pages[$idstring]['visible'] == 0 )
+		{
+			$search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>''));
+		}
+		else
+		{
+			$search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>$row['page_text'] . ' ' . $this->pages[$idstring]['name']));
+		}
+		
+		$new_index = $search->index;
+		
+		if ( count($search->index) == 0 )
+			// o_O
+			// nothing indexed.
+			return true;
+		
+		if ( ENANO_DBLAYER == 'MYSQL' )
+		{
+			$keys = array_keys($search->index);
+			foreach($keys as $i => $k)
+			{
+				$c =& $keys[$i];
+				$c = hexencode($c, '', '');
+			}
+			$keys = "word=0x" . implode ( " OR word=0x", $keys ) . "";
+		}
+		else
+		{
+			$keys = array_keys($search->index);
+			foreach($keys as $i => $k)
+			{
+				$c =& $keys[$i];
+				$c = $db->escape($c);
+			}
+			$keys = "word='" . implode ( "' OR word='", $keys ) . "'";
+		}
+		
+		$query = $db->sql_query('SELECT word,page_names FROM '.table_prefix.'search_index WHERE '.$keys.';');
+		
+		while($row = $db->fetchrow())
+		{
+			$row['word'] = rtrim($row['word'], "\0");
+			$new_index[ $row['word'] ] = $row['page_names'] . ',' . $search->index[ $row['word'] ];
+		}
+		$db->free_result();
+		
+		$db->sql_query('DELETE FROM '.table_prefix.'search_index WHERE '.$keys.';');
+		
+		$secs = Array();
+		$q = 'INSERT INTO '.table_prefix.'search_index(word,word_lcase,page_names) VALUES';
+		foreach($new_index as $word => $pages)
+		{
+			$secs[] = '(\''.$db->escape($word).'\', \'' . $db->escape(strtolower($word)) . '\', \''.$db->escape($pages).'\')';
+		}
+		$q .= implode(',', $secs);
+		unset($secs);
+		$q .= ';';
+		if(!$db->check_query($q))
+		{
+			die('BUG: PathManager::rebuild_page_index: Query rejected by SQL parser:<pre>'.$q.'</pre>');
+		}
+		$result = $db->sql_query($q);
+		if($result)
+			return true;
+		else
+			$db->_die('The search index was trying to rebuild itself when the error occured.');
+		
+	}
+	
+	/**
+ 	* Returns a list of groups that a given page is a member of.
+ 	* @param string Page ID
+ 	* @param string Namespace
+ 	* @return array
+ 	*/
+	
+	function get_page_groups($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		static $cache = array();
+		
+		if ( count($cache) == 0 )
+		{
+			foreach ( $this->nslist as $key => $_ )
+			{
+				$cache[$key] = array();
+			}
+		}
+		
+		if ( !isset($this->nslist[$namespace]) )
+			die('$paths->get_page_groups(): HACKING ATTEMPT: namespace "'. htmlspecialchars($namespace) .'" doesn\'t exist');
+		
+		$page_id_unescaped = $paths->nslist[$namespace] .
+ 												dirtify_page_id($page_id);
+		$page_id_str       = $paths->nslist[$namespace] .
+ 												sanitize_page_id($page_id);
+		
+		$page_id = $db->escape(sanitize_page_id($page_id));
+		
+		if ( isset($cache[$namespace][$page_id]) )
+		{
+			return $cache[$namespace][$page_id];
+		}
+		
+		$group_list = array();
+		
+		// What linked categories have this page?
+		$q = $db->sql_unbuffered_query('SELECT g.pg_id, g.pg_type, g.pg_target FROM '.table_prefix.'page_groups AS g
+	LEFT JOIN '.table_prefix.'categories AS c
+		ON ( ( c.category_id = g.pg_target AND g.pg_type = ' . PAGE_GRP_CATLINK . ' ) OR c.category_id IS NULL )
+	LEFT JOIN '.table_prefix.'page_group_members AS m
+		ON ( ( g.pg_id = m.pg_id AND g.pg_type = ' . PAGE_GRP_NORMAL . ' ) OR ( m.pg_id IS NULL ) )
+	LEFT JOIN '.table_prefix.'tags AS t
+		ON ( ( t.tag_name = g.pg_target AND pg_type = ' . PAGE_GRP_TAGGED . ' ) OR t.tag_name IS NULL )
+	WHERE
+		( c.page_id=\'' . $page_id . '\' AND c.namespace=\'' . $namespace . '\' ) OR
+		( t.page_id=\'' . $page_id . '\' AND t.namespace=\'' . $namespace . '\' ) OR
+		( m.page_id=\'' . $page_id . '\' AND m.namespace=\'' . $namespace . '\' ) OR
+		( g.pg_type = ' . PAGE_GRP_REGEX . ' );');
+		if ( !$q )
+			$db->_die();
+		
+		while ( $row = $db->fetchrow() )
+		{
+			if ( $row['pg_type'] == PAGE_GRP_REGEX )
+			{
+				//echo "&lt;debug&gt; matching page " . htmlspecialchars($page_id_unescaped) . " against regex <tt>" . htmlspecialchars($row['pg_target']) . "</tt>.";
+				if ( @preg_match($row['pg_target'], $page_id_unescaped) || @preg_match($row['pg_target'], $page_id_str) )
+				{
+					//echo "..matched";
+					$group_list[] = $row['pg_id'];
+				}
+				//echo "<br />";
+			}
+			else
+			{
+				$group_list[] = $row['pg_id'];
+			}
+		}
+		
+		$db->free_result();
+		
+		$cache[$namespace][$page_id] = $group_list;
+		
+		return $group_list;
+		
+	}
+	
 }
 
 /**
@@ -1106,19 +1106,19 @@
 
 function register_special_page($urlname, $name, $visible = true)
 {
-  global $paths;
-  
-  return $paths->add_page(Array(
-      'name' => $name,
-      'urlname' => $urlname,
-      'namespace' => 'Special',
-      'special' => 0,
-      'visible' => $visible ? 1 : 0,
-      'comments_on' => 0,
-      'protected' => 1,
-      'delvotes' => 0,
-      'delvote_ips' => '',
-    ));
+	global $paths;
+	
+	return $paths->add_page(Array(
+			'name' => $name,
+			'urlname' => $urlname,
+			'namespace' => 'Special',
+			'special' => 0,
+			'visible' => $visible ? 1 : 0,
+			'comments_on' => 0,
+			'protected' => 1,
+			'delvotes' => 0,
+			'delvote_ips' => '',
+		));
 }
 
 ?>
--- a/includes/plugins.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/plugins.php	Sun Mar 28 23:10:46 2010 -0400
@@ -20,1111 +20,1111 @@
  */
 
 class pluginLoader {
-  
-  /**
-   * The list of hooks registered.
-   * @var array
-   * @access private
-   */
-  
-  var $hook_list;
-  
-  /**
-   * The list of plugins that should be loaded. Used only by common.php.
-   * @var array
-   * @access private
-   */
-  
-  var $load_list;
-  
-  /**
-   * The list of plugins that are loaded currently. This is only used by the loaded() method which in turn is
-   * used by template files with the <!-- IFPLUGIN --> special tag.
-   * @var array
-   * @access private
-   */
-  
-  var $loaded_plugins;
-  
-  /**
-   * The list of plugins that are always loaded because they're part of the Enano core. This cannot be modified
-   * by any external code because user plugins are loaded after the load_list is calculated. Can be useful in
-   * alternative administration panel frameworks that need the list of system plugins.
-   * @var array
-   */
-  
-  var $system_plugins = Array('SpecialUserFuncs.php','SpecialUserPrefs.php','SpecialPageFuncs.php','SpecialAdmin.php','SpecialCSS.php','SpecialUpdownload.php','SpecialSearch.php','PrivateMessages.php','SpecialGroups.php', 'SpecialLog.php', 'DemoMode.php');
-  
-  /**
-   * Name kept for compatibility. Effectively a constructor. Calculates the list of plugins that should be loaded
-   * and puts that list in the $load_list property. Plugin developers have absolutely no use for this whatsoever.
-   */
-  
-  function loadAll() 
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $GLOBALS['plugins_cache'] = array();
-    
-    // if we're in an upgrade, just skip this step
-    if ( defined('IN_ENANO_UPGRADE') )
-    {
-      $this->load_list = array();
-      return false;
-    }
-    
-    $dir = ENANO_ROOT.'/plugins/';
-    
-    $plugin_list = $this->get_plugin_list();
-    $this->load_list = array();
-  
-    foreach ( $plugin_list as $filename => $data )
-    {
-      if ( !$data['system plugin'] && ( $data['status'] & PLUGIN_OUTOFDATE || $data['status'] & PLUGIN_DISABLED || !$data['installed'] ) )
-        continue;
-      
-      $this->load_list[] = $filename;
-      $this->loaded_plugins[$filename] = $data;
-    }
-  }
-  
-  /**
-   * Name kept for compatibility. This method is used to add a new hook into the code somewhere. Plugins are encouraged
-   * to set hooks and hook into other plugins in a fail-safe way, this encourages reuse of code. Returns an array, whose
-   * values should be eval'ed.
-   * @example <code>
-   $code = $plugins->setHook('my_hook_name');
-   foreach ( $code as $cmd )
-   {
-     eval($cmd);
-   }
-   </code>
-   * @param string The name of the hook.
-   * @param array Deprecated.
-   */
-  
-  function setHook($name, $dont_split = false)
-  {
-    if ( !empty($this->hook_list[$name]) && is_array($this->hook_list[$name]) )
-    {
-      if ( $dont_split )
-        return $this->hook_list[$name];
-      
-      return array(implode("\n", $this->hook_list[$name]));
-    }
-    else
-    {
-      return Array();
-    }
-  }
-  
-  /**
-   * Attaches to a hook effectively scheduling some code to be run at that point. You should try to keep hooks clean by
-   * making a function that has variables that need to be modified passed by reference.
-   * @example Simple example: <code>
-   $plugins->attachHook('render_wikiformat_pre', '$text = str_replace("Goodbye, Mr. Chips", "Hello, Mr. Carrots", $text);');
-   </code>
-   * @example More complicated example: <code>
-   $plugins->attachHook('render_wikiformat_pre', 'myplugin_parser_ext($text);');
-   
-   // Notice that $text is passed by reference.
-   function myplugin_parser_ext(&$text)
-   {
-     $text = str_replace("Goodbye, Mr. Chips", "Hello, Mr. Carrots", $text);
-   }
-   </code>
-   */
-  
-  function attachHook($name, $code)
-  {
-    if ( !isset($this->hook_list[$name]) )
-    {
-      $this->hook_list[$name] = Array();
-    }
-    $this->hook_list[$name][] = $code;
-  }
-  
-  /**
-   * Tell whether a plugin is loaded or not.
-   * @param string The filename of the plugin
-   * @return bool
-   */
-  
-  function loaded($plugid)
-  {
-    return isset( $this->loaded_plugins[$plugid] );
-  }
-  
-  /**
-   * Parses all special comment blocks in a plugin and returns an array in the format:
-   <code>
-   array(
-       0 => array(
-           'block' => 'upgrade',
-           // parsed from the block's parameters section
-             'release_from' => '1.0b1',
-             'release_to' => '1.0b2',
-           'value' => 'foo'
-         ),
-       1 => array(
-           ...
-         )
-     );
-   </code>
-   * @param string Path to plugin file
-   * @param string Optional. The type of block to fetch. If this is specified, only the block type specified will be read, all others will be discarded.
-   * @return array
-   */
-  
-  public static function parse_plugin_blocks($file, $type = false)
-  {
-    if ( !file_exists($file) )
-    {
-      return array();
-    }
-    $blocks = array();
-    $contents = @file_get_contents($file);
-    if ( empty($contents) )
-    {
-      return array();
-    }
-    
-    // The "\r?" in this regular expression is the result of an embarrassing failure during a job related meeting.
-    // I was demoing the authoring of an Enano plugin and simply could not figure out why the plugin would not
-    // show up in the admin panel.
+	
+	/**
+ 	* The list of hooks registered.
+ 	* @var array
+ 	* @access private
+ 	*/
+	
+	var $hook_list;
+	
+	/**
+ 	* The list of plugins that should be loaded. Used only by common.php.
+ 	* @var array
+ 	* @access private
+ 	*/
+	
+	var $load_list;
+	
+	/**
+ 	* The list of plugins that are loaded currently. This is only used by the loaded() method which in turn is
+ 	* used by template files with the <!-- IFPLUGIN --> special tag.
+ 	* @var array
+ 	* @access private
+ 	*/
+	
+	var $loaded_plugins;
+	
+	/**
+ 	* The list of plugins that are always loaded because they're part of the Enano core. This cannot be modified
+ 	* by any external code because user plugins are loaded after the load_list is calculated. Can be useful in
+ 	* alternative administration panel frameworks that need the list of system plugins.
+ 	* @var array
+ 	*/
+	
+	var $system_plugins = Array('SpecialUserFuncs.php','SpecialUserPrefs.php','SpecialPageFuncs.php','SpecialAdmin.php','SpecialCSS.php','SpecialUpdownload.php','SpecialSearch.php','PrivateMessages.php','SpecialGroups.php', 'SpecialLog.php', 'DemoMode.php');
+	
+	/**
+ 	* Name kept for compatibility. Effectively a constructor. Calculates the list of plugins that should be loaded
+ 	* and puts that list in the $load_list property. Plugin developers have absolutely no use for this whatsoever.
+ 	*/
+	
+	function loadAll() 
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$GLOBALS['plugins_cache'] = array();
+		
+		// if we're in an upgrade, just skip this step
+		if ( defined('IN_ENANO_UPGRADE') )
+		{
+			$this->load_list = array();
+			return false;
+		}
+		
+		$dir = ENANO_ROOT.'/plugins/';
+		
+		$plugin_list = $this->get_plugin_list();
+		$this->load_list = array();
+	
+		foreach ( $plugin_list as $filename => $data )
+		{
+			if ( !$data['system plugin'] && ( $data['status'] & PLUGIN_OUTOFDATE || $data['status'] & PLUGIN_DISABLED || !$data['installed'] ) )
+				continue;
+			
+			$this->load_list[] = $filename;
+			$this->loaded_plugins[$filename] = $data;
+		}
+	}
+	
+	/**
+ 	* Name kept for compatibility. This method is used to add a new hook into the code somewhere. Plugins are encouraged
+ 	* to set hooks and hook into other plugins in a fail-safe way, this encourages reuse of code. Returns an array, whose
+ 	* values should be eval'ed.
+ 	* @example <code>
+ 	$code = $plugins->setHook('my_hook_name');
+ 	foreach ( $code as $cmd )
+ 	{
+ 		eval($cmd);
+ 	}
+ 	</code>
+ 	* @param string The name of the hook.
+ 	* @param array Deprecated.
+ 	*/
+	
+	function setHook($name, $dont_split = false)
+	{
+		if ( !empty($this->hook_list[$name]) && is_array($this->hook_list[$name]) )
+		{
+			if ( $dont_split )
+				return $this->hook_list[$name];
+			
+			return array(implode("\n", $this->hook_list[$name]));
+		}
+		else
+		{
+			return Array();
+		}
+	}
+	
+	/**
+ 	* Attaches to a hook effectively scheduling some code to be run at that point. You should try to keep hooks clean by
+ 	* making a function that has variables that need to be modified passed by reference.
+ 	* @example Simple example: <code>
+ 	$plugins->attachHook('render_wikiformat_pre', '$text = str_replace("Goodbye, Mr. Chips", "Hello, Mr. Carrots", $text);');
+ 	</code>
+ 	* @example More complicated example: <code>
+ 	$plugins->attachHook('render_wikiformat_pre', 'myplugin_parser_ext($text);');
+ 	
+ 	// Notice that $text is passed by reference.
+ 	function myplugin_parser_ext(&$text)
+ 	{
+ 		$text = str_replace("Goodbye, Mr. Chips", "Hello, Mr. Carrots", $text);
+ 	}
+ 	</code>
+ 	*/
+	
+	function attachHook($name, $code)
+	{
+		if ( !isset($this->hook_list[$name]) )
+		{
+			$this->hook_list[$name] = Array();
+		}
+		$this->hook_list[$name][] = $code;
+	}
+	
+	/**
+ 	* Tell whether a plugin is loaded or not.
+ 	* @param string The filename of the plugin
+ 	* @return bool
+ 	*/
+	
+	function loaded($plugid)
+	{
+		return isset( $this->loaded_plugins[$plugid] );
+	}
+	
+	/**
+ 	* Parses all special comment blocks in a plugin and returns an array in the format:
+ 	<code>
+ 	array(
+ 			0 => array(
+ 					'block' => 'upgrade',
+ 					// parsed from the block's parameters section
+ 						'release_from' => '1.0b1',
+ 						'release_to' => '1.0b2',
+ 					'value' => 'foo'
+ 				),
+ 			1 => array(
+ 					...
+ 				)
+ 		);
+ 	</code>
+ 	* @param string Path to plugin file
+ 	* @param string Optional. The type of block to fetch. If this is specified, only the block type specified will be read, all others will be discarded.
+ 	* @return array
+ 	*/
+	
+	public static function parse_plugin_blocks($file, $type = false)
+	{
+		if ( !file_exists($file) )
+		{
+			return array();
+		}
+		$blocks = array();
+		$contents = @file_get_contents($file);
+		if ( empty($contents) )
+		{
+			return array();
+		}
+		
+		// The "\r?" in this regular expression is the result of an embarrassing failure during a job related meeting.
+		// I was demoing the authoring of an Enano plugin and simply could not figure out why the plugin would not
+		// show up in the admin panel.
 
-    $regexp = '#^/\*\*!([a-z0-9_]+)'  // block header and type
-            . '(([\s]+[a-z0-9_]+[\s]*=[\s]*".+?"[\s]*;)*)' // parameters
-            . '[\s]*\*\*' . "\r?\n"      // spacing and header close
-            . '([\w\W]+?)' . "\r?\n"     // value
-            . '\*\*!\*/'              // closing comment
-            . '#m';
-            
-    // Match out all blocks
-    $results = preg_match_all($regexp, $contents, $blocks);
-    
-    $return = array();
-    foreach ( $blocks[0] as $i => $_ )
-    {
-      if ( is_string($type) && $blocks[1][$i] !== $type )
-        continue;
-      
-      $value =& $blocks[4][$i];
-      // parse includes
-      preg_match_all('/^!include [\'"]?(.+?)[\'"]?$/m', $value, $includes);
-      foreach ( $includes[0] as $i => $replace )
-      {
-        $filename = ENANO_ROOT . '/' . $includes[1][$i];
-        if ( @file_exists( $filename ) && @is_readable( $filename ) )
-        {
-          $contents = @file_get_contents($filename);
-          $value = str_replace_once($replace, $contents, $value);
-        }
-      }
-      
-      $el = self::parse_vars($blocks[2][$i]);
-      $el['block'] = $blocks[1][$i];
-      $el['value'] = $value;
-      $return[] = $el;
-    }
-    
-    return $return;
-  }
-  
-  private static function parse_vars($var_block)
-  {
-    preg_match_all('/[\s]+([a-z0-9_]+)[\s]*=[\s]*"(.+?)";/', $var_block, $matches);
-    $return = array();
-    foreach ( $matches[0] as $i => $_ )
-    {
-      $return[ $matches[1][$i] ] = $matches[2][$i];
-    }
-    return $return;
-  }
-  
-  /**
-   * Reads all plugins in the filesystem and cross-references them with the database, providing a very complete summary of plugins
-   * on the site.
-   * @param array If specified, will restrict scanned files to this list. Defaults to null, which means all PHP files will be scanned.
-   * @param bool If true, allows using cached information. Defaults to true.
-   * @return array
-   */
-  
-  function get_plugin_list($restrict = null, $use_cache = true)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Scan all plugins
-    $plugin_list = array();
-    $ta = 0;
-    // won't load twice (failsafe automatic skip)
-    $this->load_plugins_cache();
-    global $plugins_cache;
-    if ( $use_cache && !empty($plugins_cache) )
-    {
-      return $plugins_cache;
-    }
-    else
-    {
-      // blank array - effectively skips importing the cache
-      $plugins_cache = array();
-    }
-    
-    // List all plugins
-    if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) )
-    {
-      while ( $dh = @readdir($dirh) )
-      {
-        if ( !preg_match('/\.php$/i', $dh) )
-          continue;
-        
-        if ( is_array($restrict) )
-          if ( !in_array($dh, $restrict) )
-            continue;
-          
-        // it's a PHP file, attempt to read metadata
-        $fullpath = ENANO_ROOT . "/plugins/$dh";
-        $plugin_meta = $this->read_plugin_headers($fullpath, $use_cache);
-        
-        if ( is_array($plugin_meta) )
-        {
-          // all checks passed
-          $plugin_list[$dh] = $plugin_meta;
-        }
-      }
-    }
-    
-    // Populate with additional metadata from database
-    $q = $db->sql_query('SELECT plugin_id, plugin_filename, plugin_version, plugin_flags FROM ' . table_prefix . 'plugins;');
-    if ( !$q )
-      $db->_die();
-    while ( $row = $db->fetchrow() )
-    {
-      if ( !isset($plugin_list[ $row['plugin_filename'] ]) )
-      {
-        // missing plugin file, don't report (for now)
-        continue;
-      }
-      $filename =& $row['plugin_filename'];
-      $plugin_list[$filename]['installed'] = true;
-      $plugin_list[$filename]['status'] = PLUGIN_INSTALLED;
-      $plugin_list[$filename]['plugin id'] = $row['plugin_id'];
-      if ( $row['plugin_version'] != $plugin_list[$filename]['version'] )
-      {
-        $plugin_list[$filename]['status'] |= PLUGIN_OUTOFDATE;
-        $plugin_list[$filename]['version installed'] = $row['plugin_version'];
-      }
-      if ( $row['plugin_flags'] & PLUGIN_DISABLED )
-      {
-        $plugin_list[$filename]['status'] |= PLUGIN_DISABLED;
-      }
-    }
-    $db->free_result();
-    
-    // sort it all out by filename
-    ksort($plugin_list);
-    
-    // done
-    return $plugin_list;
-  }
-  
-  /**
-   * Retrieves the metadata block from a plugin file
-   * @param string Path to plugin file (full path)
-   * @return array
-   */
-  
-  function read_plugin_headers($fullpath, $use_cache = true)
-  {
-    global $plugins_cache;
-    $dh = basename($fullpath);
-    
-    // first can we use cached info?
-    if ( isset($plugins_cache[$dh]) && $plugins_cache[$dh]['file md5'] === $this->md5_header($fullpath) )
-    {
-      $plugin_meta = $plugins_cache[$dh];
-    }
-    else
-    {
-      // the cache is out of date if we reached here -- regenerate
-      if ( $use_cache )
-        $this->generate_plugins_cache();
-      
-      // pass 1: try to read a !info block
-      $blockdata = $this->parse_plugin_blocks($fullpath, 'info');
-      if ( empty($blockdata) )
-      {
-        // no !info block, check for old header
-        $fh = @fopen($fullpath, 'r');
-        if ( !$fh )
-          // can't read, bail out
-          return false;
-        $plugin_data = array();
-        for ( $i = 0; $i < 8; $i++ )
-        {
-          $plugin_data[] = @fgets($fh, 8096);
-        }
-        // close our file handle
-        fclose($fh);
-        // is the header correct?
-        if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' )
-        {
-          // nope. get out.
-          return false;
-        }
-        // parse all the variables
-        $plugin_meta = array();
-        for ( $i = 2; $i <= 7; $i++ )
-        {
-          if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) )
-            return false;
-          $plugin_meta[ strtolower($match[1]) ] = $match[2];
-        }
-      }
-      else
-      {
-        // parse JSON block
-        $plugin_data =& $blockdata[0]['value'];
-        $plugin_data = enano_clean_json(enano_trim_json($plugin_data));
-        try
-        {
-          $plugin_meta_uc = enano_json_decode($plugin_data);
-        }
-        catch ( Exception $e )
-        {
-          return false;
-        }
-        // convert all the keys to lowercase
-        $plugin_meta = array();
-        foreach ( $plugin_meta_uc as $key => $value )
-        {
-          $plugin_meta[ strtolower($key) ] = $value;
-        }
-      }
-    }
-    if ( !isset($plugin_meta) || !is_array(@$plugin_meta) )
-    {
-      // parsing didn't work.
-      return false;
-    }
-    // check for required keys
-    $required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri');
-    foreach ( $required_keys as $key )
-    {
-      if ( !isset($plugin_meta[$key]) )
-        // not set, skip this plugin
-        return false;
-    }
-    // decide if it's a system plugin
-    $plugin_meta['system plugin'] = in_array($dh, $this->system_plugins);
-    // reset installed variable
-    $plugin_meta['installed'] = false;
-    $plugin_meta['status'] = 0;
-    
-    return $plugin_meta;
-  }
-  
-  
-  /**
-   * Attempts to cache plugin information in a file to speed fetching.
-   */
-  
-  function generate_plugins_cache()
-  {
-    if ( getConfig('cache_thumbs') != '1' )
-      return;
-    
-    // fetch the most current info
-    $plugin_info = $this->get_plugin_list(null, false);
-    foreach ( $plugin_info as $plugin => &$info )
-    {
-      $info['file md5'] = $this->md5_header(ENANO_ROOT . "/plugins/$plugin");
-    }
-    
-    $this->update_plugins_cache($plugin_info);
-    $GLOBALS['plugins_cache'] = $plugin_info;
-    
-    return true;
-  }
-  
-  /**
-   * Writes an information array to the cache file.
-   * @param array
-   * @access private
-   */
-  
-  function update_plugins_cache($plugin_info)
-  {
-    global $cache;
-    try
-    {
-      $result = $cache->store('plugins', $plugin_info, -1);
-    }
-    catch ( Exception $e )
-    {
-      return false;
-    }
-    return $result;
-  }
-  
-  /**
-   * Loads the plugins cache if any.
-   */
-  
-  function load_plugins_cache()
-  {
-    global $cache;
-    if ( $data = $cache->fetch('plugins') )
-    {
-      $GLOBALS['plugins_cache'] = $data;
-    }
-  }
-  
-  /**
-   * Calculates the MD5 sum of the first 10 lines of a file. Useful for caching plugin header information.
-   * @param string File
-   * @return string
-   */
-  
-  function md5_header($file)
-  {
-    $fh = @fopen($file, 'r');
-    if ( !$fh )
-      return false;
-    $i = 0;
-    $h = '';
-    while ( $i < 10 )
-    {
-      $line = fgets($fh, 8096);
-      $h .= $line . "\n";
-      $i++;
-    }
-    fclose($fh);
-    return md5($h);
-  }
-  
-  /**
-   * Determines if a file is an authentication extension by looking at the file contents.
-   * @param string Plugin filename
-   * @return bool
-   */
-  
-  function is_file_auth_plugin($filename)
-  {
-    $filename = ENANO_ROOT . '/plugins/' . $filename;
-    if ( !file_exists($filename) )
-      return false;
-    
-    $info = $this->read_plugin_headers($filename);
-    if ( isset($info['auth plugin']) )
-      return true;
-    
-    $contents = @file_get_contents($filename);
-    if ( strstr($contents, 'login_process_userdata_json') )
-      return true;
-    
-    return false;
-  }
-  
-  /**
-   * Installs a plugin.
-   * @param string Filename of plugin.
-   * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
-   * @return array JSON-formatted but not encoded response
-   */
-  
-  function install_plugin($filename, $plugin_list = null)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $cache;
-    
-    if ( defined('ENANO_DEMO_MODE') )
-    {
-      return array(
-          'mode' => 'error',
-          'error' => $lang->get('acppl_err_demo_mode')
-        );
-    }
-    
-    if ( !$plugin_list )
-      $plugin_list = $this->get_plugin_list();
-    
-    // we're gonna need this
-    require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
-    
-    switch ( true ): case true:
-      
-    // is the plugin in the directory and awaiting installation?
-    if ( !isset($plugin_list[$filename]) || (
-        isset($plugin_list[$filename]) && $plugin_list[$filename]['installed']
-      ))
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => 'Invalid plugin specified.',
-        'debug' => $filename
-      );
-      break;
-    }
-    
-    $dataset =& $plugin_list[$filename];
-    
-    // load up the installer schema
-    $schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'install' );
-    
-    $sql = array();
-    global $dbdriver;
-    if ( !empty($schema) )
-    {
-      // Decide which schema to use
-      $use_schema = false;
-      foreach ( $schema as $current_schema )
-      {
-        if ( isset($current_schema['dbms']) && $current_schema['dbms'] === $dbdriver )
-        {
-          $use_schema =& $current_schema['value'];
-          break;
-        }
-      }
-      if ( !$use_schema )
-      {
-        if ( !isset($schema[0]['dbms']) )
-        {
-          $use_schema =& $schema[0]['value'];
-        }
-        else
-        {
-          $return = array(
-            'mode' => 'error',
-            'error' => $lang->get('acppl_err_dmbs_not_supported', array('dbdriver' => $db->dbms_name))
-          );
-          break;
-        }
-      }
-      // parse SQL
-      $parser = new SQL_Parser($use_schema, true);
-      $parser->assign_vars(array(
-        'TABLE_PREFIX' => table_prefix
-        ));
-      $sql = $parser->parse();
-    }
-    
-    // schema is final, check queries
-    foreach ( $sql as $query )
-    {
-      if ( !$db->check_query($query) )
-      {
-        // aww crap, a query is bad
-        $return = array(
-          'mode' => 'error',
-          'error' => $lang->get('acppl_err_upgrade_bad_query'),
-        );
-        break 2;
-      }
-    }
-    
-    // this is it, perform installation
-    foreach ( $sql as $query )
-    {
-      if ( substr($query, 0, 1) == '@' )
-      {
-        $query = substr($query, 1);
-        $db->sql_query($query);
-      }
-      else
-      {
-        if ( !$db->sql_query($query) )
-        {
-          $return = array(
-              'mode' => 'error',
-              'error' => "[SQL] " . $db->sql_error()
-            );
-          break 2;
-        }
-      }
-    }
-    
-    // log action
-    $time        = time();
-    $ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
-    $username_db = $db->escape($session->username);
-    $file_db     = $db->escape($filename);
-    $q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
-                      . "  ('security', 'plugin_install', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
-    if ( !$q )
-      $db->_die();
-    
-    // register plugin
-    $version_db = $db->escape($dataset['version']);
-    $filename_db = $db->escape($filename);
-    $flags = PLUGIN_INSTALLED;
-    
-    $q = $db->sql_query('INSERT INTO ' . table_prefix . "plugins ( plugin_version, plugin_filename, plugin_flags )\n"
-                      . "  VALUES ( '$version_db', '$filename_db', $flags );");
-    if ( !$q )
-      $db->die_json();
-    
-    $plugin_list[$filename]['installed'] = true;
-    $this->reimport_plugin_strings($filename, $plugin_list);
-    
-    $return = array(
-      'success' => true
-    );
-    
-    endswitch;
-    
-    $cache->purge('plugins');
-    $cache->purge('page_meta');
-    $cache->purge('anon_sidebar');
-    
-    return $return;
-  }
-  
-  /**
-   * Uninstalls a plugin, removing it completely from the database and calling any custom uninstallation code the plugin specifies.
-   * @param string Filename of plugin.
-   * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
-   * @return array JSON-formatted but not encoded response
-   */
-  
-  function uninstall_plugin($filename, $plugin_list = null)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $cache;
-    
-    if ( defined('ENANO_DEMO_MODE') )
-    {
-      return array(
-          'mode' => 'error',
-          'error' => $lang->get('acppl_err_demo_mode')
-        );
-    }
-    
-    if ( !$plugin_list )
-      $plugin_list = $this->get_plugin_list();
-    
-    // we're gonna need this
-    require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
-    
-    switch ( true ): case true:
-    
-    // is the plugin in the directory and already installed?
-    if ( !isset($plugin_list[$filename]) || (
-        isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
-      ))
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => 'Invalid plugin specified.',
-      );
-      break;
-    }
-    // get plugin id
-    $dataset =& $plugin_list[$filename];
-    if ( empty($dataset['plugin id']) )
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => 'Couldn\'t retrieve plugin ID.',
-      );
-      break;
-    }
-    
-    // load up the installer schema
-    $schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'uninstall' );
-    
-    $sql = array();
-    global $dbdriver;
-    if ( !empty($schema) )
-    {
-      // Decide which schema to use
-      $use_schema = false;
-      foreach ( $schema as $current_schema )
-      {
-        if ( isset($current_schema['dbms']) && $current_schema['dbms'] === $dbdriver )
-        {
-          $use_schema =& $current_schema['value'];
-          break;
-        }
-      }
-      if ( !$use_schema )
-      {
-        if ( !isset($schema[0]['dbms']) )
-        {
-          $use_schema =& $schema[0]['value'];
-        }
-        else
-        {
-          $return = array(
-            'mode' => 'error',
-            'error' => $lang->get('acppl_err_dmbs_not_supported', array('dbdriver' => $db->dbms_name))
-          );
-          break;
-        }
-      }
-      // parse SQL
-      $parser = new SQL_Parser($use_schema, true);
-      $parser->assign_vars(array(
-        'TABLE_PREFIX' => table_prefix
-        ));
-      $sql = $parser->parse();
-    }
-    
-    // schema is final, check queries
-    foreach ( $sql as $query )
-    {
-      if ( !$db->check_query($query) )
-      {
-        // aww crap, a query is bad
-        $return = array(
-          'mode' => 'error',
-          'error' => $lang->get('acppl_err_upgrade_bad_query'),
-        );
-        break 2;
-      }
-    }
-    
-    // this is it, perform uninstallation
-    foreach ( $sql as $query )
-    {
-      if ( substr($query, 0, 1) == '@' )
-      {
-        $query = substr($query, 1);
-        $db->sql_query($query);
-      }
-      else
-      {
-        if ( !$db->sql_query($query) )
-        {
-          $return = array(
-              'mode' => 'error',
-              'error' => "[SQL] " . $db->sql_error()
-            );
-          break 2;
-        }
-      }
-    }
-    
-    // log action
-    $time        = time();
-    $ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
-    $username_db = $db->escape($session->username);
-    $file_db     = $db->escape($filename);
-    $q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
-                      . "  ('security', 'plugin_uninstall', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
-    if ( !$q )
-      $db->_die();
-    
-    // deregister plugin
-    $q = $db->sql_query('DELETE FROM ' . table_prefix . "plugins WHERE plugin_id = {$dataset['plugin id']};");
-    if ( !$q )
-      $db->die_json();
-    
-    $return = array(
-      'success' => true
-    );
-    
-    endswitch;
-    
-    $cache->purge('plugins');
-    $cache->purge('page_meta');
-    $cache->purge('anon_sidebar');
-    
-    return $return;
-  }
-  
-  /**
-   * Very intelligently upgrades a plugin to the version specified in the filesystem.
-   * @param string Filename of plugin.
-   * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
-   * @return array JSON-formatted but not encoded response
-   */
-  
-  function upgrade_plugin($filename, $plugin_list = null)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang, $cache;
-    
-    if ( !$plugin_list )
-      $plugin_list = $this->get_plugin_list();
-    
-    // we're gonna need this
-    require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
-    
-    switch ( true ): case true:
-    
-    // is the plugin in the directory and already installed?
-    if ( !isset($plugin_list[$filename]) || (
-        isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
-      ))
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => 'Invalid plugin specified.',
-      );
-      break;
-    }
-    // get plugin id
-    $dataset =& $plugin_list[$filename];
-    if ( empty($dataset['plugin id']) )
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => 'Couldn\'t retrieve plugin ID.',
-      );
-      break;
-    }
-    
-    //
-    // Here we go with the main upgrade process. This is the same logic that the
-    // Enano official upgrader uses, in fact it's the same SQL parser. We need
-    // list of all versions of the plugin to continue, though.
-    //
-    
-    if ( !isset($dataset['version list']) || ( isset($dataset['version list']) && !is_array($dataset['version list']) ) )
-    {
-      // no version list - update the version number but leave the rest alone
-      $version = $db->escape($dataset['version']);
-      $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};");
-      if ( !$q )
-        $db->die_json();
-      
-      // send an error and notify the user even though it was technically a success
-      $return = array(
-        'mode' => 'error',
-        'error' => $lang->get('acppl_err_upgrade_not_supported'),
-      );
-      break;
-    }
-    
-    // build target list
-    $versions  = $dataset['version list'];
-    $indices   = array_flip($versions);
-    $installed = $dataset['version installed'];
-    
-    // is the current version upgradeable?
-    if ( !isset($indices[$installed]) )
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => $lang->get('acppl_err_upgrade_bad_version'),
-      );
-      break;
-    }
-    
-    // does the plugin support upgrading to its own version?
-    if ( !isset($indices[$installed]) )
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => $lang->get('acppl_err_upgrade_bad_target_version'),
-      );
-      break;
-    }
-    
-    // list out which versions to do
-    $index_start = @$indices[$installed];
-    $index_stop  = @$indices[$dataset['version']];
-    
-    // Are we trying to go backwards?
-    if ( $index_stop <= $index_start )
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => $lang->get('acppl_err_upgrade_to_older'),
-        // 'debug' => "going from $installed ($index_start) to {$dataset['version']} ($index_stop)"
-      );
-      break;
-    }
-    
-    // build the list of version sets
-    $ver_previous = $installed;
-    $targets = array();
-    for ( $i = $index_start; $i <= $index_stop; $i++ )
-    {
-      $targets[] = array($ver_previous, $versions[$i]);
-      $ver_previous = $versions[$i];
-    }
-    
-    // parse out upgrade sections in plugin file
-    $plugin_blocks = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'upgrade' );
-    $sql_blocks = array();
-    foreach ( $plugin_blocks as $block )
-    {
-      if ( !isset($block['from']) || !isset($block['to']) )
-      {
-        continue;
-      }
-      $key = "{$block['from']} TO {$block['to']}";
-      $sql_blocks[$key] = $block['value'];
-    }
-    
-    // do version list check
-    // for now we won't fret if a specific version set isn't found, we'll just
-    // not do that version and assume there were no DB changes.
-    foreach ( $targets as $i => $target )
-    {
-      list($from, $to) = $target;
-      $key = "$from TO $to";
-      if ( !isset($sql_blocks[$key]) )
-      {
-        unset($targets[$i]);
-      }
-    }
-    $targets = array_values($targets);
-    
-    // parse and finalize schema
-    $schema = array();
-    foreach ( $targets as $i => $target )
-    {
-      list($from, $to) = $target;
-      $key = "$from TO $to";
-      try
-      {
-        $parser = new SQL_Parser($sql_blocks[$key], true);
-      }
-      catch ( Exception $e )
-      {
-        $return = array(
-          'mode' => 'error',
-          'error' => 'SQL parser init exception',
-          'debug' => "$e"
-        );
-        break 2;
-      }
-      $parser->assign_vars(array(
-        'TABLE_PREFIX' => table_prefix
-        ));
-      $parsed = $parser->parse();
-      foreach ( $parsed as $query )
-      {
-        $schema[] = $query;
-      }
-    }
-    
-    // schema is final, check queries
-    foreach ( $schema as $query )
-    {
-      if ( !$db->check_query($query) )
-      {
-        // aww crap, a query is bad
-        $return = array(
-          'mode' => 'error',
-          'error' => $lang->get('acppl_err_upgrade_bad_query'),
-        );
-        break 2;
-      }
-    }
-    
-    // this is it, perform upgrade
-    foreach ( $schema as $query )
-    {
-      if ( substr($query, 0, 1) == '@' )
-      {
-        $query = substr($query, 1);
-        $db->sql_query($query);
-      }
-      else
-      {
-        if ( !$db->sql_query($query) )
-        {
-          $return = array(
-              'mode' => 'error',
-              'error' => "[SQL] " . $db->sql_error()
-            );
-          break 2;
-        }
-      }
-    }
-    
-    // log action
-    $time        = time();
-    $ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
-    $username_db = $db->escape($session->username);
-    $file_db     = $db->escape($filename);
-    $q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
-                      . "  ('security', 'plugin_upgrade', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
-    if ( !$q )
-      $db->_die();
-    
-    // update version number
-    $version = $db->escape($dataset['version']);
-    $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};");
-    if ( !$q )
-      $db->die_json();
-    
-    // all done :-)
-    $return = array(
-      'success' => true
-    );
-    
-    endswitch;
-    
-    $cache->purge('plugins');
-    $cache->purge('page_meta');
-    $cache->purge('anon_sidebar');
-    
-    return $return;
-  }
-  
-  /**
-   * Re-imports the language strings from a plugin.
-   * @param string File name
-   * @return array Enano JSON response protocol
-   */
-  
-  function reimport_plugin_strings($filename, $plugin_list = null)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if ( !$plugin_list )
-      $plugin_list = $this->get_plugin_list();
-    
-    switch ( true ): case true:
-    
-    // is the plugin in the directory and already installed?
-    if ( !isset($plugin_list[$filename]) || (
-        isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
-      ))
-    {
-      $return = array(
-        'mode' => 'error',
-        'error' => 'Invalid plugin specified.',
-      );
-      break;
-    }
-    // get plugin data
-    $dataset =& $plugin_list[$filename];
-    
-    // check for a language block
-    $blocks = self::parse_plugin_blocks(ENANO_ROOT . '/plugins/' . $filename, 'language');
-    if ( count($blocks) < 1 )
-    {
-      return array(
-          'mode' => 'error',
-          'error' => $lang->get('acppl_err_import_no_strings')
-        );
-    }
-    
-    $result = $lang->import_plugin(ENANO_ROOT . '/plugins/' . $filename);
-    if ( $result )
-    {
-      return array(
-        'success' => true
-      );
-    }
-    else
-    {
-      return array(
-        'mode' => 'error',
-        'error' => 'Language API returned error'
-      );
-    }
-    
-    endswitch;
-  }
+		$regexp = '#^/\*\*!([a-z0-9_]+)'  // block header and type
+						. '(([\s]+[a-z0-9_]+[\s]*=[\s]*".+?"[\s]*;)*)' // parameters
+						. '[\s]*\*\*' . "\r?\n"      // spacing and header close
+						. '([\w\W]+?)' . "\r?\n"     // value
+						. '\*\*!\*/'              // closing comment
+						. '#m';
+						
+		// Match out all blocks
+		$results = preg_match_all($regexp, $contents, $blocks);
+		
+		$return = array();
+		foreach ( $blocks[0] as $i => $_ )
+		{
+			if ( is_string($type) && $blocks[1][$i] !== $type )
+				continue;
+			
+			$value =& $blocks[4][$i];
+			// parse includes
+			preg_match_all('/^!include [\'"]?(.+?)[\'"]?$/m', $value, $includes);
+			foreach ( $includes[0] as $i => $replace )
+			{
+				$filename = ENANO_ROOT . '/' . $includes[1][$i];
+				if ( @file_exists( $filename ) && @is_readable( $filename ) )
+				{
+					$contents = @file_get_contents($filename);
+					$value = str_replace_once($replace, $contents, $value);
+				}
+			}
+			
+			$el = self::parse_vars($blocks[2][$i]);
+			$el['block'] = $blocks[1][$i];
+			$el['value'] = $value;
+			$return[] = $el;
+		}
+		
+		return $return;
+	}
+	
+	private static function parse_vars($var_block)
+	{
+		preg_match_all('/[\s]+([a-z0-9_]+)[\s]*=[\s]*"(.+?)";/', $var_block, $matches);
+		$return = array();
+		foreach ( $matches[0] as $i => $_ )
+		{
+			$return[ $matches[1][$i] ] = $matches[2][$i];
+		}
+		return $return;
+	}
+	
+	/**
+ 	* Reads all plugins in the filesystem and cross-references them with the database, providing a very complete summary of plugins
+ 	* on the site.
+ 	* @param array If specified, will restrict scanned files to this list. Defaults to null, which means all PHP files will be scanned.
+ 	* @param bool If true, allows using cached information. Defaults to true.
+ 	* @return array
+ 	*/
+	
+	function get_plugin_list($restrict = null, $use_cache = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Scan all plugins
+		$plugin_list = array();
+		$ta = 0;
+		// won't load twice (failsafe automatic skip)
+		$this->load_plugins_cache();
+		global $plugins_cache;
+		if ( $use_cache && !empty($plugins_cache) )
+		{
+			return $plugins_cache;
+		}
+		else
+		{
+			// blank array - effectively skips importing the cache
+			$plugins_cache = array();
+		}
+		
+		// List all plugins
+		if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) )
+		{
+			while ( $dh = @readdir($dirh) )
+			{
+				if ( !preg_match('/\.php$/i', $dh) )
+					continue;
+				
+				if ( is_array($restrict) )
+					if ( !in_array($dh, $restrict) )
+						continue;
+					
+				// it's a PHP file, attempt to read metadata
+				$fullpath = ENANO_ROOT . "/plugins/$dh";
+				$plugin_meta = $this->read_plugin_headers($fullpath, $use_cache);
+				
+				if ( is_array($plugin_meta) )
+				{
+					// all checks passed
+					$plugin_list[$dh] = $plugin_meta;
+				}
+			}
+		}
+		
+		// Populate with additional metadata from database
+		$q = $db->sql_query('SELECT plugin_id, plugin_filename, plugin_version, plugin_flags FROM ' . table_prefix . 'plugins;');
+		if ( !$q )
+			$db->_die();
+		while ( $row = $db->fetchrow() )
+		{
+			if ( !isset($plugin_list[ $row['plugin_filename'] ]) )
+			{
+				// missing plugin file, don't report (for now)
+				continue;
+			}
+			$filename =& $row['plugin_filename'];
+			$plugin_list[$filename]['installed'] = true;
+			$plugin_list[$filename]['status'] = PLUGIN_INSTALLED;
+			$plugin_list[$filename]['plugin id'] = $row['plugin_id'];
+			if ( $row['plugin_version'] != $plugin_list[$filename]['version'] )
+			{
+				$plugin_list[$filename]['status'] |= PLUGIN_OUTOFDATE;
+				$plugin_list[$filename]['version installed'] = $row['plugin_version'];
+			}
+			if ( $row['plugin_flags'] & PLUGIN_DISABLED )
+			{
+				$plugin_list[$filename]['status'] |= PLUGIN_DISABLED;
+			}
+		}
+		$db->free_result();
+		
+		// sort it all out by filename
+		ksort($plugin_list);
+		
+		// done
+		return $plugin_list;
+	}
+	
+	/**
+ 	* Retrieves the metadata block from a plugin file
+ 	* @param string Path to plugin file (full path)
+ 	* @return array
+ 	*/
+	
+	function read_plugin_headers($fullpath, $use_cache = true)
+	{
+		global $plugins_cache;
+		$dh = basename($fullpath);
+		
+		// first can we use cached info?
+		if ( isset($plugins_cache[$dh]) && $plugins_cache[$dh]['file md5'] === $this->md5_header($fullpath) )
+		{
+			$plugin_meta = $plugins_cache[$dh];
+		}
+		else
+		{
+			// the cache is out of date if we reached here -- regenerate
+			if ( $use_cache )
+				$this->generate_plugins_cache();
+			
+			// pass 1: try to read a !info block
+			$blockdata = $this->parse_plugin_blocks($fullpath, 'info');
+			if ( empty($blockdata) )
+			{
+				// no !info block, check for old header
+				$fh = @fopen($fullpath, 'r');
+				if ( !$fh )
+					// can't read, bail out
+					return false;
+				$plugin_data = array();
+				for ( $i = 0; $i < 8; $i++ )
+				{
+					$plugin_data[] = @fgets($fh, 8096);
+				}
+				// close our file handle
+				fclose($fh);
+				// is the header correct?
+				if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' )
+				{
+					// nope. get out.
+					return false;
+				}
+				// parse all the variables
+				$plugin_meta = array();
+				for ( $i = 2; $i <= 7; $i++ )
+				{
+					if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) )
+						return false;
+					$plugin_meta[ strtolower($match[1]) ] = $match[2];
+				}
+			}
+			else
+			{
+				// parse JSON block
+				$plugin_data =& $blockdata[0]['value'];
+				$plugin_data = enano_clean_json(enano_trim_json($plugin_data));
+				try
+				{
+					$plugin_meta_uc = enano_json_decode($plugin_data);
+				}
+				catch ( Exception $e )
+				{
+					return false;
+				}
+				// convert all the keys to lowercase
+				$plugin_meta = array();
+				foreach ( $plugin_meta_uc as $key => $value )
+				{
+					$plugin_meta[ strtolower($key) ] = $value;
+				}
+			}
+		}
+		if ( !isset($plugin_meta) || !is_array(@$plugin_meta) )
+		{
+			// parsing didn't work.
+			return false;
+		}
+		// check for required keys
+		$required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri');
+		foreach ( $required_keys as $key )
+		{
+			if ( !isset($plugin_meta[$key]) )
+				// not set, skip this plugin
+				return false;
+		}
+		// decide if it's a system plugin
+		$plugin_meta['system plugin'] = in_array($dh, $this->system_plugins);
+		// reset installed variable
+		$plugin_meta['installed'] = false;
+		$plugin_meta['status'] = 0;
+		
+		return $plugin_meta;
+	}
+	
+	
+	/**
+ 	* Attempts to cache plugin information in a file to speed fetching.
+ 	*/
+	
+	function generate_plugins_cache()
+	{
+		if ( getConfig('cache_thumbs') != '1' )
+			return;
+		
+		// fetch the most current info
+		$plugin_info = $this->get_plugin_list(null, false);
+		foreach ( $plugin_info as $plugin => &$info )
+		{
+			$info['file md5'] = $this->md5_header(ENANO_ROOT . "/plugins/$plugin");
+		}
+		
+		$this->update_plugins_cache($plugin_info);
+		$GLOBALS['plugins_cache'] = $plugin_info;
+		
+		return true;
+	}
+	
+	/**
+ 	* Writes an information array to the cache file.
+ 	* @param array
+ 	* @access private
+ 	*/
+	
+	function update_plugins_cache($plugin_info)
+	{
+		global $cache;
+		try
+		{
+			$result = $cache->store('plugins', $plugin_info, -1);
+		}
+		catch ( Exception $e )
+		{
+			return false;
+		}
+		return $result;
+	}
+	
+	/**
+ 	* Loads the plugins cache if any.
+ 	*/
+	
+	function load_plugins_cache()
+	{
+		global $cache;
+		if ( $data = $cache->fetch('plugins') )
+		{
+			$GLOBALS['plugins_cache'] = $data;
+		}
+	}
+	
+	/**
+ 	* Calculates the MD5 sum of the first 10 lines of a file. Useful for caching plugin header information.
+ 	* @param string File
+ 	* @return string
+ 	*/
+	
+	function md5_header($file)
+	{
+		$fh = @fopen($file, 'r');
+		if ( !$fh )
+			return false;
+		$i = 0;
+		$h = '';
+		while ( $i < 10 )
+		{
+			$line = fgets($fh, 8096);
+			$h .= $line . "\n";
+			$i++;
+		}
+		fclose($fh);
+		return md5($h);
+	}
+	
+	/**
+ 	* Determines if a file is an authentication extension by looking at the file contents.
+ 	* @param string Plugin filename
+ 	* @return bool
+ 	*/
+	
+	function is_file_auth_plugin($filename)
+	{
+		$filename = ENANO_ROOT . '/plugins/' . $filename;
+		if ( !file_exists($filename) )
+			return false;
+		
+		$info = $this->read_plugin_headers($filename);
+		if ( isset($info['auth plugin']) )
+			return true;
+		
+		$contents = @file_get_contents($filename);
+		if ( strstr($contents, 'login_process_userdata_json') )
+			return true;
+		
+		return false;
+	}
+	
+	/**
+ 	* Installs a plugin.
+ 	* @param string Filename of plugin.
+ 	* @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
+ 	* @return array JSON-formatted but not encoded response
+ 	*/
+	
+	function install_plugin($filename, $plugin_list = null)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $cache;
+		
+		if ( defined('ENANO_DEMO_MODE') )
+		{
+			return array(
+					'mode' => 'error',
+					'error' => $lang->get('acppl_err_demo_mode')
+				);
+		}
+		
+		if ( !$plugin_list )
+			$plugin_list = $this->get_plugin_list();
+		
+		// we're gonna need this
+		require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
+		
+		switch ( true ): case true:
+			
+		// is the plugin in the directory and awaiting installation?
+		if ( !isset($plugin_list[$filename]) || (
+				isset($plugin_list[$filename]) && $plugin_list[$filename]['installed']
+			))
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => 'Invalid plugin specified.',
+				'debug' => $filename
+			);
+			break;
+		}
+		
+		$dataset =& $plugin_list[$filename];
+		
+		// load up the installer schema
+		$schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'install' );
+		
+		$sql = array();
+		global $dbdriver;
+		if ( !empty($schema) )
+		{
+			// Decide which schema to use
+			$use_schema = false;
+			foreach ( $schema as $current_schema )
+			{
+				if ( isset($current_schema['dbms']) && $current_schema['dbms'] === $dbdriver )
+				{
+					$use_schema =& $current_schema['value'];
+					break;
+				}
+			}
+			if ( !$use_schema )
+			{
+				if ( !isset($schema[0]['dbms']) )
+				{
+					$use_schema =& $schema[0]['value'];
+				}
+				else
+				{
+					$return = array(
+						'mode' => 'error',
+						'error' => $lang->get('acppl_err_dmbs_not_supported', array('dbdriver' => $db->dbms_name))
+					);
+					break;
+				}
+			}
+			// parse SQL
+			$parser = new SQL_Parser($use_schema, true);
+			$parser->assign_vars(array(
+				'TABLE_PREFIX' => table_prefix
+				));
+			$sql = $parser->parse();
+		}
+		
+		// schema is final, check queries
+		foreach ( $sql as $query )
+		{
+			if ( !$db->check_query($query) )
+			{
+				// aww crap, a query is bad
+				$return = array(
+					'mode' => 'error',
+					'error' => $lang->get('acppl_err_upgrade_bad_query'),
+				);
+				break 2;
+			}
+		}
+		
+		// this is it, perform installation
+		foreach ( $sql as $query )
+		{
+			if ( substr($query, 0, 1) == '@' )
+			{
+				$query = substr($query, 1);
+				$db->sql_query($query);
+			}
+			else
+			{
+				if ( !$db->sql_query($query) )
+				{
+					$return = array(
+							'mode' => 'error',
+							'error' => "[SQL] " . $db->sql_error()
+						);
+					break 2;
+				}
+			}
+		}
+		
+		// log action
+		$time        = time();
+		$ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
+		$username_db = $db->escape($session->username);
+		$file_db     = $db->escape($filename);
+		$q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
+											. "  ('security', 'plugin_install', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
+		if ( !$q )
+			$db->_die();
+		
+		// register plugin
+		$version_db = $db->escape($dataset['version']);
+		$filename_db = $db->escape($filename);
+		$flags = PLUGIN_INSTALLED;
+		
+		$q = $db->sql_query('INSERT INTO ' . table_prefix . "plugins ( plugin_version, plugin_filename, plugin_flags )\n"
+											. "  VALUES ( '$version_db', '$filename_db', $flags );");
+		if ( !$q )
+			$db->die_json();
+		
+		$plugin_list[$filename]['installed'] = true;
+		$this->reimport_plugin_strings($filename, $plugin_list);
+		
+		$return = array(
+			'success' => true
+		);
+		
+		endswitch;
+		
+		$cache->purge('plugins');
+		$cache->purge('page_meta');
+		$cache->purge('anon_sidebar');
+		
+		return $return;
+	}
+	
+	/**
+ 	* Uninstalls a plugin, removing it completely from the database and calling any custom uninstallation code the plugin specifies.
+ 	* @param string Filename of plugin.
+ 	* @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
+ 	* @return array JSON-formatted but not encoded response
+ 	*/
+	
+	function uninstall_plugin($filename, $plugin_list = null)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $cache;
+		
+		if ( defined('ENANO_DEMO_MODE') )
+		{
+			return array(
+					'mode' => 'error',
+					'error' => $lang->get('acppl_err_demo_mode')
+				);
+		}
+		
+		if ( !$plugin_list )
+			$plugin_list = $this->get_plugin_list();
+		
+		// we're gonna need this
+		require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
+		
+		switch ( true ): case true:
+		
+		// is the plugin in the directory and already installed?
+		if ( !isset($plugin_list[$filename]) || (
+				isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
+			))
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => 'Invalid plugin specified.',
+			);
+			break;
+		}
+		// get plugin id
+		$dataset =& $plugin_list[$filename];
+		if ( empty($dataset['plugin id']) )
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => 'Couldn\'t retrieve plugin ID.',
+			);
+			break;
+		}
+		
+		// load up the installer schema
+		$schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'uninstall' );
+		
+		$sql = array();
+		global $dbdriver;
+		if ( !empty($schema) )
+		{
+			// Decide which schema to use
+			$use_schema = false;
+			foreach ( $schema as $current_schema )
+			{
+				if ( isset($current_schema['dbms']) && $current_schema['dbms'] === $dbdriver )
+				{
+					$use_schema =& $current_schema['value'];
+					break;
+				}
+			}
+			if ( !$use_schema )
+			{
+				if ( !isset($schema[0]['dbms']) )
+				{
+					$use_schema =& $schema[0]['value'];
+				}
+				else
+				{
+					$return = array(
+						'mode' => 'error',
+						'error' => $lang->get('acppl_err_dmbs_not_supported', array('dbdriver' => $db->dbms_name))
+					);
+					break;
+				}
+			}
+			// parse SQL
+			$parser = new SQL_Parser($use_schema, true);
+			$parser->assign_vars(array(
+				'TABLE_PREFIX' => table_prefix
+				));
+			$sql = $parser->parse();
+		}
+		
+		// schema is final, check queries
+		foreach ( $sql as $query )
+		{
+			if ( !$db->check_query($query) )
+			{
+				// aww crap, a query is bad
+				$return = array(
+					'mode' => 'error',
+					'error' => $lang->get('acppl_err_upgrade_bad_query'),
+				);
+				break 2;
+			}
+		}
+		
+		// this is it, perform uninstallation
+		foreach ( $sql as $query )
+		{
+			if ( substr($query, 0, 1) == '@' )
+			{
+				$query = substr($query, 1);
+				$db->sql_query($query);
+			}
+			else
+			{
+				if ( !$db->sql_query($query) )
+				{
+					$return = array(
+							'mode' => 'error',
+							'error' => "[SQL] " . $db->sql_error()
+						);
+					break 2;
+				}
+			}
+		}
+		
+		// log action
+		$time        = time();
+		$ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
+		$username_db = $db->escape($session->username);
+		$file_db     = $db->escape($filename);
+		$q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
+											. "  ('security', 'plugin_uninstall', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
+		if ( !$q )
+			$db->_die();
+		
+		// deregister plugin
+		$q = $db->sql_query('DELETE FROM ' . table_prefix . "plugins WHERE plugin_id = {$dataset['plugin id']};");
+		if ( !$q )
+			$db->die_json();
+		
+		$return = array(
+			'success' => true
+		);
+		
+		endswitch;
+		
+		$cache->purge('plugins');
+		$cache->purge('page_meta');
+		$cache->purge('anon_sidebar');
+		
+		return $return;
+	}
+	
+	/**
+ 	* Very intelligently upgrades a plugin to the version specified in the filesystem.
+ 	* @param string Filename of plugin.
+ 	* @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
+ 	* @return array JSON-formatted but not encoded response
+ 	*/
+	
+	function upgrade_plugin($filename, $plugin_list = null)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang, $cache;
+		
+		if ( !$plugin_list )
+			$plugin_list = $this->get_plugin_list();
+		
+		// we're gonna need this
+		require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
+		
+		switch ( true ): case true:
+		
+		// is the plugin in the directory and already installed?
+		if ( !isset($plugin_list[$filename]) || (
+				isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
+			))
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => 'Invalid plugin specified.',
+			);
+			break;
+		}
+		// get plugin id
+		$dataset =& $plugin_list[$filename];
+		if ( empty($dataset['plugin id']) )
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => 'Couldn\'t retrieve plugin ID.',
+			);
+			break;
+		}
+		
+		//
+		// Here we go with the main upgrade process. This is the same logic that the
+		// Enano official upgrader uses, in fact it's the same SQL parser. We need
+		// list of all versions of the plugin to continue, though.
+		//
+		
+		if ( !isset($dataset['version list']) || ( isset($dataset['version list']) && !is_array($dataset['version list']) ) )
+		{
+			// no version list - update the version number but leave the rest alone
+			$version = $db->escape($dataset['version']);
+			$q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};");
+			if ( !$q )
+				$db->die_json();
+			
+			// send an error and notify the user even though it was technically a success
+			$return = array(
+				'mode' => 'error',
+				'error' => $lang->get('acppl_err_upgrade_not_supported'),
+			);
+			break;
+		}
+		
+		// build target list
+		$versions  = $dataset['version list'];
+		$indices   = array_flip($versions);
+		$installed = $dataset['version installed'];
+		
+		// is the current version upgradeable?
+		if ( !isset($indices[$installed]) )
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => $lang->get('acppl_err_upgrade_bad_version'),
+			);
+			break;
+		}
+		
+		// does the plugin support upgrading to its own version?
+		if ( !isset($indices[$installed]) )
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => $lang->get('acppl_err_upgrade_bad_target_version'),
+			);
+			break;
+		}
+		
+		// list out which versions to do
+		$index_start = @$indices[$installed];
+		$index_stop  = @$indices[$dataset['version']];
+		
+		// Are we trying to go backwards?
+		if ( $index_stop <= $index_start )
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => $lang->get('acppl_err_upgrade_to_older'),
+				// 'debug' => "going from $installed ($index_start) to {$dataset['version']} ($index_stop)"
+			);
+			break;
+		}
+		
+		// build the list of version sets
+		$ver_previous = $installed;
+		$targets = array();
+		for ( $i = $index_start; $i <= $index_stop; $i++ )
+		{
+			$targets[] = array($ver_previous, $versions[$i]);
+			$ver_previous = $versions[$i];
+		}
+		
+		// parse out upgrade sections in plugin file
+		$plugin_blocks = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'upgrade' );
+		$sql_blocks = array();
+		foreach ( $plugin_blocks as $block )
+		{
+			if ( !isset($block['from']) || !isset($block['to']) )
+			{
+				continue;
+			}
+			$key = "{$block['from']} TO {$block['to']}";
+			$sql_blocks[$key] = $block['value'];
+		}
+		
+		// do version list check
+		// for now we won't fret if a specific version set isn't found, we'll just
+		// not do that version and assume there were no DB changes.
+		foreach ( $targets as $i => $target )
+		{
+			list($from, $to) = $target;
+			$key = "$from TO $to";
+			if ( !isset($sql_blocks[$key]) )
+			{
+				unset($targets[$i]);
+			}
+		}
+		$targets = array_values($targets);
+		
+		// parse and finalize schema
+		$schema = array();
+		foreach ( $targets as $i => $target )
+		{
+			list($from, $to) = $target;
+			$key = "$from TO $to";
+			try
+			{
+				$parser = new SQL_Parser($sql_blocks[$key], true);
+			}
+			catch ( Exception $e )
+			{
+				$return = array(
+					'mode' => 'error',
+					'error' => 'SQL parser init exception',
+					'debug' => "$e"
+				);
+				break 2;
+			}
+			$parser->assign_vars(array(
+				'TABLE_PREFIX' => table_prefix
+				));
+			$parsed = $parser->parse();
+			foreach ( $parsed as $query )
+			{
+				$schema[] = $query;
+			}
+		}
+		
+		// schema is final, check queries
+		foreach ( $schema as $query )
+		{
+			if ( !$db->check_query($query) )
+			{
+				// aww crap, a query is bad
+				$return = array(
+					'mode' => 'error',
+					'error' => $lang->get('acppl_err_upgrade_bad_query'),
+				);
+				break 2;
+			}
+		}
+		
+		// this is it, perform upgrade
+		foreach ( $schema as $query )
+		{
+			if ( substr($query, 0, 1) == '@' )
+			{
+				$query = substr($query, 1);
+				$db->sql_query($query);
+			}
+			else
+			{
+				if ( !$db->sql_query($query) )
+				{
+					$return = array(
+							'mode' => 'error',
+							'error' => "[SQL] " . $db->sql_error()
+						);
+					break 2;
+				}
+			}
+		}
+		
+		// log action
+		$time        = time();
+		$ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
+		$username_db = $db->escape($session->username);
+		$file_db     = $db->escape($filename);
+		$q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
+											. "  ('security', 'plugin_upgrade', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
+		if ( !$q )
+			$db->_die();
+		
+		// update version number
+		$version = $db->escape($dataset['version']);
+		$q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};");
+		if ( !$q )
+			$db->die_json();
+		
+		// all done :-)
+		$return = array(
+			'success' => true
+		);
+		
+		endswitch;
+		
+		$cache->purge('plugins');
+		$cache->purge('page_meta');
+		$cache->purge('anon_sidebar');
+		
+		return $return;
+	}
+	
+	/**
+ 	* Re-imports the language strings from a plugin.
+ 	* @param string File name
+ 	* @return array Enano JSON response protocol
+ 	*/
+	
+	function reimport_plugin_strings($filename, $plugin_list = null)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if ( !$plugin_list )
+			$plugin_list = $this->get_plugin_list();
+		
+		switch ( true ): case true:
+		
+		// is the plugin in the directory and already installed?
+		if ( !isset($plugin_list[$filename]) || (
+				isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
+			))
+		{
+			$return = array(
+				'mode' => 'error',
+				'error' => 'Invalid plugin specified.',
+			);
+			break;
+		}
+		// get plugin data
+		$dataset =& $plugin_list[$filename];
+		
+		// check for a language block
+		$blocks = self::parse_plugin_blocks(ENANO_ROOT . '/plugins/' . $filename, 'language');
+		if ( count($blocks) < 1 )
+		{
+			return array(
+					'mode' => 'error',
+					'error' => $lang->get('acppl_err_import_no_strings')
+				);
+		}
+		
+		$result = $lang->import_plugin(ENANO_ROOT . '/plugins/' . $filename);
+		if ( $result )
+		{
+			return array(
+				'success' => true
+			);
+		}
+		else
+		{
+			return array(
+				'mode' => 'error',
+				'error' => 'Language API returned error'
+			);
+		}
+		
+		endswitch;
+	}
 }
 
 ?>
--- a/includes/render.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/render.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,1372 +13,1372 @@
  */
  
 class RenderMan {
-  
-  public static function strToPageID($string)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $k = array_keys($paths->nslist);
-    $proj_alt = 'Project:';
-    if ( substr($string, 0, (strlen($proj_alt))) == $proj_alt )
-    {
-      $ns = 'Project';
-      $pg = substr($string, strlen($proj_alt), strlen($string));
-      return Array($pg, $ns);
-    }
-    for($i=0;$i<sizeof($paths->nslist);$i++)
-    {
-      $ln = strlen($paths->nslist[$k[$i]]);
-      if(substr($string, 0, $ln) == $paths->nslist[$k[$i]])
-      {
-        $ns = $k[$i];
-        $pg = substr($string, strlen($paths->nslist[$ns]), strlen($string));
-      }
-    }
-    return Array($pg, $ns);
-  }
-  
-  public static function getPage($page_id, $namespace, $wiki = 1, $smilies = true, $filter_links = true, $redir = true, $render = true)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $page = new PageProcessor($page_id, $namespace);
-    $text = $page->fetch_text();
-    
-    if ( !$render )
-      return $text;
-    
-    $text = self::render($text, $wiki, $smilies, $filter_links);
-    return $text;
-  }
-  
-  public static function getTemplate($id, $parms)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( !isPage($paths->get_pathskey($id, 'Template')) ) 
-    {
-      return '[['.$paths->nslist['Template'].$id.']]';
-    }
-    if(isset($paths->template_cache[$id]))
-    {
-      $text = $paths->template_cache[$id];
-    }
-    else
-    {
-      $page = new PageProcessor($id, 'Template');
-      $text = $page->fetch_text();
-      $paths->template_cache[$id] = $text;
-    }
-    
-    $text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $text);
-    $text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $text);
-    
-    preg_match_all('#\(_([0-9]+)_\)#', $text, $matchlist);
-    
-    foreach($matchlist[1] as $m)
-    {
-      if(isset($parms[((int)$m)+1])) 
-      {
-        $p = $parms[((int)$m)+1];
-      }
-      else
-      {
-        $p = '<b>Notice:</b> RenderMan::getTemplate(): Parameter '.$m.' is not set';
-      }
-      $text = str_replace('(_'.$m.'_)', $p, $text);
-      $text = str_replace('{{' . ( $m + 1 ) . '}}', $p, $text);
-    }
-    $text = RenderMan::include_templates($text);
-    return $text;
-  }
-  
-  public static function fetch_template_text($id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $fetch_ns = 'Template';
-    if ( !isPage($paths->get_pathskey($id, 'Template')) ) 
-    {
-      // Transclusion of another page
-      // 1.1.5: Now You, Too, Can Be A Template, Even If You're Just A Plain Old Article! (TM)
-      $nssep = substr($paths->nslist['Special'], -1);
-      $nslist = $paths->nslist;
-      foreach ( $nslist as &$ns )
-      {
-        if ( $ns == '' )
-          $ns = $nssep;
-      }
-      $prefixlist = array_flip($nslist);
-      foreach ( $nslist as &$ns )
-      {
-        $ns = preg_quote($ns);
-      }
-      $nslist = implode('|', $nslist);
-      if ( preg_match("/^($nslist)(.*?)$/", $id, $match) )
-      {
-        // in practice this should always be true but just to be safe...
-        if ( isset($prefixlist[$match[1]]) )
-        {
-          $new_id = $paths->nslist[ $prefixlist[$match[1]] ] . sanitize_page_id($match[2]);
-          if ( !isPage($new_id) )
-          {
-            return "[[$new_id]]";
-          }
-          $fetch_ns = $prefixlist[$match[1]];
-          $id = sanitize_page_id($match[2]);
-        }
-      }
-      else
-      {
-        return '[['.$paths->nslist['Template'].$id.']]';
-      }
-    }
-    if(isset($paths->template_cache[$id]))
-    {
-      $text = $paths->template_cache[$id];
-    }
-    else
-    {
-      $text = RenderMan::getPage($id, $fetch_ns, 0, false, false, false, false);
-      $paths->template_cache[$id] = $text;
-    }
-    
-    if ( is_string($text) )
-    {
-      $text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $text);
-      $text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $text);
-    }
-    
-    return $text;
-  }
-  
-  /**
-   * Renders a glob of text. Note that this is PHP-safe, so if returned text (or rather, "?>" . $returned) has PHP it can be eval'ed.
-   * @param string Text to render
-   * @param int Render parameters - see constants.php
-   * @return string Rendered text
-   */
-  
-  public static function render($text, $flags = RENDER_WIKI_DEFAULT, $smilies = true)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !$smilies )
-      $flags |= RENDER_NOSMILIES;
-    
-    if ( !($flags & RENDER_NOSMILIES) )
-    {
-      $text = RenderMan::smilieyize($text);
-    }
-    if ( $flags & RENDER_WIKI_DEFAULT )
-    {
-      $text = RenderMan::next_gen_wiki_format($text, $flags);
-    }
-    else if ( $flags & RENDER_WIKI_TEMPLATE )
-    {
-      $text = $template->tplWikiFormat($text);
-    }           
-    return $text;
-  }
-  
-  private static function next_gen_wiki_format($text, $flags = 0)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    profiler_log("RenderMan: starting wikitext render");
-    require_once( ENANO_ROOT . '/includes/wikiformat.php' );
-    require_once( ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php' );
-    require_once( ENANO_ROOT . '/includes/wikiengine/Tables.php' );
-    
-    // this is still needed by parser plugins
-    $random_id = md5( time() . mt_rand() );
-    
-    // Strip out <nowiki> sections
-    self::nowiki_strip($text, $nowiki_stripped);
-    
-    // Run early parsing plugins
-    $code = $plugins->setHook('render_wikiformat_veryearly');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    // Strip out embedded PHP
-    self::php_strip($text, $php_stripped);
-    
-    // Convert newlines for the parser
-    $text = str_replace("\r\n", "\n", $text);
-    
-    // Perform render through the engine
-    $carpenter = new Carpenter();
-    $carpenter->flags = $flags;
-    $carpenter->hook(array(__CLASS__, 'hook_pre'), PO_AFTER, 'lang');
-    $carpenter->hook(array(__CLASS__, 'hook_posttemplates'), PO_AFTER, 'templates');
-    if ( $flags & RENDER_WIKI_TEMPLATE )
-    {
-      // FIXME: Where is noinclude/nodisplay being processed in the pipeline? (Seems to be processed, but not here)
-    }
-    
-    //
-    // Set rules for the rendering process
-    //
-    
-    if ( $flags & RENDER_BLOCK && !($flags & RENDER_INLINE) )
-    {
-      // block only
-      $carpenter->disable_all_rules();
-      foreach ( array('blockquote', 'tables', 'heading', 'hr', 'multilist', 'bold', 'italic', 'underline', 'paragraph', 'blockquotepost') as $rule )
-      {
-        $carpenter->enable_rule($rule);
-      }
-      
-      $code = $plugins->setHook('render_block_only');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-    }
-    else if ( $flags & RENDER_INLINE && !($flags & RENDER_BLOCK) )
-    {
-      // inline only
-      $carpenter->disable_all_rules();
-      foreach ( array('bold', 'italic', 'underline', 'externalwithtext', 'externalnotext', 'image', 'internallink') as $rule )
-      {
-        $carpenter->enable_rule($rule);
-      }
-      
-      $code = $plugins->setHook('render_inline_only');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-    }
-    else
-    {
-      // full render
-      $code = $plugins->setHook('render_full');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-    }
-    $text = $carpenter->render($text);
-    
-    // For plugin compat
-    $result =& $text;
-    
-    // Post processing hook
-    $code = $plugins->setHook('render_wikiformat_post');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    // Add PHP and nowiki back in
-    self::nowiki_unstrip($text, $nowiki_stripped);
-    self::php_unstrip($text, $php_stripped);
-    
-    profiler_log("RenderMan: finished wikitext render");
-    
-    return $text;
-  }
-  
-  public static function hook_pre($text)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $code = $plugins->setHook('render_wikiformat_pre');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    return $text;
-  }
-  
-  public static function hook_posttemplates($text)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $code = $plugins->setHook('render_wikiformat_posttemplates');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    return $text;
-  }
-  
-  /**
-   * Strip out <nowiki> tags (to bypass parsing on them)
-   * @access private
-   */
-  
-  private static function nowiki_strip(&$text, &$stripdata)
-  {
-    self::tag_strip('nowiki', $text, $stripdata);
-  }
-  
-  /**
-   * Restore stripped <nowiki> tags.
-   * @access private
-   */
-  
-  public static function nowiki_unstrip(&$text, &$stripdata)
-  {
-    self::tag_unstrip('nowiki', $text, $stripdata);
-  }
-  
-  /**
-   * Strip out an arbitrary HTML tag.
-   * @access private
-   */
-  
-  public static function tag_strip($tag, &$text, &$stripdata)
-  {
-    $random_id = md5( time() . mt_rand() );
-    
-    preg_match_all("#<$tag>(.*?)</$tag>#is", $text, $blocks);
-    
-    foreach ( $blocks[0] as $i => $match )
-    {
-      $text = str_replace($match, "{{$tag}:{$random_id}:{$i}}", $text);
-    }
-    
-    $stripdata = array(
-        'random_id' => $random_id,
-        'blocks' => $blocks[1]
-      );
-  }
-  
-  /**
-   * Strip out an arbitrary HTML tag, pushing on to the existing list of stripped data.
-   * @access private
-   */
-  
-  public static function tag_strip_push($tag, &$text, &$stripdata)
-  {
-    if ( !is_array($stripdata) )
-    {
-      $stripdata = array(
-          'random_id' => md5( time() . mt_rand() ),
-          'blocks' => array()
-        );
-    }
-    $random_id =& $stripdata['random_id'];
-    
-    preg_match_all("#<$tag>(.*?)</$tag>#is", $text, $blocks);
-    
-    foreach ( $blocks[0] as $i => $match )
-    {
-      $j = count($stripdata['blocks']);
-      $stripdata['blocks'][] = $blocks[1][$i];
-      $text = str_replace($match, "{{$tag}:{$random_id}:{$j}}", $text);
-    }
-  }
-  
-  /**
-   * Restore stripped <nowiki> tags.
-   * @access private
-   */
-  
-  public static function tag_unstrip($tag, &$text, &$stripdata, $keep = false)
-  {
-    $random_id = $stripdata['random_id'];
-    
-    foreach ( $stripdata['blocks'] as $i => $block )
-    {
-      $block = $keep ? "<$tag>$block</$tag>" : $block;
-      $text = str_replace("{{$tag}:{$random_id}:{$i}}", $block, $text);
-    }
-    
-    $stripdata = array();
-  }
-  
-  /**
-   * Strip out PHP code (to prevent it from being sent through the parser). Private because it does not do what you think it does. (The method you are looking for is strip_php.)
-   * @access private
-   */
-  
-  private static function php_strip(&$text, &$stripdata)
-  {
-    $random_id = md5( time() . mt_rand() );
-    
-    preg_match_all('#<\?(?:php)?[\s=].+?\?>#is', $text, $blocks);
-    
-    foreach ( $blocks[0] as $i => $match )
-    {
-      $text = str_replace($match, "{PHP:$random_id:$i}", $text);
-    }
-    
-    $stripdata = array(
-        'random_id' => $random_id,
-        'blocks' => $blocks[0]
-      );
-  }
-  
-  /**
-   * Restore stripped PHP code
-   * @access private
-   */
-  
-  private static function php_unstrip(&$text, &$stripdata)
-  {
-    $random_id = $stripdata['random_id'];
-    
-    foreach ( $stripdata['blocks'] as $i => $block )
-    {
-      $text = str_replace("{PHP:$random_id:$i}", $block, $text);
-    }
-    
-    $stripdata = array();
-  }
-  
-  /**
-   * Deprecated.
-   */
-  
-  public static function wikiFormat($message, $filter_links = true, $do_params = false, $plaintext = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    return RenderMan::next_gen_wiki_format($message, $plaintext, $filter_links, $do_params);
-  }
-  
-  public static function destroy_javascript($message, $_php = false)
-  {
-    $message = preg_replace('#<(script|object|applet|embed|iframe|frame|form|input|select)(.*?)>#is', '&lt;\\1\\2&gt;', $message);
-    $message = preg_replace('#</(script|object|applet|embed|iframe|frame|form|input|select)(.*?)>#is', '&lt;/\\1\\2&gt;', $message);
-    $message = preg_replace('#(javascript|script|activex|chrome|about|applet):#is', '\\1&#058;', $message);
-    if ( $_php )
-    {
-      // Left in only for compatibility
-      $message = preg_replace('#&lt;(.*?)>#is', '<\\1>', $message);
-      $message = preg_replace('#<(.*?)&gt;#is', '<\\1>', $message);
-      $message = preg_replace('#<(\?|\?php|%)(.*?)(\?|%)>#is', '&lt;\\1\\2\\3&gt;', $message);
-      // strip <a href="foo" onclick="bar();">-type attacks
-      $message = preg_replace('#<([a-zA-Z:\-]+) (.*?)on([A-Za-z]*)=(.*?)>#is', '&lt;\\1\\2on\\3=\\4&gt;', $message);
-    }
-    return $message;
-  }
-  
-  public static function strip_php($message)
-  {
-    return RenderMan::destroy_javascript($message, true);
-  }
-  
-  public static function sanitize_html($text)
-  {
-    $text = htmlspecialchars($text);
-    $allowed_tags = Array('b', 'i', 'u', 'pre', 'code', 'tt', 'br', 'p', 'nowiki', '!--([\w\W]+)--');
-    foreach($allowed_tags as $t)
-    {
-      $text = preg_replace('#&lt;'.$t.'&gt;(.*?)&lt;/'.$t.'&gt;#is', '<'.$t.'>\\1</'.$t.'>', $text);
-      $text = preg_replace('#&lt;'.$t.' /&gt;#is', '<'.$t.' />', $text);
-      $text = preg_replace('#&lt;'.$t.'&gt;#is', '<'.$t.'>', $text);
-    }
-    return $text;
-  }
-  
-  /**
-   * Reverse-renders a blob of text (converts it from XHTML back to wikitext) by using parser hints and educated guesses.
-   * @param string XHTML
-   * @return string Wikitext
-   */
-  
-  public static function reverse_render($text)
-  {
-    // convert \r\n to \n
-    $text = str_replace("\r\n", "\n", $text);
-    
-    // Separate certain block level elements onto their own lines. This tidies up the tag
-    // soup that TinyMCE sometimes produces.
-    $block_elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'table', 'ul', 'pre');
-    $block_elements = implode('|', $block_elements);
-    $regex = "#(</(?:$block_elements)>)\n?<($block_elements)(>| .+?>)#i";
-    $text = preg_replace($regex, "$1\n\n<$2$3", $text);
-    
-    $text = self::reverse_process_parser_hints($text);
-    $text = self::reverse_process_headings($text);
-    $text = self::reverse_process_lists($text);
-    $text = self::reverse_process_tables($text);
-    
-    // Lastly, strip out paragraph tags.
-    $text = preg_replace('|^ *<p>(.+?)</p> *$|m', "\\1", $text);
-    
-    return $text;
-  }
-  
-  public static function reverse_process_parser_hints($text)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !preg_match_all('|<!--#([a-z0-9_]+)(?: (.+?))?-->([\w\W]*?)<!--#/\\1-->|s', $text, $matches) )
-      return $text;
-    
-    foreach ( $matches[0] as $i => $match )
-    {
-      $tag =& $matches[1][$i];
-      $attribs =& $matches[2][$i];
-      $inner =& $matches[3][$i];
-      
-      $attribs = self::reverse_process_hint_attribs($attribs);
-      switch($tag)
-      {
-        case 'smiley':
-        case 'internallink':
-        case 'imagelink':
-          if ( isset($attribs['code']) )
-          {
-            $text = str_replace($match, $attribs['code'], $text);
-          }
-          else if ( isset($attribs['src']) )
-          {
-            $text = str_replace($match, $attribs['src'], $text);
-          }
-          break;
-      }
-    }
-    
-    return $text;
-  }
-  
-  public static function reverse_process_hint_attribs($attribs)
-  {
-    $return = array();
-    if ( !preg_match_all('/([a-z0-9_-]+)="([^"]+?)"/', $attribs, $matches) )
-      return array();
-    
-    foreach ( $matches[0] as $i => $match )
-    {
-      $name =& $matches[1][$i];
-      $value =& $matches[2][$i];
-      
-      $value = base64_decode($value);
-      
-      $return[$name] = $value;
-    }
-    
-    return $return;
-  }
-  
-  /**
-   * Escapes a string so that it's safe to use as an attribute in a parser hint.
-   * @param string
-   * @return string
-   */
-  
-  public static function escape_parser_hint_attrib($text)
-  {
-    return base64_encode($text);
-  }
-  
-  public static function reverse_process_headings($text)
-  {
-    if ( !preg_match_all('|^<h([1-6])(?: id="toc[0-9]+")?>(.*?)</h\\1>$|m', $text, $matches) )
-      return $text;
-    
-    foreach ( $matches[0] as $i => $match )
-    {
-      // generate heading tag
-      $heading_size = intval($matches[1][$i]);
-      $eq = '';
-      for ( $j = 0; $j < $heading_size; $j++ )
-        $eq .= '=';
-      
-      $heading =& $matches[2][$i];
-      
-      $tag = "$eq $heading $eq";
-      $text = str_replace($match, $tag, $text);
-    }
-    
-    return $text;
-  }
-  
-  public static function reverse_process_lists($text)
-  {
-    if ( !preg_match('!(</?(?:ul|ol|li)>)!', $text) )
-      return $text;
-    
-    $split = preg_split('!(</?(?:ul|ol|li)>)!', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
-    
-    $stack_height = 0;
-    $current_list = '';
-    $old_current_list = '';
-    $spaces = '';
-    $marker = '*';
-    $list_id = 0;
-    $just_terminated = false;
-    foreach ( $split as $tag )
-    {
-      switch($tag) 
-      {
-        case '<ul>':
-        case '<ol>':
-          $stack_height++;
-          $just_terminated = false;
-          if ( $stack_height > 1 )
-            $spaces .= $marker;
-          
-          $marker = ( $tag == 'ol' ) ? '#' : '*';
-          if ( $stack_height > 1 )
-            $current_list .= "\n";
-          
-          break;
-        case '</ul>':
-        case '</ol>':
-          $stack_height--;
-          $spaces = substr($spaces, 1);
-          
-          if ( $stack_height == 0 )
-          {
-            // rotate
-            $text = str_replace_once("{$old_current_list}{$tag}", trim($current_list), $text);
-            $current_list = '';
-            $old_current_list = '';
-          }
-          $just_terminated = true;
-          break;
-        case '<li>':
-          if ( $stack_height < 1 )
-            break;
-          
-          $current_list .= "{$spaces}{$marker} ";
-          break;
-        case '</li>':
-          if ( $stack_height < 1 )
-            break;
-          
-          if ( !$just_terminated )
-            $current_list .= "\n";
-          
-          $just_terminated = false;
-          break;
-        default:
-          if ( $stack_height > 0 )
-          {
-            $current_list .= trim($tag);
-          }
-          break;
-      }
-      if ( $stack_height > 0 )
-      {
-        $old_current_list .= $tag;
-      }
-    }
-    
-    return $text;
-  }
-  
-  public static function reverse_process_tables($text)
-  {
-    return $text;
-  }
-  
-  /**
-   * Parses internal links (wikilinks) in a block of text.
-   * @param string Text to process
-   * @param string Optional. If included will be used as a template instead of using the default syntax.
-   * @param bool If false, does not add wikilink-nonexistent or check for exsistence of pages. Can reduce DB queries; defualts to true.
-   * @param string Page ID. If specified, class="currentpage" will be added to links if they match the given page ID and namespace
-   * @param string Namespace. If specified, class="currentpage" will be added to links if they match the given page ID and namespace
-   * @return string
-   */
-  
-  public static function parse_internal_links($text, $tplcode = false, $do_exist_check = true, $match_page_id = false, $match_namespace = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-  
-    $parser = is_string($tplcode) ? $template->makeParserText($tplcode) : false;
-    
-    // allow blank urlname?
-    $repeater = have_blank_urlname_page() ? '*' : '+';
-    
-    // stage 1 - links with alternate text
-    preg_match_all('/\[\[([^\[\]<>\{\}\|]' . $repeater . ')\|(.+?)\]\]/', $text, $matches);
-    foreach ( $matches[0] as $i => $match )
-    {
-      list($page_id, $namespace) = RenderMan::strToPageID($matches[1][$i]);
-      $link = self::generate_internal_link($namespace, $page_id, $matches[2][$i], $match, $parser, $do_exist_check, $match_page_id, $match_namespace);
-      $text = str_replace($match, $link, $text);
-    }
-    
-    // stage 2 - links with no alternate text
-    preg_match_all('/\[\[([^\[\]<>\{\}\|]' . $repeater . ')\]\]/', $text, $matches);
-    foreach ( $matches[0] as $i => $match )
-    {
-      list($page_id, $namespace) = RenderMan::strToPageID($matches[1][$i]);
-      $pid_clean = $paths->nslist[$namespace] . sanitize_page_id($page_id);
-      $inner_text = ( isPage($pid_clean) ) ? htmlspecialchars(get_page_title($pid_clean)) : htmlspecialchars($matches[1][$i]);
-      
-      $link = self::generate_internal_link($namespace, $page_id, $inner_text, $match, $parser, $do_exist_check, $match_page_id, $match_namespace);
-      
-      $text = str_replace($match, $link, $text);
-    }
-    
-    return $text;
-  }
-  
-  /**
-   * Internal link generation function
-   * @access private
-   * @return string HTML
-   */
-  
-  private static function generate_internal_link($namespace, $page_id, $inner_text, $match, $parser = false, $do_exist_check = true, $match_page_id = false, $match_namespace = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( ($pos = strrpos($page_id, '#')) !== false )
-    {
-      $hash = substr($page_id, $pos);
-      $page_id = substr($page_id, 0, $pos);
-    }
-    else
-    {
-      $hash = '';
-    }
-    
-    if ( $namespace == 'Admin' )
-    {
-      // No linking directly to Admin pages!
-      $get = 'module=' . $paths->nslist[$namespace] . sanitize_page_id($page_id);
-      $pid_clean = $paths->nslist['Special'] . 'Administration';
-      $onclick = ' onclick="ajaxLoginNavTo(\'Special\', \'Administration\', USER_LEVEL_ADMIN, \'' . addslashes($get) . '\'); return false;"';
-    }
-    else
-    {
-      $get = false;
-      $onclick = '';
-      $pid_clean = $paths->nslist[$namespace] . sanitize_page_id($page_id);
-    }
-    
-    $url = makeUrl($pid_clean, $get, true) . $hash;
-    $quot = '"';
-    $exists = ( ($do_exist_check && isPage($pid_clean)) || !$do_exist_check ) ? '' : ' class="wikilink-nonexistent"';
-    
-    if ( $match_page_id && $match_namespace && $pid_clean === $paths->get_pathskey($match_page_id, $match_namespace) )
-      $exists .= ' class="currentpage"';
-    
-    if ( $parser )
-    {
-      $parser->assign_vars(array(
-          'HREF' => $url,
-          'FLAGS' => $exists,
-          'TEXT' => $inner_text
-        ));
-      $link = $parser->run();
-    }
-    else
-    {
-      $omatch = self::escape_parser_hint_attrib($match);
-      $link = "<!--#internallink src=\"$omatch\" --><a{$onclick} href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a><!--#/internallink-->";
-    }
-    
-    return $link;
-  }
-  
-  /**
-   * Parses a partial template tag in wikitext, and return an array with the parameters.
-   * @param string The portion of the template tag that contains the parameters.
-   * @example
-   * <code>
-   foo = lorem ipsum
-   bar = dolor sit amet
-   * </code>
-   * @return array Example:
-   * [foo] => lorem ipsum
-   * [bar] => dolor sit amet
-   */
-  
-  public static function parse_template_vars($input, $newlinemode = true)
-  {
-    $parms = array();
-    $input = trim($input);
-    if ( $newlinemode )
-    {
-      // we're going by newlines.
-      // split by parameter, then parse each one individually
-      $input = explode("\n", str_replace("\r", '', $input));
-      $lastparam = '';
-      $i = 0;
-      foreach ( $input as $line )
-      {
-        if ( preg_match('/^ *\|? *([A-z0-9_]+) *= *(.+)$/', $line, $match) )
-        {
-          // new parameter, named
-          $parms[ $match[1] ] = $match[2];
-          $lastparam = $match[1];
-        }
-        else if ( preg_match('/^ *\| *(.+)$/', $line, $match) || $lastparam === '' )
-        {
-          $parms[ $i ] = $match[1];
-          $lastparam = $i;
-          $i++;
-        }
-        else
-        {
-          $parms[ $lastparam ] .= "\n$line";
-        }
-      }
-    }
-    else
-    {
-      $result = preg_match_all('/
-                                 (?:^|[ ]*)\|         # start of parameter - string start or series of spaces
-                                 [ ]*
-                                 (?:
-                                   ([A-z0-9_]+)       # variable name
-                                   [ ]* = [ ]*        # assignment
-                                 )?                   # name section is optional - if the parameter name is not given, a numerical index is assigned
-                                 ([^\|]+|.+?\n[ ]*\|) # value
-                               /x', trim($input), $matches);
-      if ( $result )
-      {
-        $pi = 0;
-        for ( $i = 0; $i < count($matches[0]); $i++ )
-        {
-          $matches[1][$i] = trim($matches[1][$i]);
-          $parmname = !empty($matches[1][$i]) ? $matches[1][$i] : strval(++$pi);
-          $parms[ $parmname ] = $matches[2][$i];
-        }
-      }
-    }
-    // die('<pre>' . print_r($parms, true) . '</pre>');
-    return $parms;
-  }
-  
-  /**
-   * Processes all template tags within a block of wikitext.
-   * Updated in 1.0.2 to also parse template tags in the format of {{Foo |a = b |b = c |c = therefore, a}}
-   * @param string The text to process
-   * @return string Formatted text
-   * @example
-   * <code>
-   $text = '{{Template
-       | parm1 = Foo
-       | parm2 = Bar
-     }}';
-   $text = RenderMan::include_templates($text);
-   * </code>
-   */
-  
-  public static function include_templates($text)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    // $template_regex = "/\{\{([^\]]+?)((\n([ ]*?)[A-z0-9]+([ ]*?)=([ ]*?)(.+?))*)\}\}/is";
-    // matches:
-    //  1 - template name
-    //  2 - parameter section
-    $template_regex = "/
-                         \{\{                     # opening
-                           ([^\n\t\a\r]+)         # template name
-                           ((?:(?:[\s]+\|?)[ ]*(?:[A-z0-9_]+)[ ]*=[ ]*?(?:.+))*) # parameters
-                         \}\}                     # closing
-                       /isxU";
-    if ( $count = preg_match_all($template_regex, $text, $matches) )
-    {
-      // die('<pre>' . print_r($matches, true) . '</pre>');
-      for ( $i = 0; $i < $count; $i++ )
-      {
-        $matches[1][$i] = sanitize_page_id($matches[1][$i]);
-        $newlinemode = ( substr($matches[2][$i], 0, 1) == "\n" );
-        $parmsection = trim($matches[2][$i]);
-        if ( !empty($parmsection) )
-        {
-          $parms = RenderMan::parse_template_vars($parmsection, $newlinemode);
-          if ( !is_array($parms) )
-            // Syntax error
-            $parms = array();
-        }
-        else
-        {
-          $parms = Array();
-        }
-        if ( $tpl_code = RenderMan::fetch_template_text($matches[1][$i]) )
-        {
-          // Intelligent paragraphs.
-          // If:
-          //   * A line is fully wrapped in a <p> tag
-          //   * The line contains a variable
-          //   * The variable contains newlines
-          // Then:
-          //   * Drop the <p> tag, replace it fully paragraph-ized by newlines
-          
-          if ( preg_match_all('/^( *)<p(\s.+)?>(.*?\{([A-z0-9]+)\}.*?)<\/p> *$/m', $tpl_code, $paramatch) )
-          {
-            $parser = new Carpenter();
-            $parser->exclusive_rule('paragraph');
-            
-            foreach ( $paramatch[0] as $j => $match )
-            {
-              // $line is trimmed (the <p> is gone)
-              $spacing =& $paramatch[1][$i];
-              $para_attrs =& $paramatch[2][$j];
-              $para_attrs = str_replace(array('$', '\\'), array('\$', '\\\\'), $para_attrs);
-              $line =& $paramatch[3][$j];
-              $varname =& $paramatch[4][$j];
-              if ( isset($parms[$varname]) && strstr($parms[$varname], "\n") )
-              {
-                $newline = str_replace('{' . $varname . '}', $parms[$varname], $line);
-                $paraized = $parser->render($newline);
-                $paraized = preg_replace('/^<p>/m', "$spacing<p{$para_attrs}>", $paraized);
-                $paraized = $spacing . trim($paraized);
-                $tpl_code = str_replace_once($match, $paraized, $tpl_code);
-              }
-            }
-          }
-          
-          $parser = $template->makeParserText($tpl_code);
-          $parser->assign_vars($parms);
-          $text = str_replace($matches[0][$i], $parser->run(), $text);
-        }
-      }
-    }
-    return $text;
-  }
-  
-  /**
-   * Preprocesses an HTML text string prior to being sent to MySQL.
-   * @param string $text
-   * @param bool $strip_all_php - if true, strips all PHP regardless of user permissions. Else, strips PHP only if user level < USER_LEVEL_ADMIN. Defaults to true.
-   * @param bool $sqlescape - if true, sends text through $db->escape(). Otherwise returns unescaped text. Defaults to true.
-   * @param bool $reduceheadings - if true, finds HTML headings and replaces them with wikitext. Else, does not touch headings. Defaults to true.
-   * @param Session_ACLPageInfo Optional permissions instance to check against, $session is used if not provided
-   */
-  public static function preprocess_text($text, $strip_all_php = true, $sqlescape = true, $reduceheadings = true, $perms = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $random_id = md5( time() . mt_rand() );
-    
-    $code = $plugins->setHook('render_sanitize_pre');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    if ( !is_object($perms) )
-    {
-      $namespace = $paths->namespace;
-      $perms =& $session;
-    }
-    else
-    {
-      $namespace = $perms->namespace;
-    }
-    
-    $can_do_php = ( !$strip_all_php && $perms->get_permissions('php_in_pages') );
-    $can_do_html = $session->check_acl_scope('html_in_pages', $namespace) && $perms->get_permissions('html_in_pages');
-    
-    if ( $can_do_html && !$can_do_php )
-    {
-      $text = preg_replace('#<(\?|\?php|%)(.*?)(\?|%)>#is', '&lt;\\1\\2\\3&gt;', $text);
-    }
-    else if ( !$can_do_html && !$can_do_php )
-    {
-      $text = sanitize_html($text, true);
-      // If we can't do PHP, we can't do Javascript either.
-      $text = RenderMan::destroy_javascript($text);
-    }
-    
-    // Strip out <nowiki> sections and PHP code
-    
-    $php = preg_match_all('#(<|&lt;)\?php(.*?)\?(>|&gt;)#is', $text, $phpsec);
-    
-    //die('<pre>'.htmlspecialchars(print_r($phpsec, true))."\n".htmlspecialchars(print_r($text, true)).'</pre>');
-    
-    for($i=0;$i<sizeof($phpsec[1]);$i++)
-    {
-      $text = str_replace($phpsec[0][$i], '{PHP:'.$random_id.':'.$i.'}', $text);
-    }
-    
-    $nw = preg_match_all('#<nowiki>(.*?)<\/nowiki>#is', $text, $nowiki);
-    
-    for($i=0;$i<sizeof($nowiki[1]);$i++)
-    {
-      $text = str_replace('<nowiki>'.$nowiki[1][$i].'</nowiki>', '{NOWIKI:'.$random_id.':'.$i.'}', $text);
-    }
-    
-    $text = str_replace('~~~~~', enano_date('G:i, j F Y (T)'), $text);
-    $text = str_replace('~~~~', "[[User:$session->username|$session->username]] ".enano_date('G:i, j F Y (T)'), $text);
-    $text = str_replace('~~~', "[[User:$session->username|$session->username]] ", $text);
-    
-    $code = $plugins->setHook('render_sanitize_post');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    // gently apply some reverse-processing to allow the parser to do magic with TOCs and stuff
-    if ( $reduceheadings )
-      $text = self::reverse_process_headings($text);
-    
-    // Reinsert <nowiki> sections
-    for($i=0;$i<$nw;$i++)
-    {
-      $text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', '<nowiki>'.$nowiki[1][$i].'</nowiki>', $text);
-    }
-    // Reinsert PHP
-    for($i=0;$i<$php;$i++)
-    {
-      $phsec = ''.$phpsec[1][$i].'?php'.$phpsec[2][$i].'?'.$phpsec[3][$i].'';
-      if ( $strip_all_php )
-        $phsec = htmlspecialchars($phsec);
-      $text = str_replace('{PHP:'.$random_id.':'.$i.'}', $phsec, $text);
-    }
-    
-    $text = ( $sqlescape ) ? $db->escape($text) : $text;
-    
-    return $text;
-  }
-  
-  public static function smilieyize($text, $complete_urls = false)
-  {
-    
-    $random_id = md5( time() . mt_rand() );
-    
-    // Smileys array - eventually this will be fetched from the database by
-    // RenderMan::initSmileys during initialization, but it will all be hardcoded for beta 2
-    
-    $smileys = Array(
-      'O:-)'    => 'face-angel.png',
-      'O:)'     => 'face-angel.png',
-      'O=)'     => 'face-angel.png',
-      ':-)'     => 'face-smile.png',
-      ':)'      => 'face-smile.png',
-      '=)'      => 'face-smile-big.png',
-      ':-('     => 'face-sad.png',
-      ':('      => 'face-sad.png',
-      ';('      => 'face-sad.png',
-      ':-O'     => 'face-surprise.png',
-      ';-)'     => 'face-wink.png',
-      ';)'      => 'face-wink.png',
-      '8-)'     => 'face-glasses.png',
-      '8)'      => 'face-glasses.png',
-      ':-D'     => 'face-grin.png',
-      ':D'      => 'face-grin.png',
-      '=D'      => 'face-grin.png',
-      ':-*'     => 'face-kiss.png',
-      ':*'      => 'face-kiss.png',
-      '=*'      => 'face-kiss.png',
-      ':\'('    => 'face-crying.png',
-      ':-|'     => 'face-plain.png',
-      ':-\\'    => 'face-plain.png',
-      ':-/'     => 'face-plain.png',
-      ':joke:'  => 'face-plain.png',
-      ']:-&gt;' => 'face-devil-grin.png',
-      ']:->'    => 'face-devil-grin.png',
-      ':kiss:'  => 'face-kiss.png',
-      ':-P'     => 'face-tongue-out.png',
-      ':P'      => 'face-tongue-out.png',
-      ':-p'     => 'face-tongue-out.png',
-      ':p'      => 'face-tongue-out.png',
-      ':-X'     => 'face-sick.png',
-      ':X'      => 'face-sick.png',
-      ':sick:'  => 'face-sick.png',
-      ':-]'     => 'face-oops.png',
-      ':]'      => 'face-oops.png',
-      ':oops:'  => 'face-oops.png',
-      ':-['     => 'face-embarassed.png',
-      ':['      => 'face-embarassed.png'
-      );
-    
-    // Strip out <nowiki> sections
-    //return '<pre>'.htmlspecialchars($text).'</pre>';
-    $nw = preg_match_all('#<nowiki>(.*?)<\/nowiki>#is', $text, $nowiki);
-    
-    for ( $i = 0; $i < count($nowiki[1]); $i++ )
-    {
-      $text = str_replace('<nowiki>' . $nowiki[1][$i] . '</nowiki>', '{NOWIKI:'.$random_id.':'.$i.'}', $text);
-    }
-    
-    foreach ( $smileys as $smiley => $smiley_path )
-    {
-      $hex_smiley = hexencode($smiley, '&#x', ';');
-      $pfx = ( $complete_urls ) ? get_server_url() : '';
-      $text = str_replace(' ' . $smiley,
-          ' <!--#smiley code="' . self::escape_parser_hint_attrib($smiley) . '"--><nowiki>
-           <!-- The above is a reverse-parser hint -->
-             <img title="' . $hex_smiley . '" alt="' . $hex_smiley . '" src="' . $pfx . scriptPath . '/images/smilies/' . $smiley_path . '"
-              style="border: 0;" />
-           </nowiki><!--#/smiley-->', $text);
-    }
-    //*/
-    
-    // Reinsert <nowiki> sections
-    for ( $i = 0; $i < $nw; $i++ )
-    {
-      $text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', '<nowiki>'.$nowiki[1][$i].'</nowiki>', $text);
-    }
-    
-    return $text;
-  }
-  
-  /**
-   * Generates a summary of the differences between two texts, and formats it as XHTML.
-   * @param $str1 string the first block of text
-   * @param $str2 string the second block of text
-   * @return string
-   */
-  public static function diff($str1, $str2)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    require_once(ENANO_ROOT.'/includes/diff.php');
-    $str1 = explode("\n", $str1);
-    $str2 = explode("\n", $str2);
-    $diff = new Diff($str1, $str2);
-    $renderer = new TableDiffFormatter();
-    return '<table class="diff">'.$renderer->format($diff).'</table>';
-  }
-  
-  /**
-   * Changes wikitext image tags to HTML.
-   * @param string The wikitext to process
-   * @param array Will be overwritten with the list of HTML tags (the system uses tokens for TextWiki compatibility)
-   * @return string
-   */
-  
-  public static function process_image_tags($text, &$taglist)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $s_delim = "\xFF";
-    $f_delim = "\xFF";
-    $taglist = array();
-    
-    // Wicked huh?
-    $ns_file = str_replace('/', '\\/', preg_quote($paths->nslist['File']));
-    $regex = '/
-           \[\[                                                                  # starting delimiter 
-           :' . $ns_file . '([\w\s0-9_\(\)!@%\^\+\|\.-]+?\.(?:png|gif|jpg|jpeg)) # image filename
-           (?:(?:\|(?:.+?))*)                                                    # parameters
-           \]\]                                                                  # ending delimiter
-           /ix';
-    
-    preg_match_all($regex, $text, $matches);
-    
-    foreach ( $matches[0] as $i => $match )
-    {
-      
-      $full_tag   =& $matches[0][$i];
-      $filename   =& $matches[1][$i];
-      
-      // apply recursion (hack? @todo could this be done with (?R) in PCRE?)
-      $tag_pos = strpos($text, $full_tag);
-      $tag_end_pos = $tag_pos + strlen($full_tag);
-      while ( get_char_count($full_tag, ']') < get_char_count($full_tag, '[') && $tag_end_pos < strlen($text) )
-      {
-        $full_tag .= substr($text, $tag_end_pos, 1);
-        $tag_end_pos++;
-      }
-      if ( $tag_end_pos > strlen($text) )
-      {
-        // discard tag, not closed fully
-        continue;
-      }
-      
-      // init the various image parameters
-      $width = null;
-      $height = null;
-      $scale_type = null;
-      $raw_display = false;
-      $clear = null;
-      $caption = null;
-      
-      // trim tag and parse particles
-      $tag_trim = rtrim(ltrim($full_tag, '['), ']');
-      // trim off the filename from the start of the tag
-      $filepart_len = 1 + strlen($paths->nslist['File']) + strlen($filename) + 1;
-      $tag_trim = substr($tag_trim, $filepart_len);
-      // explode and we should have parameters
-      $tag_parts = explode('|', $tag_trim);
-      
-      // for each of the parameters, see if it matches a known option. If so, apply it;
-      // otherwise, see if a plugin reserved that parameter and if not treat it as the caption
-      foreach ( $tag_parts as $param )
-      {
-        switch($param)
-        {
-          case 'left':
-          case 'right':
-            $clear = $param;
-            break;
-          case 'thumb':
-            $scale_type = 'thumb';
-            break;
-          case 'raw':
-            $raw_display = true;
-            break;
-          default:
-            // height specification
-            if ( preg_match('/^([0-9]+)x([0-9]+)$/', $param, $dims) )
-            {
-              $width = intval($dims[1]);
-              $height = intval($dims[2]);
-              break;
-            }
-            // not the height, so see if a plugin took this over
-            // this hook requires plugins to return true if they modified anything
-            $code = $plugins->setHook('img_tag_parse_params');
-            foreach ( $code as $cmd )
-            {
-              if ( eval($cmd) )
-                break 2;
-            }
-            // we would have broken out by now if a plugin properly handled this,
-            // so just set the caption now.
-            $caption = $param;
-            break;
-        }
-      }
-      
-      if ( !isPage( $paths->nslist['File'] . $filename ) )
-      {
-        $text = str_replace($full_tag, '[[' . $paths->nslist['File'] . $filename . ']]', $text);
-        continue;
-      }
-      
-      if ( $scale_type == 'thumb' )
-      {
-        $r_width  = 225;
-        $r_height = 225;
-        
-        $url = makeUrlNS('Special', 'DownloadFile/' . $filename, 'preview&width=' . $r_width . '&height=' . $r_height, true);
-      }
-      else if ( !empty($width) && !empty($height) )
-      {
-        $r_width = $width;
-        $r_height = $height;
-        
-        $url = makeUrlNS('Special', 'DownloadFile/' . $filename, 'preview&width=' . $r_width . '&height=' . $r_height, true);
-      }
-      else
-      {
-        $url = makeUrlNS('Special', 'DownloadFile/' . $filename);
-      }
-      
-      $img_tag = '<img src="' . $url . '" ';
-      
-      // if ( isset($r_width) && isset($r_height) && $scale_type != '|thumb' )
-      // {
-      //   $img_tag .= 'width="' . $r_width . '" height="' . $r_height . '" ';
-      // }
-      
-      $img_tag .= 'style="border-width: 0px; /* background-color: white; */" ';
-      
-      $code = $plugins->setHook('img_tag_parse_img');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      
-      $img_tag .= '/>';
-      
-      $s_full_tag = self::escape_parser_hint_attrib($full_tag);
-      $complete_tag = '<!--#imagelink src="' . $s_full_tag . '" -->';
-      
-      if ( !empty($scale_type) && !$raw_display )
-      {
-        $complete_tag .= '<div class="thumbnail" ';
-        $clear_text = '';
-        if ( !empty($clear) )
-        {
-          $side = ( $clear == 'left' ) ? 'left' : 'right';
-          $opposite = ( $clear == 'left' ) ? 'right' : 'left';
-          $clear_text .= "float: $side; margin-$opposite: 20px; width: {$r_width}px;";
-          $complete_tag .= 'style="' . $clear_text . '" ';
-        }
-        $complete_tag .= '>';
-        
-        $complete_tag .= '<a href="' . makeUrlNS('File', $filename) . '" style="display: block;">';
-        $complete_tag .= $img_tag;
-        $complete_tag .= '</a>';
-        
-        $mag_button = '<a href="' . makeUrlNS('File', $filename) . '" style="display: block; float: right; clear: right; margin: 0 0 10px 10px;"><img alt="[ + ]" src="' . scriptPath . '/images/thumbnail.png" style="border-width: 0px;" /></a>';
-      
-        if ( !empty($caption) )
-        {
-          $complete_tag .= $mag_button . $caption;
-        }
-        
-        $complete_tag .= '</div>';
-      }
-      else if ( $raw_display )
-      {
-        $complete_tag .= "$img_tag";
-        $taglist[$i] = $complete_tag;
-        
-        $repl = "{$s_delim}e_img_{$i}{$f_delim}";
-        $text = str_replace($full_tag, $repl, $text);
-        continue;
-      }
-      else
-      {
-        $complete_tag .= '<a href="' . makeUrlNS('File', $filename) . '" style="display: block;"';
-        $code = $plugins->setHook('img_tag_parse_link');
-        foreach ( $code as $cmd )
-        {
-          eval($cmd);
-        }
-        $complete_tag .= '>';
-        $complete_tag .= $img_tag;
-        $complete_tag .= '</a>';
-      }
-      
-      $complete_tag .= "<!--#/imagelink-->";
-      $taglist[$i] = $complete_tag;
-      
-      /*
-      $pos = strpos($text, $full_tag);
-      
-      while(true)
-      {
-        $check1 = substr($text, $pos, 3);
-        $check2 = substr($text, $pos, 1);
-        if ( $check1 == '<p>' || $pos == 0 || $check2 == "\n" )
-        {
-          // die('found at pos '.$pos);
-          break;
-        }
-        $pos--;
-      }
-      */
-      
-      /*
-      $repl = "{$s_delim}e_img_{$i}{$f_delim}";
-      $text = substr($text, 0, $pos) . $repl . substr($text, $pos);
-      
-      $text = str_replace($full_tag, '', $text);
-      */
-      $text = str_replace_once($full_tag, $complete_tag, $text);
-      
-      unset($full_tag, $filename, $scale_type, $width, $height, $clear, $caption, $r_width, $r_height);
-      
-    }
-    
-    // if ( count($matches[0]) > 0 )
-    //   die('<pre>' . htmlspecialchars($text) . '</pre>');
-    
-    return $text;
-  }
-  
-  /**
-   * Finalizes processing of image tags.
-   * @param string The preprocessed text
-   * @param array The list of image tags created by RenderMan::process_image_tags()
-   */
-   
-  public static function process_imgtags_stage2($text, $taglist)
-  {
-    $s_delim = "\xFF";
-    $f_delim = "\xFF";
-    foreach ( $taglist as $i => $tag )
-    {
-      $repl = "{$s_delim}e_img_{$i}{$f_delim}";
-      $text = str_replace($repl, $tag, $text);
-    }               
-    return $text;
-  }
-  
+	
+	public static function strToPageID($string)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$k = array_keys($paths->nslist);
+		$proj_alt = 'Project:';
+		if ( substr($string, 0, (strlen($proj_alt))) == $proj_alt )
+		{
+			$ns = 'Project';
+			$pg = substr($string, strlen($proj_alt), strlen($string));
+			return Array($pg, $ns);
+		}
+		for($i=0;$i<sizeof($paths->nslist);$i++)
+		{
+			$ln = strlen($paths->nslist[$k[$i]]);
+			if(substr($string, 0, $ln) == $paths->nslist[$k[$i]])
+			{
+				$ns = $k[$i];
+				$pg = substr($string, strlen($paths->nslist[$ns]), strlen($string));
+			}
+		}
+		return Array($pg, $ns);
+	}
+	
+	public static function getPage($page_id, $namespace, $wiki = 1, $smilies = true, $filter_links = true, $redir = true, $render = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$page = new PageProcessor($page_id, $namespace);
+		$text = $page->fetch_text();
+		
+		if ( !$render )
+			return $text;
+		
+		$text = self::render($text, $wiki, $smilies, $filter_links);
+		return $text;
+	}
+	
+	public static function getTemplate($id, $parms)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( !isPage($paths->get_pathskey($id, 'Template')) ) 
+		{
+			return '[['.$paths->nslist['Template'].$id.']]';
+		}
+		if(isset($paths->template_cache[$id]))
+		{
+			$text = $paths->template_cache[$id];
+		}
+		else
+		{
+			$page = new PageProcessor($id, 'Template');
+			$text = $page->fetch_text();
+			$paths->template_cache[$id] = $text;
+		}
+		
+		$text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $text);
+		$text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $text);
+		
+		preg_match_all('#\(_([0-9]+)_\)#', $text, $matchlist);
+		
+		foreach($matchlist[1] as $m)
+		{
+			if(isset($parms[((int)$m)+1])) 
+			{
+				$p = $parms[((int)$m)+1];
+			}
+			else
+			{
+				$p = '<b>Notice:</b> RenderMan::getTemplate(): Parameter '.$m.' is not set';
+			}
+			$text = str_replace('(_'.$m.'_)', $p, $text);
+			$text = str_replace('{{' . ( $m + 1 ) . '}}', $p, $text);
+		}
+		$text = RenderMan::include_templates($text);
+		return $text;
+	}
+	
+	public static function fetch_template_text($id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$fetch_ns = 'Template';
+		if ( !isPage($paths->get_pathskey($id, 'Template')) ) 
+		{
+			// Transclusion of another page
+			// 1.1.5: Now You, Too, Can Be A Template, Even If You're Just A Plain Old Article! (TM)
+			$nssep = substr($paths->nslist['Special'], -1);
+			$nslist = $paths->nslist;
+			foreach ( $nslist as &$ns )
+			{
+				if ( $ns == '' )
+					$ns = $nssep;
+			}
+			$prefixlist = array_flip($nslist);
+			foreach ( $nslist as &$ns )
+			{
+				$ns = preg_quote($ns);
+			}
+			$nslist = implode('|', $nslist);
+			if ( preg_match("/^($nslist)(.*?)$/", $id, $match) )
+			{
+				// in practice this should always be true but just to be safe...
+				if ( isset($prefixlist[$match[1]]) )
+				{
+					$new_id = $paths->nslist[ $prefixlist[$match[1]] ] . sanitize_page_id($match[2]);
+					if ( !isPage($new_id) )
+					{
+						return "[[$new_id]]";
+					}
+					$fetch_ns = $prefixlist[$match[1]];
+					$id = sanitize_page_id($match[2]);
+				}
+			}
+			else
+			{
+				return '[['.$paths->nslist['Template'].$id.']]';
+			}
+		}
+		if(isset($paths->template_cache[$id]))
+		{
+			$text = $paths->template_cache[$id];
+		}
+		else
+		{
+			$text = RenderMan::getPage($id, $fetch_ns, 0, false, false, false, false);
+			$paths->template_cache[$id] = $text;
+		}
+		
+		if ( is_string($text) )
+		{
+			$text = preg_replace('/<noinclude>(.*?)<\/noinclude>/is', '', $text);
+			$text = preg_replace('/<nodisplay>(.*?)<\/nodisplay>/is', '\\1', $text);
+		}
+		
+		return $text;
+	}
+	
+	/**
+ 	* Renders a glob of text. Note that this is PHP-safe, so if returned text (or rather, "?>" . $returned) has PHP it can be eval'ed.
+ 	* @param string Text to render
+ 	* @param int Render parameters - see constants.php
+ 	* @return string Rendered text
+ 	*/
+	
+	public static function render($text, $flags = RENDER_WIKI_DEFAULT, $smilies = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !$smilies )
+			$flags |= RENDER_NOSMILIES;
+		
+		if ( !($flags & RENDER_NOSMILIES) )
+		{
+			$text = RenderMan::smilieyize($text);
+		}
+		if ( $flags & RENDER_WIKI_DEFAULT )
+		{
+			$text = RenderMan::next_gen_wiki_format($text, $flags);
+		}
+		else if ( $flags & RENDER_WIKI_TEMPLATE )
+		{
+			$text = $template->tplWikiFormat($text);
+		}           
+		return $text;
+	}
+	
+	private static function next_gen_wiki_format($text, $flags = 0)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		profiler_log("RenderMan: starting wikitext render");
+		require_once( ENANO_ROOT . '/includes/wikiformat.php' );
+		require_once( ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php' );
+		require_once( ENANO_ROOT . '/includes/wikiengine/Tables.php' );
+		
+		// this is still needed by parser plugins
+		$random_id = md5( time() . mt_rand() );
+		
+		// Strip out <nowiki> sections
+		self::nowiki_strip($text, $nowiki_stripped);
+		
+		// Run early parsing plugins
+		$code = $plugins->setHook('render_wikiformat_veryearly');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		// Strip out embedded PHP
+		self::php_strip($text, $php_stripped);
+		
+		// Convert newlines for the parser
+		$text = str_replace("\r\n", "\n", $text);
+		
+		// Perform render through the engine
+		$carpenter = new Carpenter();
+		$carpenter->flags = $flags;
+		$carpenter->hook(array(__CLASS__, 'hook_pre'), PO_AFTER, 'lang');
+		$carpenter->hook(array(__CLASS__, 'hook_posttemplates'), PO_AFTER, 'templates');
+		if ( $flags & RENDER_WIKI_TEMPLATE )
+		{
+			// FIXME: Where is noinclude/nodisplay being processed in the pipeline? (Seems to be processed, but not here)
+		}
+		
+		//
+		// Set rules for the rendering process
+		//
+		
+		if ( $flags & RENDER_BLOCK && !($flags & RENDER_INLINE) )
+		{
+			// block only
+			$carpenter->disable_all_rules();
+			foreach ( array('blockquote', 'tables', 'heading', 'hr', 'multilist', 'bold', 'italic', 'underline', 'paragraph', 'blockquotepost') as $rule )
+			{
+				$carpenter->enable_rule($rule);
+			}
+			
+			$code = $plugins->setHook('render_block_only');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+		}
+		else if ( $flags & RENDER_INLINE && !($flags & RENDER_BLOCK) )
+		{
+			// inline only
+			$carpenter->disable_all_rules();
+			foreach ( array('bold', 'italic', 'underline', 'externalwithtext', 'externalnotext', 'image', 'internallink') as $rule )
+			{
+				$carpenter->enable_rule($rule);
+			}
+			
+			$code = $plugins->setHook('render_inline_only');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+		}
+		else
+		{
+			// full render
+			$code = $plugins->setHook('render_full');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+		}
+		$text = $carpenter->render($text);
+		
+		// For plugin compat
+		$result =& $text;
+		
+		// Post processing hook
+		$code = $plugins->setHook('render_wikiformat_post');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		// Add PHP and nowiki back in
+		self::nowiki_unstrip($text, $nowiki_stripped);
+		self::php_unstrip($text, $php_stripped);
+		
+		profiler_log("RenderMan: finished wikitext render");
+		
+		return $text;
+	}
+	
+	public static function hook_pre($text)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$code = $plugins->setHook('render_wikiformat_pre');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		return $text;
+	}
+	
+	public static function hook_posttemplates($text)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$code = $plugins->setHook('render_wikiformat_posttemplates');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		return $text;
+	}
+	
+	/**
+ 	* Strip out <nowiki> tags (to bypass parsing on them)
+ 	* @access private
+ 	*/
+	
+	private static function nowiki_strip(&$text, &$stripdata)
+	{
+		self::tag_strip('nowiki', $text, $stripdata);
+	}
+	
+	/**
+ 	* Restore stripped <nowiki> tags.
+ 	* @access private
+ 	*/
+	
+	public static function nowiki_unstrip(&$text, &$stripdata)
+	{
+		self::tag_unstrip('nowiki', $text, $stripdata);
+	}
+	
+	/**
+ 	* Strip out an arbitrary HTML tag.
+ 	* @access private
+ 	*/
+	
+	public static function tag_strip($tag, &$text, &$stripdata)
+	{
+		$random_id = md5( time() . mt_rand() );
+		
+		preg_match_all("#<$tag>(.*?)</$tag>#is", $text, $blocks);
+		
+		foreach ( $blocks[0] as $i => $match )
+		{
+			$text = str_replace($match, "{{$tag}:{$random_id}:{$i}}", $text);
+		}
+		
+		$stripdata = array(
+				'random_id' => $random_id,
+				'blocks' => $blocks[1]
+			);
+	}
+	
+	/**
+ 	* Strip out an arbitrary HTML tag, pushing on to the existing list of stripped data.
+ 	* @access private
+ 	*/
+	
+	public static function tag_strip_push($tag, &$text, &$stripdata)
+	{
+		if ( !is_array($stripdata) )
+		{
+			$stripdata = array(
+					'random_id' => md5( time() . mt_rand() ),
+					'blocks' => array()
+				);
+		}
+		$random_id =& $stripdata['random_id'];
+		
+		preg_match_all("#<$tag>(.*?)</$tag>#is", $text, $blocks);
+		
+		foreach ( $blocks[0] as $i => $match )
+		{
+			$j = count($stripdata['blocks']);
+			$stripdata['blocks'][] = $blocks[1][$i];
+			$text = str_replace($match, "{{$tag}:{$random_id}:{$j}}", $text);
+		}
+	}
+	
+	/**
+ 	* Restore stripped <nowiki> tags.
+ 	* @access private
+ 	*/
+	
+	public static function tag_unstrip($tag, &$text, &$stripdata, $keep = false)
+	{
+		$random_id = $stripdata['random_id'];
+		
+		foreach ( $stripdata['blocks'] as $i => $block )
+		{
+			$block = $keep ? "<$tag>$block</$tag>" : $block;
+			$text = str_replace("{{$tag}:{$random_id}:{$i}}", $block, $text);
+		}
+		
+		$stripdata = array();
+	}
+	
+	/**
+ 	* Strip out PHP code (to prevent it from being sent through the parser). Private because it does not do what you think it does. (The method you are looking for is strip_php.)
+ 	* @access private
+ 	*/
+	
+	private static function php_strip(&$text, &$stripdata)
+	{
+		$random_id = md5( time() . mt_rand() );
+		
+		preg_match_all('#<\?(?:php)?[\s=].+?\?>#is', $text, $blocks);
+		
+		foreach ( $blocks[0] as $i => $match )
+		{
+			$text = str_replace($match, "{PHP:$random_id:$i}", $text);
+		}
+		
+		$stripdata = array(
+				'random_id' => $random_id,
+				'blocks' => $blocks[0]
+			);
+	}
+	
+	/**
+ 	* Restore stripped PHP code
+ 	* @access private
+ 	*/
+	
+	private static function php_unstrip(&$text, &$stripdata)
+	{
+		$random_id = $stripdata['random_id'];
+		
+		foreach ( $stripdata['blocks'] as $i => $block )
+		{
+			$text = str_replace("{PHP:$random_id:$i}", $block, $text);
+		}
+		
+		$stripdata = array();
+	}
+	
+	/**
+ 	* Deprecated.
+ 	*/
+	
+	public static function wikiFormat($message, $filter_links = true, $do_params = false, $plaintext = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		return RenderMan::next_gen_wiki_format($message, $plaintext, $filter_links, $do_params);
+	}
+	
+	public static function destroy_javascript($message, $_php = false)
+	{
+		$message = preg_replace('#<(script|object|applet|embed|iframe|frame|form|input|select)(.*?)>#is', '&lt;\\1\\2&gt;', $message);
+		$message = preg_replace('#</(script|object|applet|embed|iframe|frame|form|input|select)(.*?)>#is', '&lt;/\\1\\2&gt;', $message);
+		$message = preg_replace('#(javascript|script|activex|chrome|about|applet):#is', '\\1&#058;', $message);
+		if ( $_php )
+		{
+			// Left in only for compatibility
+			$message = preg_replace('#&lt;(.*?)>#is', '<\\1>', $message);
+			$message = preg_replace('#<(.*?)&gt;#is', '<\\1>', $message);
+			$message = preg_replace('#<(\?|\?php|%)(.*?)(\?|%)>#is', '&lt;\\1\\2\\3&gt;', $message);
+			// strip <a href="foo" onclick="bar();">-type attacks
+			$message = preg_replace('#<([a-zA-Z:\-]+) (.*?)on([A-Za-z]*)=(.*?)>#is', '&lt;\\1\\2on\\3=\\4&gt;', $message);
+		}
+		return $message;
+	}
+	
+	public static function strip_php($message)
+	{
+		return RenderMan::destroy_javascript($message, true);
+	}
+	
+	public static function sanitize_html($text)
+	{
+		$text = htmlspecialchars($text);
+		$allowed_tags = Array('b', 'i', 'u', 'pre', 'code', 'tt', 'br', 'p', 'nowiki', '!--([\w\W]+)--');
+		foreach($allowed_tags as $t)
+		{
+			$text = preg_replace('#&lt;'.$t.'&gt;(.*?)&lt;/'.$t.'&gt;#is', '<'.$t.'>\\1</'.$t.'>', $text);
+			$text = preg_replace('#&lt;'.$t.' /&gt;#is', '<'.$t.' />', $text);
+			$text = preg_replace('#&lt;'.$t.'&gt;#is', '<'.$t.'>', $text);
+		}
+		return $text;
+	}
+	
+	/**
+ 	* Reverse-renders a blob of text (converts it from XHTML back to wikitext) by using parser hints and educated guesses.
+ 	* @param string XHTML
+ 	* @return string Wikitext
+ 	*/
+	
+	public static function reverse_render($text)
+	{
+		// convert \r\n to \n
+		$text = str_replace("\r\n", "\n", $text);
+		
+		// Separate certain block level elements onto their own lines. This tidies up the tag
+		// soup that TinyMCE sometimes produces.
+		$block_elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div', 'table', 'ul', 'pre');
+		$block_elements = implode('|', $block_elements);
+		$regex = "#(</(?:$block_elements)>)\n?<($block_elements)(>| .+?>)#i";
+		$text = preg_replace($regex, "$1\n\n<$2$3", $text);
+		
+		$text = self::reverse_process_parser_hints($text);
+		$text = self::reverse_process_headings($text);
+		$text = self::reverse_process_lists($text);
+		$text = self::reverse_process_tables($text);
+		
+		// Lastly, strip out paragraph tags.
+		$text = preg_replace('|^ *<p>(.+?)</p> *$|m', "\\1", $text);
+		
+		return $text;
+	}
+	
+	public static function reverse_process_parser_hints($text)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !preg_match_all('|<!--#([a-z0-9_]+)(?: (.+?))?-->([\w\W]*?)<!--#/\\1-->|s', $text, $matches) )
+			return $text;
+		
+		foreach ( $matches[0] as $i => $match )
+		{
+			$tag =& $matches[1][$i];
+			$attribs =& $matches[2][$i];
+			$inner =& $matches[3][$i];
+			
+			$attribs = self::reverse_process_hint_attribs($attribs);
+			switch($tag)
+			{
+				case 'smiley':
+				case 'internallink':
+				case 'imagelink':
+					if ( isset($attribs['code']) )
+					{
+						$text = str_replace($match, $attribs['code'], $text);
+					}
+					else if ( isset($attribs['src']) )
+					{
+						$text = str_replace($match, $attribs['src'], $text);
+					}
+					break;
+			}
+		}
+		
+		return $text;
+	}
+	
+	public static function reverse_process_hint_attribs($attribs)
+	{
+		$return = array();
+		if ( !preg_match_all('/([a-z0-9_-]+)="([^"]+?)"/', $attribs, $matches) )
+			return array();
+		
+		foreach ( $matches[0] as $i => $match )
+		{
+			$name =& $matches[1][$i];
+			$value =& $matches[2][$i];
+			
+			$value = base64_decode($value);
+			
+			$return[$name] = $value;
+		}
+		
+		return $return;
+	}
+	
+	/**
+ 	* Escapes a string so that it's safe to use as an attribute in a parser hint.
+ 	* @param string
+ 	* @return string
+ 	*/
+	
+	public static function escape_parser_hint_attrib($text)
+	{
+		return base64_encode($text);
+	}
+	
+	public static function reverse_process_headings($text)
+	{
+		if ( !preg_match_all('|^<h([1-6])(?: id="toc[0-9]+")?>(.*?)</h\\1>$|m', $text, $matches) )
+			return $text;
+		
+		foreach ( $matches[0] as $i => $match )
+		{
+			// generate heading tag
+			$heading_size = intval($matches[1][$i]);
+			$eq = '';
+			for ( $j = 0; $j < $heading_size; $j++ )
+				$eq .= '=';
+			
+			$heading =& $matches[2][$i];
+			
+			$tag = "$eq $heading $eq";
+			$text = str_replace($match, $tag, $text);
+		}
+		
+		return $text;
+	}
+	
+	public static function reverse_process_lists($text)
+	{
+		if ( !preg_match('!(</?(?:ul|ol|li)>)!', $text) )
+			return $text;
+		
+		$split = preg_split('!(</?(?:ul|ol|li)>)!', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+		
+		$stack_height = 0;
+		$current_list = '';
+		$old_current_list = '';
+		$spaces = '';
+		$marker = '*';
+		$list_id = 0;
+		$just_terminated = false;
+		foreach ( $split as $tag )
+		{
+			switch($tag) 
+			{
+				case '<ul>':
+				case '<ol>':
+					$stack_height++;
+					$just_terminated = false;
+					if ( $stack_height > 1 )
+						$spaces .= $marker;
+					
+					$marker = ( $tag == 'ol' ) ? '#' : '*';
+					if ( $stack_height > 1 )
+						$current_list .= "\n";
+					
+					break;
+				case '</ul>':
+				case '</ol>':
+					$stack_height--;
+					$spaces = substr($spaces, 1);
+					
+					if ( $stack_height == 0 )
+					{
+						// rotate
+						$text = str_replace_once("{$old_current_list}{$tag}", trim($current_list), $text);
+						$current_list = '';
+						$old_current_list = '';
+					}
+					$just_terminated = true;
+					break;
+				case '<li>':
+					if ( $stack_height < 1 )
+						break;
+					
+					$current_list .= "{$spaces}{$marker} ";
+					break;
+				case '</li>':
+					if ( $stack_height < 1 )
+						break;
+					
+					if ( !$just_terminated )
+						$current_list .= "\n";
+					
+					$just_terminated = false;
+					break;
+				default:
+					if ( $stack_height > 0 )
+					{
+						$current_list .= trim($tag);
+					}
+					break;
+			}
+			if ( $stack_height > 0 )
+			{
+				$old_current_list .= $tag;
+			}
+		}
+		
+		return $text;
+	}
+	
+	public static function reverse_process_tables($text)
+	{
+		return $text;
+	}
+	
+	/**
+ 	* Parses internal links (wikilinks) in a block of text.
+ 	* @param string Text to process
+ 	* @param string Optional. If included will be used as a template instead of using the default syntax.
+ 	* @param bool If false, does not add wikilink-nonexistent or check for exsistence of pages. Can reduce DB queries; defualts to true.
+ 	* @param string Page ID. If specified, class="currentpage" will be added to links if they match the given page ID and namespace
+ 	* @param string Namespace. If specified, class="currentpage" will be added to links if they match the given page ID and namespace
+ 	* @return string
+ 	*/
+	
+	public static function parse_internal_links($text, $tplcode = false, $do_exist_check = true, $match_page_id = false, $match_namespace = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+	
+		$parser = is_string($tplcode) ? $template->makeParserText($tplcode) : false;
+		
+		// allow blank urlname?
+		$repeater = have_blank_urlname_page() ? '*' : '+';
+		
+		// stage 1 - links with alternate text
+		preg_match_all('/\[\[([^\[\]<>\{\}\|]' . $repeater . ')\|(.+?)\]\]/', $text, $matches);
+		foreach ( $matches[0] as $i => $match )
+		{
+			list($page_id, $namespace) = RenderMan::strToPageID($matches[1][$i]);
+			$link = self::generate_internal_link($namespace, $page_id, $matches[2][$i], $match, $parser, $do_exist_check, $match_page_id, $match_namespace);
+			$text = str_replace($match, $link, $text);
+		}
+		
+		// stage 2 - links with no alternate text
+		preg_match_all('/\[\[([^\[\]<>\{\}\|]' . $repeater . ')\]\]/', $text, $matches);
+		foreach ( $matches[0] as $i => $match )
+		{
+			list($page_id, $namespace) = RenderMan::strToPageID($matches[1][$i]);
+			$pid_clean = $paths->nslist[$namespace] . sanitize_page_id($page_id);
+			$inner_text = ( isPage($pid_clean) ) ? htmlspecialchars(get_page_title($pid_clean)) : htmlspecialchars($matches[1][$i]);
+			
+			$link = self::generate_internal_link($namespace, $page_id, $inner_text, $match, $parser, $do_exist_check, $match_page_id, $match_namespace);
+			
+			$text = str_replace($match, $link, $text);
+		}
+		
+		return $text;
+	}
+	
+	/**
+ 	* Internal link generation function
+ 	* @access private
+ 	* @return string HTML
+ 	*/
+	
+	private static function generate_internal_link($namespace, $page_id, $inner_text, $match, $parser = false, $do_exist_check = true, $match_page_id = false, $match_namespace = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( ($pos = strrpos($page_id, '#')) !== false )
+		{
+			$hash = substr($page_id, $pos);
+			$page_id = substr($page_id, 0, $pos);
+		}
+		else
+		{
+			$hash = '';
+		}
+		
+		if ( $namespace == 'Admin' )
+		{
+			// No linking directly to Admin pages!
+			$get = 'module=' . $paths->nslist[$namespace] . sanitize_page_id($page_id);
+			$pid_clean = $paths->nslist['Special'] . 'Administration';
+			$onclick = ' onclick="ajaxLoginNavTo(\'Special\', \'Administration\', USER_LEVEL_ADMIN, \'' . addslashes($get) . '\'); return false;"';
+		}
+		else
+		{
+			$get = false;
+			$onclick = '';
+			$pid_clean = $paths->nslist[$namespace] . sanitize_page_id($page_id);
+		}
+		
+		$url = makeUrl($pid_clean, $get, true) . $hash;
+		$quot = '"';
+		$exists = ( ($do_exist_check && isPage($pid_clean)) || !$do_exist_check ) ? '' : ' class="wikilink-nonexistent"';
+		
+		if ( $match_page_id && $match_namespace && $pid_clean === $paths->get_pathskey($match_page_id, $match_namespace) )
+			$exists .= ' class="currentpage"';
+		
+		if ( $parser )
+		{
+			$parser->assign_vars(array(
+					'HREF' => $url,
+					'FLAGS' => $exists,
+					'TEXT' => $inner_text
+				));
+			$link = $parser->run();
+		}
+		else
+		{
+			$omatch = self::escape_parser_hint_attrib($match);
+			$link = "<!--#internallink src=\"$omatch\" --><a{$onclick} href={$quot}{$url}{$quot}{$exists}>{$inner_text}</a><!--#/internallink-->";
+		}
+		
+		return $link;
+	}
+	
+	/**
+ 	* Parses a partial template tag in wikitext, and return an array with the parameters.
+ 	* @param string The portion of the template tag that contains the parameters.
+ 	* @example
+ 	* <code>
+ 	foo = lorem ipsum
+ 	bar = dolor sit amet
+ 	* </code>
+ 	* @return array Example:
+ 	* [foo] => lorem ipsum
+ 	* [bar] => dolor sit amet
+ 	*/
+	
+	public static function parse_template_vars($input, $newlinemode = true)
+	{
+		$parms = array();
+		$input = trim($input);
+		if ( $newlinemode )
+		{
+			// we're going by newlines.
+			// split by parameter, then parse each one individually
+			$input = explode("\n", str_replace("\r", '', $input));
+			$lastparam = '';
+			$i = 0;
+			foreach ( $input as $line )
+			{
+				if ( preg_match('/^ *\|? *([A-z0-9_]+) *= *(.+)$/', $line, $match) )
+				{
+					// new parameter, named
+					$parms[ $match[1] ] = $match[2];
+					$lastparam = $match[1];
+				}
+				else if ( preg_match('/^ *\| *(.+)$/', $line, $match) || $lastparam === '' )
+				{
+					$parms[ $i ] = $match[1];
+					$lastparam = $i;
+					$i++;
+				}
+				else
+				{
+					$parms[ $lastparam ] .= "\n$line";
+				}
+			}
+		}
+		else
+		{
+			$result = preg_match_all('/
+ 																(?:^|[ ]*)\|         # start of parameter - string start or series of spaces
+ 																[ ]*
+ 																(?:
+ 																	([A-z0-9_]+)       # variable name
+ 																	[ ]* = [ ]*        # assignment
+ 																)?                   # name section is optional - if the parameter name is not given, a numerical index is assigned
+ 																([^\|]+|.+?\n[ ]*\|) # value
+ 															/x', trim($input), $matches);
+			if ( $result )
+			{
+				$pi = 0;
+				for ( $i = 0; $i < count($matches[0]); $i++ )
+				{
+					$matches[1][$i] = trim($matches[1][$i]);
+					$parmname = !empty($matches[1][$i]) ? $matches[1][$i] : strval(++$pi);
+					$parms[ $parmname ] = $matches[2][$i];
+				}
+			}
+		}
+		// die('<pre>' . print_r($parms, true) . '</pre>');
+		return $parms;
+	}
+	
+	/**
+ 	* Processes all template tags within a block of wikitext.
+ 	* Updated in 1.0.2 to also parse template tags in the format of {{Foo |a = b |b = c |c = therefore, a}}
+ 	* @param string The text to process
+ 	* @return string Formatted text
+ 	* @example
+ 	* <code>
+ 	$text = '{{Template
+ 			| parm1 = Foo
+ 			| parm2 = Bar
+ 		}}';
+ 	$text = RenderMan::include_templates($text);
+ 	* </code>
+ 	*/
+	
+	public static function include_templates($text)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		// $template_regex = "/\{\{([^\]]+?)((\n([ ]*?)[A-z0-9]+([ ]*?)=([ ]*?)(.+?))*)\}\}/is";
+		// matches:
+		//  1 - template name
+		//  2 - parameter section
+		$template_regex = "/
+ 												\{\{                     # opening
+ 													([^\n\t\a\r]+)         # template name
+ 													((?:(?:[\s]+\|?)[ ]*(?:[A-z0-9_]+)[ ]*=[ ]*?(?:.+))*) # parameters
+ 												\}\}                     # closing
+ 											/isxU";
+		if ( $count = preg_match_all($template_regex, $text, $matches) )
+		{
+			// die('<pre>' . print_r($matches, true) . '</pre>');
+			for ( $i = 0; $i < $count; $i++ )
+			{
+				$matches[1][$i] = sanitize_page_id($matches[1][$i]);
+				$newlinemode = ( substr($matches[2][$i], 0, 1) == "\n" );
+				$parmsection = trim($matches[2][$i]);
+				if ( !empty($parmsection) )
+				{
+					$parms = RenderMan::parse_template_vars($parmsection, $newlinemode);
+					if ( !is_array($parms) )
+						// Syntax error
+						$parms = array();
+				}
+				else
+				{
+					$parms = Array();
+				}
+				if ( $tpl_code = RenderMan::fetch_template_text($matches[1][$i]) )
+				{
+					// Intelligent paragraphs.
+					// If:
+					//   * A line is fully wrapped in a <p> tag
+					//   * The line contains a variable
+					//   * The variable contains newlines
+					// Then:
+					//   * Drop the <p> tag, replace it fully paragraph-ized by newlines
+					
+					if ( preg_match_all('/^( *)<p(\s.+)?>(.*?\{([A-z0-9]+)\}.*?)<\/p> *$/m', $tpl_code, $paramatch) )
+					{
+						$parser = new Carpenter();
+						$parser->exclusive_rule('paragraph');
+						
+						foreach ( $paramatch[0] as $j => $match )
+						{
+							// $line is trimmed (the <p> is gone)
+							$spacing =& $paramatch[1][$i];
+							$para_attrs =& $paramatch[2][$j];
+							$para_attrs = str_replace(array('$', '\\'), array('\$', '\\\\'), $para_attrs);
+							$line =& $paramatch[3][$j];
+							$varname =& $paramatch[4][$j];
+							if ( isset($parms[$varname]) && strstr($parms[$varname], "\n") )
+							{
+								$newline = str_replace('{' . $varname . '}', $parms[$varname], $line);
+								$paraized = $parser->render($newline);
+								$paraized = preg_replace('/^<p>/m', "$spacing<p{$para_attrs}>", $paraized);
+								$paraized = $spacing . trim($paraized);
+								$tpl_code = str_replace_once($match, $paraized, $tpl_code);
+							}
+						}
+					}
+					
+					$parser = $template->makeParserText($tpl_code);
+					$parser->assign_vars($parms);
+					$text = str_replace($matches[0][$i], $parser->run(), $text);
+				}
+			}
+		}
+		return $text;
+	}
+	
+	/**
+ 	* Preprocesses an HTML text string prior to being sent to MySQL.
+ 	* @param string $text
+ 	* @param bool $strip_all_php - if true, strips all PHP regardless of user permissions. Else, strips PHP only if user level < USER_LEVEL_ADMIN. Defaults to true.
+ 	* @param bool $sqlescape - if true, sends text through $db->escape(). Otherwise returns unescaped text. Defaults to true.
+ 	* @param bool $reduceheadings - if true, finds HTML headings and replaces them with wikitext. Else, does not touch headings. Defaults to true.
+ 	* @param Session_ACLPageInfo Optional permissions instance to check against, $session is used if not provided
+ 	*/
+	public static function preprocess_text($text, $strip_all_php = true, $sqlescape = true, $reduceheadings = true, $perms = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$random_id = md5( time() . mt_rand() );
+		
+		$code = $plugins->setHook('render_sanitize_pre');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		if ( !is_object($perms) )
+		{
+			$namespace = $paths->namespace;
+			$perms =& $session;
+		}
+		else
+		{
+			$namespace = $perms->namespace;
+		}
+		
+		$can_do_php = ( !$strip_all_php && $perms->get_permissions('php_in_pages') );
+		$can_do_html = $session->check_acl_scope('html_in_pages', $namespace) && $perms->get_permissions('html_in_pages');
+		
+		if ( $can_do_html && !$can_do_php )
+		{
+			$text = preg_replace('#<(\?|\?php|%)(.*?)(\?|%)>#is', '&lt;\\1\\2\\3&gt;', $text);
+		}
+		else if ( !$can_do_html && !$can_do_php )
+		{
+			$text = sanitize_html($text, true);
+			// If we can't do PHP, we can't do Javascript either.
+			$text = RenderMan::destroy_javascript($text);
+		}
+		
+		// Strip out <nowiki> sections and PHP code
+		
+		$php = preg_match_all('#(<|&lt;)\?php(.*?)\?(>|&gt;)#is', $text, $phpsec);
+		
+		//die('<pre>'.htmlspecialchars(print_r($phpsec, true))."\n".htmlspecialchars(print_r($text, true)).'</pre>');
+		
+		for($i=0;$i<sizeof($phpsec[1]);$i++)
+		{
+			$text = str_replace($phpsec[0][$i], '{PHP:'.$random_id.':'.$i.'}', $text);
+		}
+		
+		$nw = preg_match_all('#<nowiki>(.*?)<\/nowiki>#is', $text, $nowiki);
+		
+		for($i=0;$i<sizeof($nowiki[1]);$i++)
+		{
+			$text = str_replace('<nowiki>'.$nowiki[1][$i].'</nowiki>', '{NOWIKI:'.$random_id.':'.$i.'}', $text);
+		}
+		
+		$text = str_replace('~~~~~', enano_date('G:i, j F Y (T)'), $text);
+		$text = str_replace('~~~~', "[[User:$session->username|$session->username]] ".enano_date('G:i, j F Y (T)'), $text);
+		$text = str_replace('~~~', "[[User:$session->username|$session->username]] ", $text);
+		
+		$code = $plugins->setHook('render_sanitize_post');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		// gently apply some reverse-processing to allow the parser to do magic with TOCs and stuff
+		if ( $reduceheadings )
+			$text = self::reverse_process_headings($text);
+		
+		// Reinsert <nowiki> sections
+		for($i=0;$i<$nw;$i++)
+		{
+			$text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', '<nowiki>'.$nowiki[1][$i].'</nowiki>', $text);
+		}
+		// Reinsert PHP
+		for($i=0;$i<$php;$i++)
+		{
+			$phsec = ''.$phpsec[1][$i].'?php'.$phpsec[2][$i].'?'.$phpsec[3][$i].'';
+			if ( $strip_all_php )
+				$phsec = htmlspecialchars($phsec);
+			$text = str_replace('{PHP:'.$random_id.':'.$i.'}', $phsec, $text);
+		}
+		
+		$text = ( $sqlescape ) ? $db->escape($text) : $text;
+		
+		return $text;
+	}
+	
+	public static function smilieyize($text, $complete_urls = false)
+	{
+		
+		$random_id = md5( time() . mt_rand() );
+		
+		// Smileys array - eventually this will be fetched from the database by
+		// RenderMan::initSmileys during initialization, but it will all be hardcoded for beta 2
+		
+		$smileys = Array(
+			'O:-)'    => 'face-angel.png',
+			'O:)'     => 'face-angel.png',
+			'O=)'     => 'face-angel.png',
+			':-)'     => 'face-smile.png',
+			':)'      => 'face-smile.png',
+			'=)'      => 'face-smile-big.png',
+			':-('     => 'face-sad.png',
+			':('      => 'face-sad.png',
+			';('      => 'face-sad.png',
+			':-O'     => 'face-surprise.png',
+			';-)'     => 'face-wink.png',
+			';)'      => 'face-wink.png',
+			'8-)'     => 'face-glasses.png',
+			'8)'      => 'face-glasses.png',
+			':-D'     => 'face-grin.png',
+			':D'      => 'face-grin.png',
+			'=D'      => 'face-grin.png',
+			':-*'     => 'face-kiss.png',
+			':*'      => 'face-kiss.png',
+			'=*'      => 'face-kiss.png',
+			':\'('    => 'face-crying.png',
+			':-|'     => 'face-plain.png',
+			':-\\'    => 'face-plain.png',
+			':-/'     => 'face-plain.png',
+			':joke:'  => 'face-plain.png',
+			']:-&gt;' => 'face-devil-grin.png',
+			']:->'    => 'face-devil-grin.png',
+			':kiss:'  => 'face-kiss.png',
+			':-P'     => 'face-tongue-out.png',
+			':P'      => 'face-tongue-out.png',
+			':-p'     => 'face-tongue-out.png',
+			':p'      => 'face-tongue-out.png',
+			':-X'     => 'face-sick.png',
+			':X'      => 'face-sick.png',
+			':sick:'  => 'face-sick.png',
+			':-]'     => 'face-oops.png',
+			':]'      => 'face-oops.png',
+			':oops:'  => 'face-oops.png',
+			':-['     => 'face-embarassed.png',
+			':['      => 'face-embarassed.png'
+			);
+		
+		// Strip out <nowiki> sections
+		//return '<pre>'.htmlspecialchars($text).'</pre>';
+		$nw = preg_match_all('#<nowiki>(.*?)<\/nowiki>#is', $text, $nowiki);
+		
+		for ( $i = 0; $i < count($nowiki[1]); $i++ )
+		{
+			$text = str_replace('<nowiki>' . $nowiki[1][$i] . '</nowiki>', '{NOWIKI:'.$random_id.':'.$i.'}', $text);
+		}
+		
+		foreach ( $smileys as $smiley => $smiley_path )
+		{
+			$hex_smiley = hexencode($smiley, '&#x', ';');
+			$pfx = ( $complete_urls ) ? get_server_url() : '';
+			$text = str_replace(' ' . $smiley,
+					' <!--#smiley code="' . self::escape_parser_hint_attrib($smiley) . '"--><nowiki>
+ 					<!-- The above is a reverse-parser hint -->
+ 						<img title="' . $hex_smiley . '" alt="' . $hex_smiley . '" src="' . $pfx . scriptPath . '/images/smilies/' . $smiley_path . '"
+							style="border: 0;" />
+ 					</nowiki><!--#/smiley-->', $text);
+		}
+		//*/
+		
+		// Reinsert <nowiki> sections
+		for ( $i = 0; $i < $nw; $i++ )
+		{
+			$text = str_replace('{NOWIKI:'.$random_id.':'.$i.'}', '<nowiki>'.$nowiki[1][$i].'</nowiki>', $text);
+		}
+		
+		return $text;
+	}
+	
+	/**
+ 	* Generates a summary of the differences between two texts, and formats it as XHTML.
+ 	* @param $str1 string the first block of text
+ 	* @param $str2 string the second block of text
+ 	* @return string
+ 	*/
+	public static function diff($str1, $str2)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		require_once(ENANO_ROOT.'/includes/diff.php');
+		$str1 = explode("\n", $str1);
+		$str2 = explode("\n", $str2);
+		$diff = new Diff($str1, $str2);
+		$renderer = new TableDiffFormatter();
+		return '<table class="diff">'.$renderer->format($diff).'</table>';
+	}
+	
+	/**
+ 	* Changes wikitext image tags to HTML.
+ 	* @param string The wikitext to process
+ 	* @param array Will be overwritten with the list of HTML tags (the system uses tokens for TextWiki compatibility)
+ 	* @return string
+ 	*/
+	
+	public static function process_image_tags($text, &$taglist)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$s_delim = "\xFF";
+		$f_delim = "\xFF";
+		$taglist = array();
+		
+		// Wicked huh?
+		$ns_file = str_replace('/', '\\/', preg_quote($paths->nslist['File']));
+		$regex = '/
+ 					\[\[                                                                  # starting delimiter 
+ 					:' . $ns_file . '([\w\s0-9_\(\)!@%\^\+\|\.-]+?\.(?:png|gif|jpg|jpeg)) # image filename
+ 					(?:(?:\|(?:.+?))*)                                                    # parameters
+ 					\]\]                                                                  # ending delimiter
+ 					/ix';
+		
+		preg_match_all($regex, $text, $matches);
+		
+		foreach ( $matches[0] as $i => $match )
+		{
+			
+			$full_tag   =& $matches[0][$i];
+			$filename   =& $matches[1][$i];
+			
+			// apply recursion (hack? @todo could this be done with (?R) in PCRE?)
+			$tag_pos = strpos($text, $full_tag);
+			$tag_end_pos = $tag_pos + strlen($full_tag);
+			while ( get_char_count($full_tag, ']') < get_char_count($full_tag, '[') && $tag_end_pos < strlen($text) )
+			{
+				$full_tag .= substr($text, $tag_end_pos, 1);
+				$tag_end_pos++;
+			}
+			if ( $tag_end_pos > strlen($text) )
+			{
+				// discard tag, not closed fully
+				continue;
+			}
+			
+			// init the various image parameters
+			$width = null;
+			$height = null;
+			$scale_type = null;
+			$raw_display = false;
+			$clear = null;
+			$caption = null;
+			
+			// trim tag and parse particles
+			$tag_trim = rtrim(ltrim($full_tag, '['), ']');
+			// trim off the filename from the start of the tag
+			$filepart_len = 1 + strlen($paths->nslist['File']) + strlen($filename) + 1;
+			$tag_trim = substr($tag_trim, $filepart_len);
+			// explode and we should have parameters
+			$tag_parts = explode('|', $tag_trim);
+			
+			// for each of the parameters, see if it matches a known option. If so, apply it;
+			// otherwise, see if a plugin reserved that parameter and if not treat it as the caption
+			foreach ( $tag_parts as $param )
+			{
+				switch($param)
+				{
+					case 'left':
+					case 'right':
+						$clear = $param;
+						break;
+					case 'thumb':
+						$scale_type = 'thumb';
+						break;
+					case 'raw':
+						$raw_display = true;
+						break;
+					default:
+						// height specification
+						if ( preg_match('/^([0-9]+)x([0-9]+)$/', $param, $dims) )
+						{
+							$width = intval($dims[1]);
+							$height = intval($dims[2]);
+							break;
+						}
+						// not the height, so see if a plugin took this over
+						// this hook requires plugins to return true if they modified anything
+						$code = $plugins->setHook('img_tag_parse_params');
+						foreach ( $code as $cmd )
+						{
+							if ( eval($cmd) )
+								break 2;
+						}
+						// we would have broken out by now if a plugin properly handled this,
+						// so just set the caption now.
+						$caption = $param;
+						break;
+				}
+			}
+			
+			if ( !isPage( $paths->nslist['File'] . $filename ) )
+			{
+				$text = str_replace($full_tag, '[[' . $paths->nslist['File'] . $filename . ']]', $text);
+				continue;
+			}
+			
+			if ( $scale_type == 'thumb' )
+			{
+				$r_width  = 225;
+				$r_height = 225;
+				
+				$url = makeUrlNS('Special', 'DownloadFile/' . $filename, 'preview&width=' . $r_width . '&height=' . $r_height, true);
+			}
+			else if ( !empty($width) && !empty($height) )
+			{
+				$r_width = $width;
+				$r_height = $height;
+				
+				$url = makeUrlNS('Special', 'DownloadFile/' . $filename, 'preview&width=' . $r_width . '&height=' . $r_height, true);
+			}
+			else
+			{
+				$url = makeUrlNS('Special', 'DownloadFile/' . $filename);
+			}
+			
+			$img_tag = '<img src="' . $url . '" ';
+			
+			// if ( isset($r_width) && isset($r_height) && $scale_type != '|thumb' )
+			// {
+			//   $img_tag .= 'width="' . $r_width . '" height="' . $r_height . '" ';
+			// }
+			
+			$img_tag .= 'style="border-width: 0px; /* background-color: white; */" ';
+			
+			$code = $plugins->setHook('img_tag_parse_img');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			
+			$img_tag .= '/>';
+			
+			$s_full_tag = self::escape_parser_hint_attrib($full_tag);
+			$complete_tag = '<!--#imagelink src="' . $s_full_tag . '" -->';
+			
+			if ( !empty($scale_type) && !$raw_display )
+			{
+				$complete_tag .= '<div class="thumbnail" ';
+				$clear_text = '';
+				if ( !empty($clear) )
+				{
+					$side = ( $clear == 'left' ) ? 'left' : 'right';
+					$opposite = ( $clear == 'left' ) ? 'right' : 'left';
+					$clear_text .= "float: $side; margin-$opposite: 20px; width: {$r_width}px;";
+					$complete_tag .= 'style="' . $clear_text . '" ';
+				}
+				$complete_tag .= '>';
+				
+				$complete_tag .= '<a href="' . makeUrlNS('File', $filename) . '" style="display: block;">';
+				$complete_tag .= $img_tag;
+				$complete_tag .= '</a>';
+				
+				$mag_button = '<a href="' . makeUrlNS('File', $filename) . '" style="display: block; float: right; clear: right; margin: 0 0 10px 10px;"><img alt="[ + ]" src="' . scriptPath . '/images/thumbnail.png" style="border-width: 0px;" /></a>';
+			
+				if ( !empty($caption) )
+				{
+					$complete_tag .= $mag_button . $caption;
+				}
+				
+				$complete_tag .= '</div>';
+			}
+			else if ( $raw_display )
+			{
+				$complete_tag .= "$img_tag";
+				$taglist[$i] = $complete_tag;
+				
+				$repl = "{$s_delim}e_img_{$i}{$f_delim}";
+				$text = str_replace($full_tag, $repl, $text);
+				continue;
+			}
+			else
+			{
+				$complete_tag .= '<a href="' . makeUrlNS('File', $filename) . '" style="display: block;"';
+				$code = $plugins->setHook('img_tag_parse_link');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				$complete_tag .= '>';
+				$complete_tag .= $img_tag;
+				$complete_tag .= '</a>';
+			}
+			
+			$complete_tag .= "<!--#/imagelink-->";
+			$taglist[$i] = $complete_tag;
+			
+			/*
+			$pos = strpos($text, $full_tag);
+			
+			while(true)
+			{
+				$check1 = substr($text, $pos, 3);
+				$check2 = substr($text, $pos, 1);
+				if ( $check1 == '<p>' || $pos == 0 || $check2 == "\n" )
+				{
+					// die('found at pos '.$pos);
+					break;
+				}
+				$pos--;
+			}
+			*/
+			
+			/*
+			$repl = "{$s_delim}e_img_{$i}{$f_delim}";
+			$text = substr($text, 0, $pos) . $repl . substr($text, $pos);
+			
+			$text = str_replace($full_tag, '', $text);
+			*/
+			$text = str_replace_once($full_tag, $complete_tag, $text);
+			
+			unset($full_tag, $filename, $scale_type, $width, $height, $clear, $caption, $r_width, $r_height);
+			
+		}
+		
+		// if ( count($matches[0]) > 0 )
+		//   die('<pre>' . htmlspecialchars($text) . '</pre>');
+		
+		return $text;
+	}
+	
+	/**
+ 	* Finalizes processing of image tags.
+ 	* @param string The preprocessed text
+ 	* @param array The list of image tags created by RenderMan::process_image_tags()
+ 	*/
+ 	
+	public static function process_imgtags_stage2($text, $taglist)
+	{
+		$s_delim = "\xFF";
+		$f_delim = "\xFF";
+		foreach ( $taglist as $i => $tag )
+		{
+			$repl = "{$s_delim}e_img_{$i}{$f_delim}";
+			$text = str_replace($repl, $tag, $text);
+		}               
+		return $text;
+	}
+	
 }
  
 ?>
--- a/includes/rijndael.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/rijndael.php	Sun Mar 28 23:10:46 2010 -0400
@@ -49,1355 +49,1355 @@
 
 class librijndael2
 {
-  //////////////////// CONVERSION FUNCTIONS ///////////////////////////
+	//////////////////// CONVERSION FUNCTIONS ///////////////////////////
 
-  // Byte to hex
-  static function byte2hex($byte)
-  {
-    if ( strlen(dechex(ord($byte))) < 2 )
-      return ( "0" . dechex(ord($byte)) );
-    else 
-      return ( dechex(ord($byte)) );
-  }
+	// Byte to hex
+	static function byte2hex($byte)
+	{
+		if ( strlen(dechex(ord($byte))) < 2 )
+			return ( "0" . dechex(ord($byte)) );
+		else 
+			return ( dechex(ord($byte)) );
+	}
 
 
-  /* Convert String to Hex String 
-   * Null byte cannot appear in middle of String.
-   * this function is necessary because toString(16) does not 
-   * produces two hex caracters for 0 to 15.
-   */
-  static function string2hex ($s) {
-    $hex = '';
-    for ($x = 0; $x < strlen($s); $x++) {
-      $byte = $s{$x};
-      $hex .= librijndael2::byte2hex($byte);
-    }
+	/* Convert String to Hex String 
+ 	* Null byte cannot appear in middle of String.
+ 	* this function is necessary because toString(16) does not 
+ 	* produces two hex caracters for 0 to 15.
+ 	*/
+	static function string2hex ($s) {
+		$hex = '';
+		for ($x = 0; $x < strlen($s); $x++) {
+			$byte = $s{$x};
+			$hex .= librijndael2::byte2hex($byte);
+		}
 
-    return $hex;
-  }
-           
-  /* Convert Hex String to String  
-   * 00 (NULL)  is not converted!
-   * Hex String must be even number of chars (A must be 0A, F must be 0F, etc)
-   */
-  static function hex2string($hex) {
-    $s = '';
-    if (strlen($hex) % 2 == 1) {
-      librijndael2::trace("Error: Hex String must be even number of chars");
-      return -1;
-    }
+		return $hex;
+	}
+ 					
+	/* Convert Hex String to String  
+ 	* 00 (NULL)  is not converted!
+ 	* Hex String must be even number of chars (A must be 0A, F must be 0F, etc)
+ 	*/
+	static function hex2string($hex) {
+		$s = '';
+		if (strlen($hex) % 2 == 1) {
+			librijndael2::trace("Error: Hex String must be even number of chars");
+			return -1;
+		}
 
-    for ( $i = 0; $i < strlen($hex); $i+=2 )
-    {
-      $byte = $hex{$i} . $hex{$i+1};
-      $s .= chr(hexdec($byte));
-    }
-          
-    return $s;
-  }
-  
-  static function trace($error)
-  {
-    // $bt = debug_backtrace();
-    echo("$error\n");
-    // echo '<pre>' . htmlspecialchars(print_r($bt, true)) . '</pre>';
-    
-    exit();
-  }
-  
-  static function parseInt($str)
-  {
-    if ( is_int($str) )
-      return $str;
-    if ( !is_string($str) )
-      librijndael2::trace('Error: non-string (' . gettype($str) . ') passed to librijndael2::parseInt(' . $str . ')');
-    if ( substr($str, 0, 2) == '0x' )
-    {
-      return ( preg_match('/^0x([a-f0-9][a-f0-9])+$/i', $str) ) ? eval("return $str;") : intval($str);
-    }
-    return intval($str);
-  }
-  
-  static function ord2hex($byte)
-  {
-    if ( !is_int($byte) )
-      librijndael2::trace('Error: non-integer passed to ord2hex()');
-    $result = strval(dechex($byte));
-    if ( strlen($result) < 2 )
-      $result = "0$result";
-    return $result;
-  }
+		for ( $i = 0; $i < strlen($hex); $i+=2 )
+		{
+			$byte = $hex{$i} . $hex{$i+1};
+			$s .= chr(hexdec($byte));
+		}
+					
+		return $s;
+	}
+	
+	static function trace($error)
+	{
+		// $bt = debug_backtrace();
+		echo("$error\n");
+		// echo '<pre>' . htmlspecialchars(print_r($bt, true)) . '</pre>';
+		
+		exit();
+	}
+	
+	static function parseInt($str)
+	{
+		if ( is_int($str) )
+			return $str;
+		if ( !is_string($str) )
+			librijndael2::trace('Error: non-string (' . gettype($str) . ') passed to librijndael2::parseInt(' . $str . ')');
+		if ( substr($str, 0, 2) == '0x' )
+		{
+			return ( preg_match('/^0x([a-f0-9][a-f0-9])+$/i', $str) ) ? eval("return $str;") : intval($str);
+		}
+		return intval($str);
+	}
+	
+	static function ord2hex($byte)
+	{
+		if ( !is_int($byte) )
+			librijndael2::trace('Error: non-integer passed to ord2hex()');
+		$result = strval(dechex($byte));
+		if ( strlen($result) < 2 )
+			$result = "0$result";
+		return $result;
+	}
 }
 
 class Crypt_Rijndael
 {
 
  //////////////////// TABLES, STRUCTURES... ////////////////////////
-   
-  var $Te0 = array(
-    0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d,
-    0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
-    0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
-    0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
-    0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87,
-    0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
-    0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea,
-    0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
-    0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
-    0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
-    0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108,
-    0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
-    0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e,
-    0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
-    0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
-    0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
-    0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e,
-    0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
-    0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce,
-    0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
-    0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
-    0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
-    0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b,
-    0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
-    0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16,
-    0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
-    0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
-    0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
-    0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a,
-    0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
-    0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163,
-    0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
-    0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
-    0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
-    0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47,
-    0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
-    0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f,
-    0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
-    0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
-    0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
-    0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e,
-    0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
-    0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6,
-    0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
-    0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
-    0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
-    0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25,
-    0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
-    0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72,
-    0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
-    0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
-    0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
-    0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa,
-    0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
-    0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0,
-    0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
-    0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
-    0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
-    0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920,
-    0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
-    0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17,
-    0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
-    0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
-    0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a
-  );
-  
-  var $Te1 = array(
-    0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b,
-    0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5,
-    0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b,
-    0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676,
-    0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d,
-    0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
-    0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf,
-    0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0,
-    0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626,
-    0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc,
-    0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1,
-    0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
-    0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3,
-    0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a,
-    0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2,
-    0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575,
-    0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a,
-    0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
-    0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3,
-    0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484,
-    0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded,
-    0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b,
-    0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939,
-    0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
-    0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb,
-    0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585,
-    0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f,
-    0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8,
-    0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f,
-    0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
-    0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121,
-    0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2,
-    0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec,
-    0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717,
-    0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d,
-    0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
-    0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc,
-    0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888,
-    0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414,
-    0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb,
-    0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a,
-    0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
-    0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262,
-    0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979,
-    0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d,
-    0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9,
-    0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea,
-    0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
-    0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e,
-    0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6,
-    0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f,
-    0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a,
-    0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666,
-    0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
-    0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9,
-    0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e,
-    0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111,
-    0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494,
-    0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9,
-    0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
-    0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d,
-    0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868,
-    0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f,
-    0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616     
-  );
-  
-  var $Te2 = array(
-    0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b,
-    0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5,
-    0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b,
-    0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76,
-    0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d,
-    0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
-    0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af,
-    0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0,
-    0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26,
-    0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc,
-    0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1,
-    0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
-    0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3,
-    0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a,
-    0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2,
-    0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75,
-    0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a,
-    0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
-    0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3,
-    0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384,
-    0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed,
-    0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b,
-    0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239,
-    0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
-    0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb,
-    0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185,
-    0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f,
-    0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8,
-    0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f,
-    0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
-    0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221,
-    0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2,
-    0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec,
-    0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17,
-    0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d,
-    0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
-    0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc,
-    0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88,
-    0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814,
-    0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb,
-    0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a,
-    0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
-    0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462,
-    0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279,
-    0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d,
-    0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9,
-    0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea,
-    0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
-    0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e,
-    0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6,
-    0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f,
-    0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a,
-    0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66,
-    0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
-    0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9,
-    0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e,
-    0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211,
-    0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394,
-    0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9,
-    0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
-    0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d,
-    0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068,
-    0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f,
-    0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16
-  );
-  
-  var $Te3 = array(
-    0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6,
-    0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491,
-    0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56,
-    0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec,
-    0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa,
-    0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
-    0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45,
-    0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b,
-    0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c,
-    0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83,
-    0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9,
-    0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
-    0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d,
-    0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f,
-    0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf,
-    0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea,
-    0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34,
-    0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
-    0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d,
-    0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713,
-    0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1,
-    0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6,
-    0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72,
-    0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
-    0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed,
-    0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411,
-    0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe,
-    0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b,
-    0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05,
-    0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
-    0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342,
-    0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf,
-    0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3,
-    0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e,
-    0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a,
-    0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
-    0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3,
-    0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b,
-    0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28,
-    0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad,
-    0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14,
-    0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
-    0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4,
-    0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2,
-    0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da,
-    0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049,
-    0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf,
-    0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
-    0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c,
-    0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197,
-    0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e,
-    0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f,
-    0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc,
-    0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
-    0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069,
-    0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927,
-    0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322,
-    0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733,
-    0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9,
-    0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
-    0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a,
-    0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0,
-    0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e,
-    0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c
-  );
-  
-  var $Te4 = array(
-    0x63636363, 0x7c7c7c7c, 0x77777777, 0x7b7b7b7b,
-    0xf2f2f2f2, 0x6b6b6b6b, 0x6f6f6f6f, 0xc5c5c5c5,
-    0x30303030, 0x01010101, 0x67676767, 0x2b2b2b2b,
-    0xfefefefe, 0xd7d7d7d7, 0xabababab, 0x76767676,
-    0xcacacaca, 0x82828282, 0xc9c9c9c9, 0x7d7d7d7d,
-    0xfafafafa, 0x59595959, 0x47474747, 0xf0f0f0f0,
-    0xadadadad, 0xd4d4d4d4, 0xa2a2a2a2, 0xafafafaf,
-    0x9c9c9c9c, 0xa4a4a4a4, 0x72727272, 0xc0c0c0c0,
-    0xb7b7b7b7, 0xfdfdfdfd, 0x93939393, 0x26262626,
-    0x36363636, 0x3f3f3f3f, 0xf7f7f7f7, 0xcccccccc,
-    0x34343434, 0xa5a5a5a5, 0xe5e5e5e5, 0xf1f1f1f1,
-    0x71717171, 0xd8d8d8d8, 0x31313131, 0x15151515,
-    0x04040404, 0xc7c7c7c7, 0x23232323, 0xc3c3c3c3,
-    0x18181818, 0x96969696, 0x05050505, 0x9a9a9a9a,
-    0x07070707, 0x12121212, 0x80808080, 0xe2e2e2e2,
-    0xebebebeb, 0x27272727, 0xb2b2b2b2, 0x75757575,
-    0x09090909, 0x83838383, 0x2c2c2c2c, 0x1a1a1a1a,
-    0x1b1b1b1b, 0x6e6e6e6e, 0x5a5a5a5a, 0xa0a0a0a0,
-    0x52525252, 0x3b3b3b3b, 0xd6d6d6d6, 0xb3b3b3b3,
-    0x29292929, 0xe3e3e3e3, 0x2f2f2f2f, 0x84848484,
-    0x53535353, 0xd1d1d1d1, 0x00000000, 0xedededed,
-    0x20202020, 0xfcfcfcfc, 0xb1b1b1b1, 0x5b5b5b5b,
-    0x6a6a6a6a, 0xcbcbcbcb, 0xbebebebe, 0x39393939,
-    0x4a4a4a4a, 0x4c4c4c4c, 0x58585858, 0xcfcfcfcf,
-    0xd0d0d0d0, 0xefefefef, 0xaaaaaaaa, 0xfbfbfbfb,
-    0x43434343, 0x4d4d4d4d, 0x33333333, 0x85858585,
-    0x45454545, 0xf9f9f9f9, 0x02020202, 0x7f7f7f7f,
-    0x50505050, 0x3c3c3c3c, 0x9f9f9f9f, 0xa8a8a8a8,
-    0x51515151, 0xa3a3a3a3, 0x40404040, 0x8f8f8f8f,
-    0x92929292, 0x9d9d9d9d, 0x38383838, 0xf5f5f5f5,
-    0xbcbcbcbc, 0xb6b6b6b6, 0xdadadada, 0x21212121,
-    0x10101010, 0xffffffff, 0xf3f3f3f3, 0xd2d2d2d2,
-    0xcdcdcdcd, 0x0c0c0c0c, 0x13131313, 0xecececec,
-    0x5f5f5f5f, 0x97979797, 0x44444444, 0x17171717,
-    0xc4c4c4c4, 0xa7a7a7a7, 0x7e7e7e7e, 0x3d3d3d3d,
-    0x64646464, 0x5d5d5d5d, 0x19191919, 0x73737373,
-    0x60606060, 0x81818181, 0x4f4f4f4f, 0xdcdcdcdc,
-    0x22222222, 0x2a2a2a2a, 0x90909090, 0x88888888,
-    0x46464646, 0xeeeeeeee, 0xb8b8b8b8, 0x14141414,
-    0xdededede, 0x5e5e5e5e, 0x0b0b0b0b, 0xdbdbdbdb,
-    0xe0e0e0e0, 0x32323232, 0x3a3a3a3a, 0x0a0a0a0a,
-    0x49494949, 0x06060606, 0x24242424, 0x5c5c5c5c,
-    0xc2c2c2c2, 0xd3d3d3d3, 0xacacacac, 0x62626262,
-    0x91919191, 0x95959595, 0xe4e4e4e4, 0x79797979,
-    0xe7e7e7e7, 0xc8c8c8c8, 0x37373737, 0x6d6d6d6d,
-    0x8d8d8d8d, 0xd5d5d5d5, 0x4e4e4e4e, 0xa9a9a9a9,
-    0x6c6c6c6c, 0x56565656, 0xf4f4f4f4, 0xeaeaeaea,
-    0x65656565, 0x7a7a7a7a, 0xaeaeaeae, 0x08080808,
-    0xbabababa, 0x78787878, 0x25252525, 0x2e2e2e2e,
-    0x1c1c1c1c, 0xa6a6a6a6, 0xb4b4b4b4, 0xc6c6c6c6,
-    0xe8e8e8e8, 0xdddddddd, 0x74747474, 0x1f1f1f1f,
-    0x4b4b4b4b, 0xbdbdbdbd, 0x8b8b8b8b, 0x8a8a8a8a,
-    0x70707070, 0x3e3e3e3e, 0xb5b5b5b5, 0x66666666,
-    0x48484848, 0x03030303, 0xf6f6f6f6, 0x0e0e0e0e,
-    0x61616161, 0x35353535, 0x57575757, 0xb9b9b9b9,
-    0x86868686, 0xc1c1c1c1, 0x1d1d1d1d, 0x9e9e9e9e,
-    0xe1e1e1e1, 0xf8f8f8f8, 0x98989898, 0x11111111,
-    0x69696969, 0xd9d9d9d9, 0x8e8e8e8e, 0x94949494,
-    0x9b9b9b9b, 0x1e1e1e1e, 0x87878787, 0xe9e9e9e9,
-    0xcececece, 0x55555555, 0x28282828, 0xdfdfdfdf,
-    0x8c8c8c8c, 0xa1a1a1a1, 0x89898989, 0x0d0d0d0d,
-    0xbfbfbfbf, 0xe6e6e6e6, 0x42424242, 0x68686868,
-    0x41414141, 0x99999999, 0x2d2d2d2d, 0x0f0f0f0f,
-    0xb0b0b0b0, 0x54545454, 0xbbbbbbbb, 0x16161616
-  );
-  
-  var $Td0 = array(
-    0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96,
-    0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
-    0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,
-    0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
-    0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1,
-    0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
-    0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da,
-    0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
-    0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,
-    0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
-    0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45,
-    0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
-    0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7,
-    0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
-    0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,
-    0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
-    0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1,
-    0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
-    0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75,
-    0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
-    0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,
-    0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,
-    0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77,
-    0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
-    0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000,
-    0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
-    0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927,
-    0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
-    0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e,
-    0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
-    0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d,
-    0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
-    0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,
-    0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
-    0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163,
-    0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
-    0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d,
-    0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,
-    0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,
-    0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
-    0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36,
-    0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
-    0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662,
-    0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
-    0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,
-    0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
-    0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8,
-    0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
-    0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6,
-    0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
-    0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,
-    0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
-    0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df,
-    0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
-    0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e,
-    0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
-    0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,
-    0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
-    0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf,
-    0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
-    0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f,
-    0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
-    0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190,
-    0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742
-  );
-  
-  var $Td1 = array(
-    0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e,
-    0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303,
-    0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c,
-    0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3,
-    0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0,
-    0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,
-    0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259,
-    0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8,
-    0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971,
-    0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a,
-    0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f,
-    0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,
-    0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8,
-    0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab,
-    0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708,
-    0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682,
-    0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2,
-    0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,
-    0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb,
-    0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10,
-    0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd,
-    0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015,
-    0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e,
-    0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,
-    0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000,
-    0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72,
-    0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39,
-    0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e,
-    0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91,
-    0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,
-    0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17,
-    0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9,
-    0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60,
-    0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e,
-    0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1,
-    0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,
-    0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1,
-    0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3,
-    0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964,
-    0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390,
-    0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b,
-    0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,
-    0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46,
-    0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af,
-    0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512,
-    0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb,
-    0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a,
-    0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8,
-    0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c,
-    0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266,
-    0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8,
-    0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6,
-    0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604,
-    0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,
-    0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41,
-    0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647,
-    0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c,
-    0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1,
-    0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737,
-    0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,
-    0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340,
-    0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95,
-    0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1,
-    0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857
-  );
-  
-  var $Td2 = array(
-    0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27,
-    0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3,
-    0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502,
-    0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562,
-    0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe,
-    0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,
-    0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552,
-    0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9,
-    0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9,
-    0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce,
-    0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253,
-    0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,
-    0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b,
-    0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655,
-    0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337,
-    0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16,
-    0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69,
-    0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,
-    0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6,
-    0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e,
-    0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6,
-    0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050,
-    0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9,
-    0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,
-    0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000,
-    0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a,
-    0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d,
-    0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436,
-    0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b,
-    0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,
-    0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b,
-    0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e,
-    0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f,
-    0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb,
-    0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4,
-    0xdccad731, 0x85104263, 0x22401397, 0x112084c6,
-    0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729,
-    0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1,
-    0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9,
-    0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233,
-    0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4,
-    0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,
-    0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e,
-    0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3,
-    0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25,
-    0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b,
-    0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f,
-    0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,
-    0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0,
-    0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2,
-    0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7,
-    0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791,
-    0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496,
-    0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,
-    0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b,
-    0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6,
-    0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13,
-    0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47,
-    0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7,
-    0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,
-    0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3,
-    0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d,
-    0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456,
-    0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8
-  );
-  
-  var $Td3 = array(
-    0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a,
-    0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b,
-    0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5,
-    0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5,
-    0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d,
-    0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,
-    0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95,
-    0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e,
-    0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27,
-    0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d,
-    0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562,
-    0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9,
-    0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752,
-    0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66,
-    0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3,
-    0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced,
-    0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e,
-    0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4,
-    0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4,
-    0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd,
-    0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d,
-    0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60,
-    0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767,
-    0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,
-    0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000,
-    0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c,
-    0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736,
-    0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24,
-    0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b,
-    0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,
-    0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12,
-    0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814,
-    0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3,
-    0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b,
-    0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8,
-    0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,
-    0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7,
-    0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077,
-    0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247,
-    0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22,
-    0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698,
-    0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,
-    0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254,
-    0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582,
-    0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf,
-    0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb,
-    0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883,
-    0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,
-    0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629,
-    0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035,
-    0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533,
-    0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17,
-    0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4,
-    0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,
-    0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb,
-    0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d,
-    0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb,
-    0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a,
-    0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73,
-    0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,
-    0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2,
-    0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff,
-    0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064,
-    0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0
-  );
-  
-  var $Td4 = array(
-    0x52525252, 0x09090909, 0x6a6a6a6a, 0xd5d5d5d5,
-    0x30303030, 0x36363636, 0xa5a5a5a5, 0x38383838,
-    0xbfbfbfbf, 0x40404040, 0xa3a3a3a3, 0x9e9e9e9e,
-    0x81818181, 0xf3f3f3f3, 0xd7d7d7d7, 0xfbfbfbfb,
-    0x7c7c7c7c, 0xe3e3e3e3, 0x39393939, 0x82828282,
-    0x9b9b9b9b, 0x2f2f2f2f, 0xffffffff, 0x87878787,
-    0x34343434, 0x8e8e8e8e, 0x43434343, 0x44444444,
-    0xc4c4c4c4, 0xdededede, 0xe9e9e9e9, 0xcbcbcbcb,
-    0x54545454, 0x7b7b7b7b, 0x94949494, 0x32323232,
-    0xa6a6a6a6, 0xc2c2c2c2, 0x23232323, 0x3d3d3d3d,
-    0xeeeeeeee, 0x4c4c4c4c, 0x95959595, 0x0b0b0b0b,
-    0x42424242, 0xfafafafa, 0xc3c3c3c3, 0x4e4e4e4e,
-    0x08080808, 0x2e2e2e2e, 0xa1a1a1a1, 0x66666666,
-    0x28282828, 0xd9d9d9d9, 0x24242424, 0xb2b2b2b2,
-    0x76767676, 0x5b5b5b5b, 0xa2a2a2a2, 0x49494949,
-    0x6d6d6d6d, 0x8b8b8b8b, 0xd1d1d1d1, 0x25252525,
-    0x72727272, 0xf8f8f8f8, 0xf6f6f6f6, 0x64646464,
-    0x86868686, 0x68686868, 0x98989898, 0x16161616,
-    0xd4d4d4d4, 0xa4a4a4a4, 0x5c5c5c5c, 0xcccccccc,
-    0x5d5d5d5d, 0x65656565, 0xb6b6b6b6, 0x92929292,
-    0x6c6c6c6c, 0x70707070, 0x48484848, 0x50505050,
-    0xfdfdfdfd, 0xedededed, 0xb9b9b9b9, 0xdadadada,
-    0x5e5e5e5e, 0x15151515, 0x46464646, 0x57575757,
-    0xa7a7a7a7, 0x8d8d8d8d, 0x9d9d9d9d, 0x84848484,
-    0x90909090, 0xd8d8d8d8, 0xabababab, 0x00000000,
-    0x8c8c8c8c, 0xbcbcbcbc, 0xd3d3d3d3, 0x0a0a0a0a,
-    0xf7f7f7f7, 0xe4e4e4e4, 0x58585858, 0x05050505,
-    0xb8b8b8b8, 0xb3b3b3b3, 0x45454545, 0x06060606,
-    0xd0d0d0d0, 0x2c2c2c2c, 0x1e1e1e1e, 0x8f8f8f8f,
-    0xcacacaca, 0x3f3f3f3f, 0x0f0f0f0f, 0x02020202,
-    0xc1c1c1c1, 0xafafafaf, 0xbdbdbdbd, 0x03030303,
-    0x01010101, 0x13131313, 0x8a8a8a8a, 0x6b6b6b6b,
-    0x3a3a3a3a, 0x91919191, 0x11111111, 0x41414141,
-    0x4f4f4f4f, 0x67676767, 0xdcdcdcdc, 0xeaeaeaea,
-    0x97979797, 0xf2f2f2f2, 0xcfcfcfcf, 0xcececece,
-    0xf0f0f0f0, 0xb4b4b4b4, 0xe6e6e6e6, 0x73737373,
-    0x96969696, 0xacacacac, 0x74747474, 0x22222222,
-    0xe7e7e7e7, 0xadadadad, 0x35353535, 0x85858585,
-    0xe2e2e2e2, 0xf9f9f9f9, 0x37373737, 0xe8e8e8e8,
-    0x1c1c1c1c, 0x75757575, 0xdfdfdfdf, 0x6e6e6e6e,
-    0x47474747, 0xf1f1f1f1, 0x1a1a1a1a, 0x71717171,
-    0x1d1d1d1d, 0x29292929, 0xc5c5c5c5, 0x89898989,
-    0x6f6f6f6f, 0xb7b7b7b7, 0x62626262, 0x0e0e0e0e,
-    0xaaaaaaaa, 0x18181818, 0xbebebebe, 0x1b1b1b1b,
-    0xfcfcfcfc, 0x56565656, 0x3e3e3e3e, 0x4b4b4b4b,
-    0xc6c6c6c6, 0xd2d2d2d2, 0x79797979, 0x20202020,
-    0x9a9a9a9a, 0xdbdbdbdb, 0xc0c0c0c0, 0xfefefefe,
-    0x78787878, 0xcdcdcdcd, 0x5a5a5a5a, 0xf4f4f4f4,
-    0x1f1f1f1f, 0xdddddddd, 0xa8a8a8a8, 0x33333333,
-    0x88888888, 0x07070707, 0xc7c7c7c7, 0x31313131,
-    0xb1b1b1b1, 0x12121212, 0x10101010, 0x59595959,
-    0x27272727, 0x80808080, 0xecececec, 0x5f5f5f5f,
-    0x60606060, 0x51515151, 0x7f7f7f7f, 0xa9a9a9a9,
-    0x19191919, 0xb5b5b5b5, 0x4a4a4a4a, 0x0d0d0d0d,
-    0x2d2d2d2d, 0xe5e5e5e5, 0x7a7a7a7a, 0x9f9f9f9f,
-    0x93939393, 0xc9c9c9c9, 0x9c9c9c9c, 0xefefefef,
-    0xa0a0a0a0, 0xe0e0e0e0, 0x3b3b3b3b, 0x4d4d4d4d,
-    0xaeaeaeae, 0x2a2a2a2a, 0xf5f5f5f5, 0xb0b0b0b0,
-    0xc8c8c8c8, 0xebebebeb, 0xbbbbbbbb, 0x3c3c3c3c,
-    0x83838383, 0x53535353, 0x99999999, 0x61616161,
-    0x17171717, 0x2b2b2b2b, 0x04040404, 0x7e7e7e7e,
-    0xbabababa, 0x77777777, 0xd6d6d6d6, 0x26262626,
-    0xe1e1e1e1, 0x69696969, 0x14141414, 0x63636363,
-    0x55555555, 0x21212121, 0x0c0c0c0c, 0x7d7d7d7d
-  );
-  
-  var $rcon = array(
-    0x01000000, 0x02000000, 0x04000000, 0x08000000,
-    0x10000000, 0x20000000, 0x40000000, 0x80000000,
-    0x1B000000, 0x36000000
-  );
+ 	
+	var $Te0 = array(
+		0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d,
+		0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
+		0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d,
+		0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
+		0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87,
+		0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
+		0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea,
+		0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
+		0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
+		0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
+		0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108,
+		0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
+		0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e,
+		0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
+		0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
+		0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
+		0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e,
+		0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
+		0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce,
+		0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
+		0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c,
+		0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
+		0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b,
+		0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
+		0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16,
+		0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
+		0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81,
+		0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
+		0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a,
+		0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
+		0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163,
+		0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
+		0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
+		0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
+		0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47,
+		0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
+		0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f,
+		0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
+		0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
+		0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
+		0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e,
+		0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
+		0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6,
+		0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
+		0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
+		0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
+		0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25,
+		0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
+		0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72,
+		0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
+		0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
+		0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
+		0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa,
+		0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
+		0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0,
+		0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
+		0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
+		0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
+		0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920,
+		0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
+		0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17,
+		0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
+		0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
+		0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a
+	);
+	
+	var $Te1 = array(
+		0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b,
+		0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5,
+		0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b,
+		0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676,
+		0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d,
+		0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0,
+		0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf,
+		0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0,
+		0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626,
+		0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc,
+		0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1,
+		0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
+		0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3,
+		0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a,
+		0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2,
+		0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575,
+		0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a,
+		0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
+		0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3,
+		0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484,
+		0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded,
+		0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b,
+		0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939,
+		0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
+		0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb,
+		0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585,
+		0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f,
+		0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8,
+		0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f,
+		0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5,
+		0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121,
+		0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2,
+		0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec,
+		0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717,
+		0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d,
+		0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
+		0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc,
+		0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888,
+		0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414,
+		0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb,
+		0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a,
+		0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c,
+		0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262,
+		0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979,
+		0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d,
+		0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9,
+		0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea,
+		0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
+		0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e,
+		0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6,
+		0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f,
+		0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a,
+		0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666,
+		0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e,
+		0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9,
+		0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e,
+		0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111,
+		0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494,
+		0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9,
+		0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
+		0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d,
+		0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868,
+		0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f,
+		0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616     
+	);
+	
+	var $Te2 = array(
+		0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b,
+		0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5,
+		0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b,
+		0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76,
+		0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d,
+		0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
+		0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af,
+		0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0,
+		0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26,
+		0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc,
+		0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1,
+		0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
+		0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3,
+		0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a,
+		0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2,
+		0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75,
+		0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a,
+		0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
+		0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3,
+		0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384,
+		0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed,
+		0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b,
+		0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239,
+		0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
+		0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb,
+		0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185,
+		0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f,
+		0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8,
+		0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f,
+		0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
+		0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221,
+		0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2,
+		0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec,
+		0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17,
+		0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d,
+		0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
+		0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc,
+		0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88,
+		0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814,
+		0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb,
+		0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a,
+		0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c,
+		0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462,
+		0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279,
+		0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d,
+		0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9,
+		0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea,
+		0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008,
+		0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e,
+		0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6,
+		0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f,
+		0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a,
+		0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66,
+		0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e,
+		0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9,
+		0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e,
+		0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211,
+		0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394,
+		0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9,
+		0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
+		0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d,
+		0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068,
+		0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f,
+		0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16
+	);
+	
+	var $Te3 = array(
+		0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6,
+		0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491,
+		0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56,
+		0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec,
+		0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa,
+		0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
+		0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45,
+		0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b,
+		0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c,
+		0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83,
+		0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9,
+		0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
+		0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d,
+		0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f,
+		0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf,
+		0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea,
+		0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34,
+		0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
+		0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d,
+		0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713,
+		0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1,
+		0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6,
+		0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72,
+		0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
+		0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed,
+		0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411,
+		0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe,
+		0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b,
+		0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05,
+		0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
+		0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342,
+		0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf,
+		0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3,
+		0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e,
+		0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a,
+		0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
+		0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3,
+		0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b,
+		0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28,
+		0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad,
+		0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14,
+		0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8,
+		0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4,
+		0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2,
+		0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da,
+		0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049,
+		0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf,
+		0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810,
+		0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c,
+		0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197,
+		0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e,
+		0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f,
+		0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc,
+		0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c,
+		0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069,
+		0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927,
+		0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322,
+		0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733,
+		0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9,
+		0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
+		0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a,
+		0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0,
+		0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e,
+		0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c
+	);
+	
+	var $Te4 = array(
+		0x63636363, 0x7c7c7c7c, 0x77777777, 0x7b7b7b7b,
+		0xf2f2f2f2, 0x6b6b6b6b, 0x6f6f6f6f, 0xc5c5c5c5,
+		0x30303030, 0x01010101, 0x67676767, 0x2b2b2b2b,
+		0xfefefefe, 0xd7d7d7d7, 0xabababab, 0x76767676,
+		0xcacacaca, 0x82828282, 0xc9c9c9c9, 0x7d7d7d7d,
+		0xfafafafa, 0x59595959, 0x47474747, 0xf0f0f0f0,
+		0xadadadad, 0xd4d4d4d4, 0xa2a2a2a2, 0xafafafaf,
+		0x9c9c9c9c, 0xa4a4a4a4, 0x72727272, 0xc0c0c0c0,
+		0xb7b7b7b7, 0xfdfdfdfd, 0x93939393, 0x26262626,
+		0x36363636, 0x3f3f3f3f, 0xf7f7f7f7, 0xcccccccc,
+		0x34343434, 0xa5a5a5a5, 0xe5e5e5e5, 0xf1f1f1f1,
+		0x71717171, 0xd8d8d8d8, 0x31313131, 0x15151515,
+		0x04040404, 0xc7c7c7c7, 0x23232323, 0xc3c3c3c3,
+		0x18181818, 0x96969696, 0x05050505, 0x9a9a9a9a,
+		0x07070707, 0x12121212, 0x80808080, 0xe2e2e2e2,
+		0xebebebeb, 0x27272727, 0xb2b2b2b2, 0x75757575,
+		0x09090909, 0x83838383, 0x2c2c2c2c, 0x1a1a1a1a,
+		0x1b1b1b1b, 0x6e6e6e6e, 0x5a5a5a5a, 0xa0a0a0a0,
+		0x52525252, 0x3b3b3b3b, 0xd6d6d6d6, 0xb3b3b3b3,
+		0x29292929, 0xe3e3e3e3, 0x2f2f2f2f, 0x84848484,
+		0x53535353, 0xd1d1d1d1, 0x00000000, 0xedededed,
+		0x20202020, 0xfcfcfcfc, 0xb1b1b1b1, 0x5b5b5b5b,
+		0x6a6a6a6a, 0xcbcbcbcb, 0xbebebebe, 0x39393939,
+		0x4a4a4a4a, 0x4c4c4c4c, 0x58585858, 0xcfcfcfcf,
+		0xd0d0d0d0, 0xefefefef, 0xaaaaaaaa, 0xfbfbfbfb,
+		0x43434343, 0x4d4d4d4d, 0x33333333, 0x85858585,
+		0x45454545, 0xf9f9f9f9, 0x02020202, 0x7f7f7f7f,
+		0x50505050, 0x3c3c3c3c, 0x9f9f9f9f, 0xa8a8a8a8,
+		0x51515151, 0xa3a3a3a3, 0x40404040, 0x8f8f8f8f,
+		0x92929292, 0x9d9d9d9d, 0x38383838, 0xf5f5f5f5,
+		0xbcbcbcbc, 0xb6b6b6b6, 0xdadadada, 0x21212121,
+		0x10101010, 0xffffffff, 0xf3f3f3f3, 0xd2d2d2d2,
+		0xcdcdcdcd, 0x0c0c0c0c, 0x13131313, 0xecececec,
+		0x5f5f5f5f, 0x97979797, 0x44444444, 0x17171717,
+		0xc4c4c4c4, 0xa7a7a7a7, 0x7e7e7e7e, 0x3d3d3d3d,
+		0x64646464, 0x5d5d5d5d, 0x19191919, 0x73737373,
+		0x60606060, 0x81818181, 0x4f4f4f4f, 0xdcdcdcdc,
+		0x22222222, 0x2a2a2a2a, 0x90909090, 0x88888888,
+		0x46464646, 0xeeeeeeee, 0xb8b8b8b8, 0x14141414,
+		0xdededede, 0x5e5e5e5e, 0x0b0b0b0b, 0xdbdbdbdb,
+		0xe0e0e0e0, 0x32323232, 0x3a3a3a3a, 0x0a0a0a0a,
+		0x49494949, 0x06060606, 0x24242424, 0x5c5c5c5c,
+		0xc2c2c2c2, 0xd3d3d3d3, 0xacacacac, 0x62626262,
+		0x91919191, 0x95959595, 0xe4e4e4e4, 0x79797979,
+		0xe7e7e7e7, 0xc8c8c8c8, 0x37373737, 0x6d6d6d6d,
+		0x8d8d8d8d, 0xd5d5d5d5, 0x4e4e4e4e, 0xa9a9a9a9,
+		0x6c6c6c6c, 0x56565656, 0xf4f4f4f4, 0xeaeaeaea,
+		0x65656565, 0x7a7a7a7a, 0xaeaeaeae, 0x08080808,
+		0xbabababa, 0x78787878, 0x25252525, 0x2e2e2e2e,
+		0x1c1c1c1c, 0xa6a6a6a6, 0xb4b4b4b4, 0xc6c6c6c6,
+		0xe8e8e8e8, 0xdddddddd, 0x74747474, 0x1f1f1f1f,
+		0x4b4b4b4b, 0xbdbdbdbd, 0x8b8b8b8b, 0x8a8a8a8a,
+		0x70707070, 0x3e3e3e3e, 0xb5b5b5b5, 0x66666666,
+		0x48484848, 0x03030303, 0xf6f6f6f6, 0x0e0e0e0e,
+		0x61616161, 0x35353535, 0x57575757, 0xb9b9b9b9,
+		0x86868686, 0xc1c1c1c1, 0x1d1d1d1d, 0x9e9e9e9e,
+		0xe1e1e1e1, 0xf8f8f8f8, 0x98989898, 0x11111111,
+		0x69696969, 0xd9d9d9d9, 0x8e8e8e8e, 0x94949494,
+		0x9b9b9b9b, 0x1e1e1e1e, 0x87878787, 0xe9e9e9e9,
+		0xcececece, 0x55555555, 0x28282828, 0xdfdfdfdf,
+		0x8c8c8c8c, 0xa1a1a1a1, 0x89898989, 0x0d0d0d0d,
+		0xbfbfbfbf, 0xe6e6e6e6, 0x42424242, 0x68686868,
+		0x41414141, 0x99999999, 0x2d2d2d2d, 0x0f0f0f0f,
+		0xb0b0b0b0, 0x54545454, 0xbbbbbbbb, 0x16161616
+	);
+	
+	var $Td0 = array(
+		0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96,
+		0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
+		0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,
+		0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
+		0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1,
+		0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
+		0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da,
+		0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
+		0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,
+		0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
+		0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45,
+		0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
+		0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7,
+		0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
+		0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,
+		0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c,
+		0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1,
+		0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a,
+		0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75,
+		0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
+		0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,
+		0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff,
+		0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77,
+		0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb,
+		0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000,
+		0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
+		0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927,
+		0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
+		0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e,
+		0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
+		0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d,
+		0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
+		0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,
+		0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
+		0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163,
+		0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
+		0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d,
+		0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0,
+		0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,
+		0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
+		0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36,
+		0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
+		0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662,
+		0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
+		0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,
+		0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
+		0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8,
+		0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
+		0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6,
+		0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
+		0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,
+		0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
+		0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df,
+		0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
+		0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e,
+		0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
+		0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,
+		0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
+		0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf,
+		0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
+		0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f,
+		0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
+		0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190,
+		0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742
+	);
+	
+	var $Td1 = array(
+		0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e,
+		0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303,
+		0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c,
+		0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3,
+		0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0,
+		0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,
+		0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259,
+		0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8,
+		0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971,
+		0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a,
+		0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f,
+		0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,
+		0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8,
+		0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab,
+		0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708,
+		0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682,
+		0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2,
+		0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,
+		0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb,
+		0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10,
+		0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd,
+		0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015,
+		0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e,
+		0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,
+		0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000,
+		0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72,
+		0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39,
+		0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e,
+		0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91,
+		0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,
+		0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17,
+		0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9,
+		0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60,
+		0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e,
+		0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1,
+		0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,
+		0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1,
+		0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3,
+		0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964,
+		0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390,
+		0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b,
+		0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,
+		0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46,
+		0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af,
+		0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512,
+		0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb,
+		0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a,
+		0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8,
+		0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c,
+		0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266,
+		0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8,
+		0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6,
+		0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604,
+		0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,
+		0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41,
+		0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647,
+		0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c,
+		0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1,
+		0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737,
+		0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,
+		0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340,
+		0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95,
+		0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1,
+		0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857
+	);
+	
+	var $Td2 = array(
+		0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27,
+		0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3,
+		0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502,
+		0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562,
+		0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe,
+		0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,
+		0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552,
+		0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9,
+		0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9,
+		0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce,
+		0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253,
+		0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,
+		0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b,
+		0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655,
+		0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337,
+		0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16,
+		0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69,
+		0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,
+		0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6,
+		0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e,
+		0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6,
+		0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050,
+		0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9,
+		0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,
+		0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000,
+		0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a,
+		0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d,
+		0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436,
+		0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b,
+		0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,
+		0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b,
+		0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e,
+		0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f,
+		0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb,
+		0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4,
+		0xdccad731, 0x85104263, 0x22401397, 0x112084c6,
+		0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729,
+		0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1,
+		0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9,
+		0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233,
+		0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4,
+		0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,
+		0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e,
+		0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3,
+		0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25,
+		0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b,
+		0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f,
+		0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,
+		0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0,
+		0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2,
+		0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7,
+		0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791,
+		0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496,
+		0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,
+		0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b,
+		0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6,
+		0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13,
+		0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47,
+		0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7,
+		0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,
+		0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3,
+		0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d,
+		0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456,
+		0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8
+	);
+	
+	var $Td3 = array(
+		0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a,
+		0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b,
+		0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5,
+		0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5,
+		0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d,
+		0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,
+		0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95,
+		0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e,
+		0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27,
+		0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d,
+		0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562,
+		0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9,
+		0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752,
+		0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66,
+		0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3,
+		0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced,
+		0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e,
+		0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4,
+		0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4,
+		0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd,
+		0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d,
+		0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60,
+		0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767,
+		0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,
+		0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000,
+		0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c,
+		0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736,
+		0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24,
+		0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b,
+		0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,
+		0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12,
+		0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814,
+		0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3,
+		0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b,
+		0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8,
+		0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,
+		0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7,
+		0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077,
+		0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247,
+		0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22,
+		0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698,
+		0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,
+		0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254,
+		0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582,
+		0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf,
+		0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb,
+		0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883,
+		0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,
+		0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629,
+		0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035,
+		0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533,
+		0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17,
+		0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4,
+		0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,
+		0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb,
+		0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d,
+		0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb,
+		0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a,
+		0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73,
+		0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,
+		0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2,
+		0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff,
+		0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064,
+		0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0
+	);
+	
+	var $Td4 = array(
+		0x52525252, 0x09090909, 0x6a6a6a6a, 0xd5d5d5d5,
+		0x30303030, 0x36363636, 0xa5a5a5a5, 0x38383838,
+		0xbfbfbfbf, 0x40404040, 0xa3a3a3a3, 0x9e9e9e9e,
+		0x81818181, 0xf3f3f3f3, 0xd7d7d7d7, 0xfbfbfbfb,
+		0x7c7c7c7c, 0xe3e3e3e3, 0x39393939, 0x82828282,
+		0x9b9b9b9b, 0x2f2f2f2f, 0xffffffff, 0x87878787,
+		0x34343434, 0x8e8e8e8e, 0x43434343, 0x44444444,
+		0xc4c4c4c4, 0xdededede, 0xe9e9e9e9, 0xcbcbcbcb,
+		0x54545454, 0x7b7b7b7b, 0x94949494, 0x32323232,
+		0xa6a6a6a6, 0xc2c2c2c2, 0x23232323, 0x3d3d3d3d,
+		0xeeeeeeee, 0x4c4c4c4c, 0x95959595, 0x0b0b0b0b,
+		0x42424242, 0xfafafafa, 0xc3c3c3c3, 0x4e4e4e4e,
+		0x08080808, 0x2e2e2e2e, 0xa1a1a1a1, 0x66666666,
+		0x28282828, 0xd9d9d9d9, 0x24242424, 0xb2b2b2b2,
+		0x76767676, 0x5b5b5b5b, 0xa2a2a2a2, 0x49494949,
+		0x6d6d6d6d, 0x8b8b8b8b, 0xd1d1d1d1, 0x25252525,
+		0x72727272, 0xf8f8f8f8, 0xf6f6f6f6, 0x64646464,
+		0x86868686, 0x68686868, 0x98989898, 0x16161616,
+		0xd4d4d4d4, 0xa4a4a4a4, 0x5c5c5c5c, 0xcccccccc,
+		0x5d5d5d5d, 0x65656565, 0xb6b6b6b6, 0x92929292,
+		0x6c6c6c6c, 0x70707070, 0x48484848, 0x50505050,
+		0xfdfdfdfd, 0xedededed, 0xb9b9b9b9, 0xdadadada,
+		0x5e5e5e5e, 0x15151515, 0x46464646, 0x57575757,
+		0xa7a7a7a7, 0x8d8d8d8d, 0x9d9d9d9d, 0x84848484,
+		0x90909090, 0xd8d8d8d8, 0xabababab, 0x00000000,
+		0x8c8c8c8c, 0xbcbcbcbc, 0xd3d3d3d3, 0x0a0a0a0a,
+		0xf7f7f7f7, 0xe4e4e4e4, 0x58585858, 0x05050505,
+		0xb8b8b8b8, 0xb3b3b3b3, 0x45454545, 0x06060606,
+		0xd0d0d0d0, 0x2c2c2c2c, 0x1e1e1e1e, 0x8f8f8f8f,
+		0xcacacaca, 0x3f3f3f3f, 0x0f0f0f0f, 0x02020202,
+		0xc1c1c1c1, 0xafafafaf, 0xbdbdbdbd, 0x03030303,
+		0x01010101, 0x13131313, 0x8a8a8a8a, 0x6b6b6b6b,
+		0x3a3a3a3a, 0x91919191, 0x11111111, 0x41414141,
+		0x4f4f4f4f, 0x67676767, 0xdcdcdcdc, 0xeaeaeaea,
+		0x97979797, 0xf2f2f2f2, 0xcfcfcfcf, 0xcececece,
+		0xf0f0f0f0, 0xb4b4b4b4, 0xe6e6e6e6, 0x73737373,
+		0x96969696, 0xacacacac, 0x74747474, 0x22222222,
+		0xe7e7e7e7, 0xadadadad, 0x35353535, 0x85858585,
+		0xe2e2e2e2, 0xf9f9f9f9, 0x37373737, 0xe8e8e8e8,
+		0x1c1c1c1c, 0x75757575, 0xdfdfdfdf, 0x6e6e6e6e,
+		0x47474747, 0xf1f1f1f1, 0x1a1a1a1a, 0x71717171,
+		0x1d1d1d1d, 0x29292929, 0xc5c5c5c5, 0x89898989,
+		0x6f6f6f6f, 0xb7b7b7b7, 0x62626262, 0x0e0e0e0e,
+		0xaaaaaaaa, 0x18181818, 0xbebebebe, 0x1b1b1b1b,
+		0xfcfcfcfc, 0x56565656, 0x3e3e3e3e, 0x4b4b4b4b,
+		0xc6c6c6c6, 0xd2d2d2d2, 0x79797979, 0x20202020,
+		0x9a9a9a9a, 0xdbdbdbdb, 0xc0c0c0c0, 0xfefefefe,
+		0x78787878, 0xcdcdcdcd, 0x5a5a5a5a, 0xf4f4f4f4,
+		0x1f1f1f1f, 0xdddddddd, 0xa8a8a8a8, 0x33333333,
+		0x88888888, 0x07070707, 0xc7c7c7c7, 0x31313131,
+		0xb1b1b1b1, 0x12121212, 0x10101010, 0x59595959,
+		0x27272727, 0x80808080, 0xecececec, 0x5f5f5f5f,
+		0x60606060, 0x51515151, 0x7f7f7f7f, 0xa9a9a9a9,
+		0x19191919, 0xb5b5b5b5, 0x4a4a4a4a, 0x0d0d0d0d,
+		0x2d2d2d2d, 0xe5e5e5e5, 0x7a7a7a7a, 0x9f9f9f9f,
+		0x93939393, 0xc9c9c9c9, 0x9c9c9c9c, 0xefefefef,
+		0xa0a0a0a0, 0xe0e0e0e0, 0x3b3b3b3b, 0x4d4d4d4d,
+		0xaeaeaeae, 0x2a2a2a2a, 0xf5f5f5f5, 0xb0b0b0b0,
+		0xc8c8c8c8, 0xebebebeb, 0xbbbbbbbb, 0x3c3c3c3c,
+		0x83838383, 0x53535353, 0x99999999, 0x61616161,
+		0x17171717, 0x2b2b2b2b, 0x04040404, 0x7e7e7e7e,
+		0xbabababa, 0x77777777, 0xd6d6d6d6, 0x26262626,
+		0xe1e1e1e1, 0x69696969, 0x14141414, 0x63636363,
+		0x55555555, 0x21212121, 0x0c0c0c0c, 0x7d7d7d7d
+	);
+	
+	var $rcon = array(
+		0x01000000, 0x02000000, 0x04000000, 0x08000000,
+		0x10000000, 0x20000000, 0x40000000, 0x80000000,
+		0x1B000000, 0x36000000
+	);
 
-  //////////////////// CORE FUNCTIONS ///////////////////////////
+	//////////////////// CORE FUNCTIONS ///////////////////////////
 
-  // Encrypt a single block (bin) and return (bout)
-  function AES_encrypt($bin, $key)
-  {
-    $rk = 0;
-    
-    if ($bin == '' || $key == '') {
-      librijndael2::trace("AES_encrypt: bin/key undefined");
-      return;
-    }
-    
-    // FIXME: BAD: we need rd_key
-  
-    // map byte array block to cipher state
-    // and add initial round key:
-    
-    $s0 = librijndael2::parseInt("0x" . substr($bin, 0 , 8)) ^ $key->rd_key[0];
-    $s1 = librijndael2::parseInt("0x" . substr($bin, 8 , 8)) ^ $key->rd_key[1];
-    $s2 = librijndael2::parseInt("0x" . substr($bin, 16, 8)) ^ $key->rd_key[2];
-    $s3 = librijndael2::parseInt("0x" . substr($bin, 24, 8)) ^ $key->rd_key[3];
-    
-    // Using Full Unroll (Bigger more a bit faster :) )
-    // round 1:
-    $t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[4];
-    $t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[5];
-    $t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[6];
-    $t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[7];
-    $s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[8];
-    $s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[9];
-    $s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[10];
-    $s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[11];
-    $t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[12];
-    $t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[13];
-    $t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[14];
-    $t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[15];
-    $s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[16];
-    $s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[17];
-    $s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[18];
-    $s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[19];
-    $t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[20];
-    $t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[21];
-    $t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[22];
-    $t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[23];
-    $s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[24];
-    $s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[25];
-    $s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[26];
-    $s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[27];
-    $t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[28];
-    $t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[29];
-    $t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[30];
-    $t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[31];
-    $s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[32];
-    $s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[33];
-    $s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[34];
-    $s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[35];
-    $t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[36];
-    $t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[37];
-    $t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[38];
-    $t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[39];
-    
-    // FIXME: BAD: need $key->rounds
-    
-    if ($key->rounds > 10) {
-        // round 10:
-        $s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[40];
-        $s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[41];
-        $s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[42];
-        $s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[43];
-        $t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[44];
-        $t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[45];
-        $t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[46];
-        $t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[47];
-        
-      if ($key->rounds > 12) {
-            $s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[48];
-            $s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[49];
-            $s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[50];
-            $s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[51];
-            $t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[52];
-            $t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[53];
-            $t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[54];
-            $t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[55];   
-        }
-    }
-   
-    // This is correct
-    $rk = $key->rounds << 2;
-  
-  
-    /*
-    // No Full Unroll 
-    */
-  
-    // Apply last round and
-    // map cipher state to byte array block:
-     
-    $s0 =  ($this->Te4[($t0 >> 24) & 0xff] & 0xff000000) ^
-           ($this->Te4[($t1 >> 16) & 0xff] & 0x00ff0000) ^
-           ($this->Te4[($t2 >>  8) & 0xff] & 0x0000ff00) ^
-           ($this->Te4[($t3      ) & 0xff] & 0x000000ff) ^
-            $key->rd_key[$rk];
-  
-    $out  = librijndael2::ord2hex(($s0 >> 24) & 0xff);
-    $out .= librijndael2::ord2hex(($s0 >> 16) & 0xff);
-    $out .= librijndael2::ord2hex(($s0 >> 8) & 0xff);
-    $out .= librijndael2::ord2hex($s0 & 0xff);
+	// Encrypt a single block (bin) and return (bout)
+	function AES_encrypt($bin, $key)
+	{
+		$rk = 0;
+		
+		if ($bin == '' || $key == '') {
+			librijndael2::trace("AES_encrypt: bin/key undefined");
+			return;
+		}
+		
+		// FIXME: BAD: we need rd_key
+	
+		// map byte array block to cipher state
+		// and add initial round key:
+		
+		$s0 = librijndael2::parseInt("0x" . substr($bin, 0 , 8)) ^ $key->rd_key[0];
+		$s1 = librijndael2::parseInt("0x" . substr($bin, 8 , 8)) ^ $key->rd_key[1];
+		$s2 = librijndael2::parseInt("0x" . substr($bin, 16, 8)) ^ $key->rd_key[2];
+		$s3 = librijndael2::parseInt("0x" . substr($bin, 24, 8)) ^ $key->rd_key[3];
+		
+		// Using Full Unroll (Bigger more a bit faster :) )
+		// round 1:
+		$t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[4];
+		$t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[5];
+		$t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[6];
+		$t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[7];
+		$s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[8];
+		$s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[9];
+		$s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[10];
+		$s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[11];
+		$t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[12];
+		$t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[13];
+		$t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[14];
+		$t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[15];
+		$s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[16];
+		$s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[17];
+		$s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[18];
+		$s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[19];
+		$t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[20];
+		$t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[21];
+		$t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[22];
+		$t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[23];
+		$s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[24];
+		$s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[25];
+		$s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[26];
+		$s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[27];
+		$t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[28];
+		$t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[29];
+		$t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[30];
+		$t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[31];
+		$s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[32];
+		$s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[33];
+		$s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[34];
+		$s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[35];
+		$t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[36];
+		$t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[37];
+		$t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[38];
+		$t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[39];
+		
+		// FIXME: BAD: need $key->rounds
+		
+		if ($key->rounds > 10) {
+				// round 10:
+				$s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[40];
+				$s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[41];
+				$s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[42];
+				$s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[43];
+				$t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[44];
+				$t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[45];
+				$t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[46];
+				$t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[47];
+				
+			if ($key->rounds > 12) {
+						$s0 = $this->Te0[($t0 >> 24) & 0xff] ^ $this->Te1[($t1 >> 16) & 0xff] ^ $this->Te2[($t2 >>  8) & 0xff] ^ $this->Te3[$t3 & 0xff] ^ $key->rd_key[48];
+						$s1 = $this->Te0[($t1 >> 24) & 0xff] ^ $this->Te1[($t2 >> 16) & 0xff] ^ $this->Te2[($t3 >>  8) & 0xff] ^ $this->Te3[$t0 & 0xff] ^ $key->rd_key[49];
+						$s2 = $this->Te0[($t2 >> 24) & 0xff] ^ $this->Te1[($t3 >> 16) & 0xff] ^ $this->Te2[($t0 >>  8) & 0xff] ^ $this->Te3[$t1 & 0xff] ^ $key->rd_key[50];
+						$s3 = $this->Te0[($t3 >> 24) & 0xff] ^ $this->Te1[($t0 >> 16) & 0xff] ^ $this->Te2[($t1 >>  8) & 0xff] ^ $this->Te3[$t2 & 0xff] ^ $key->rd_key[51];
+						$t0 = $this->Te0[($s0 >> 24) & 0xff] ^ $this->Te1[($s1 >> 16) & 0xff] ^ $this->Te2[($s2 >>  8) & 0xff] ^ $this->Te3[$s3 & 0xff] ^ $key->rd_key[52];
+						$t1 = $this->Te0[($s1 >> 24) & 0xff] ^ $this->Te1[($s2 >> 16) & 0xff] ^ $this->Te2[($s3 >>  8) & 0xff] ^ $this->Te3[$s0 & 0xff] ^ $key->rd_key[53];
+						$t2 = $this->Te0[($s2 >> 24) & 0xff] ^ $this->Te1[($s3 >> 16) & 0xff] ^ $this->Te2[($s0 >>  8) & 0xff] ^ $this->Te3[$s1 & 0xff] ^ $key->rd_key[54];
+						$t3 = $this->Te0[($s3 >> 24) & 0xff] ^ $this->Te1[($s0 >> 16) & 0xff] ^ $this->Te2[($s1 >>  8) & 0xff] ^ $this->Te3[$s2 & 0xff] ^ $key->rd_key[55];   
+				}
+		}
+ 	
+		// This is correct
+		$rk = $key->rounds << 2;
+	
+	
+		/*
+		// No Full Unroll 
+		*/
+	
+		// Apply last round and
+		// map cipher state to byte array block:
+ 		
+		$s0 =  ($this->Te4[($t0 >> 24) & 0xff] & 0xff000000) ^
+ 					($this->Te4[($t1 >> 16) & 0xff] & 0x00ff0000) ^
+ 					($this->Te4[($t2 >>  8) & 0xff] & 0x0000ff00) ^
+ 					($this->Te4[($t3      ) & 0xff] & 0x000000ff) ^
+						$key->rd_key[$rk];
+	
+		$out  = librijndael2::ord2hex(($s0 >> 24) & 0xff);
+		$out .= librijndael2::ord2hex(($s0 >> 16) & 0xff);
+		$out .= librijndael2::ord2hex(($s0 >> 8) & 0xff);
+		$out .= librijndael2::ord2hex($s0 & 0xff);
 
-    $s1 =  ($this->Te4[($t1 >> 24) & 0xff] & 0xff000000) ^
-           ($this->Te4[($t2 >> 16) & 0xff] & 0x00ff0000) ^
-           ($this->Te4[($t3 >>  8) & 0xff] & 0x0000ff00) ^
-           ($this->Te4[($t0      ) & 0xff] & 0x000000ff) ^
-           $key->rd_key[$rk+1];
-      
-    $out .= librijndael2::ord2hex(($s1 >> 24) & 0xff);
-    $out .= librijndael2::ord2hex(($s1 >> 16) & 0xff);
-    $out .= librijndael2::ord2hex(($s1 >> 8) & 0xff);
-    $out .= librijndael2::ord2hex($s1 & 0xff);
-    
-    $s2 =  ($this->Te4[($t2 >> 24) & 0xff] & 0xff000000) ^
-           ($this->Te4[($t3 >> 16) & 0xff] & 0x00ff0000) ^
-           ($this->Te4[($t0 >>  8) & 0xff] & 0x0000ff00) ^
-           ($this->Te4[($t1      ) & 0xff] & 0x000000ff) ^
-           $key->rd_key[$rk+2];
+		$s1 =  ($this->Te4[($t1 >> 24) & 0xff] & 0xff000000) ^
+ 					($this->Te4[($t2 >> 16) & 0xff] & 0x00ff0000) ^
+ 					($this->Te4[($t3 >>  8) & 0xff] & 0x0000ff00) ^
+ 					($this->Te4[($t0      ) & 0xff] & 0x000000ff) ^
+ 					$key->rd_key[$rk+1];
+			
+		$out .= librijndael2::ord2hex(($s1 >> 24) & 0xff);
+		$out .= librijndael2::ord2hex(($s1 >> 16) & 0xff);
+		$out .= librijndael2::ord2hex(($s1 >> 8) & 0xff);
+		$out .= librijndael2::ord2hex($s1 & 0xff);
+		
+		$s2 =  ($this->Te4[($t2 >> 24) & 0xff] & 0xff000000) ^
+ 					($this->Te4[($t3 >> 16) & 0xff] & 0x00ff0000) ^
+ 					($this->Te4[($t0 >>  8) & 0xff] & 0x0000ff00) ^
+ 					($this->Te4[($t1      ) & 0xff] & 0x000000ff) ^
+ 					$key->rd_key[$rk+2];
 
-    $out .= librijndael2::ord2hex(($s2 >> 24) & 0xff);
-    $out .= librijndael2::ord2hex(($s2 >> 16) & 0xff);
-    $out .= librijndael2::ord2hex(($s2 >> 8) & 0xff); 
-    $out .= librijndael2::ord2hex($s2 & 0xff);
-    
-    $s3 =  ($this->Te4[($t3 >> 24) & 0xff] & 0xff000000) ^
-           ($this->Te4[($t0 >> 16) & 0xff] & 0x00ff0000) ^
-           ($this->Te4[($t1 >>  8) & 0xff] & 0x0000ff00) ^
-           ($this->Te4[($t2      ) & 0xff] & 0x000000ff) ^
-           $key->rd_key[$rk+3];
-    
-    $out .= librijndael2::ord2hex(($s3 >> 24) & 0xff);
-    $out .= librijndael2::ord2hex(($s3 >> 16) & 0xff);
-    $out .= librijndael2::ord2hex(($s3 >> 8) & 0xff);
-    $out .= librijndael2::ord2hex($s3 & 0xff);
-  
-    return $out;
-  }
+		$out .= librijndael2::ord2hex(($s2 >> 24) & 0xff);
+		$out .= librijndael2::ord2hex(($s2 >> 16) & 0xff);
+		$out .= librijndael2::ord2hex(($s2 >> 8) & 0xff); 
+		$out .= librijndael2::ord2hex($s2 & 0xff);
+		
+		$s3 =  ($this->Te4[($t3 >> 24) & 0xff] & 0xff000000) ^
+ 					($this->Te4[($t0 >> 16) & 0xff] & 0x00ff0000) ^
+ 					($this->Te4[($t1 >>  8) & 0xff] & 0x0000ff00) ^
+ 					($this->Te4[($t2      ) & 0xff] & 0x000000ff) ^
+ 					$key->rd_key[$rk+3];
+		
+		$out .= librijndael2::ord2hex(($s3 >> 24) & 0xff);
+		$out .= librijndael2::ord2hex(($s3 >> 16) & 0xff);
+		$out .= librijndael2::ord2hex(($s3 >> 8) & 0xff);
+		$out .= librijndael2::ord2hex($s3 & 0xff);
+	
+		return $out;
+	}
 
 
-  // Decrypt a single block (bin) and return (bout)
-  function AES_decrypt($bin, $key)
-  {
-    $rk = 0;
-    // var r;  // No Full Unroll
+	// Decrypt a single block (bin) and return (bout)
+	function AES_decrypt($bin, $key)
+	{
+		$rk = 0;
+		// var r;  // No Full Unroll
 
-    if ($bin == '' || $key == '')
-    {
-      librijndael2::trace("AES_decrypt: bin/key undefined");
-      return;
-    }
-    
-    // FIXME: BAD: need $key->rd_key
+		if ($bin == '' || $key == '')
+		{
+			librijndael2::trace("AES_decrypt: bin/key undefined");
+			return;
+		}
+		
+		// FIXME: BAD: need $key->rd_key
 
-    // map byte array block to cipher state
-    // and add initial round key:
-    $s0 = librijndael2::parseInt("0x" . substr($bin, 0 , 8)) ^ $key->rd_key[0];
-    $s1 = librijndael2::parseInt("0x" . substr($bin, 8 , 8)) ^ $key->rd_key[1];
-    $s2 = librijndael2::parseInt("0x" . substr($bin, 16, 8)) ^ $key->rd_key[2];
-    $s3 = librijndael2::parseInt("0x" . substr($bin, 24, 8)) ^ $key->rd_key[3];
-   
-    // Using Full Unroll (Bigger more a bit faster :) )
-    $t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[4];  // ROUND 1
-    $t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[5];
-    $t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[6];
-    $t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[7];
-    $s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[8];  // ROUND 2
-    $s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[9];
-    $s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[10];
-    $s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[11];
-    $t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[12]; // ROUND 3
-    $t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[13];
-    $t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[14];
-    $t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[15];
-    $s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[16]; // ROUND 4
-    $s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[17];
-    $s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[18];
-    $s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[19];
-    $t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[20]; // ROUND 5
-    $t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[21];
-    $t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[22];
-    $t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[23];
-    $s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[24]; // ROUND 6
-    $s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[25];
-    $s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[26];
-    $s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[27];
-    $t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[28]; // ROUND 7
-    $t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[29];
-    $t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[30];
-    $t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[31];
-    $s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[32]; // ROUND 8
-    $s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[33];
-    $s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[34];
-    $s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[35];
-    $t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[36]; // ROUND 9
-    $t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[37];
-    $t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[38];
-    $t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[39];
-    
-    // FIXME: BAD: need $key->rounds
-    
-    if ($key->rounds > 10)
-    {
-      $s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[40]; // ROUND 10
-      $s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[41];
-      $s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[42];
-      $s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[43];
-      $t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[44]; // ROUND 11
-      $t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[45];
-      $t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[46];
-      $t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[47];  
-        
-      if ($key->rounds > 12)
-      {
-        $s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[48]; // ROUND 12
-        $s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[49];
-        $s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[50];
-        $s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[51];
-        $t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[52]; // ROUND 13
-        $t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[53];
-        $t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[54];
-        $t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[55];
-      }
-    }
-    
-    // This is correct
-    $rk = $key->rounds << 2;
+		// map byte array block to cipher state
+		// and add initial round key:
+		$s0 = librijndael2::parseInt("0x" . substr($bin, 0 , 8)) ^ $key->rd_key[0];
+		$s1 = librijndael2::parseInt("0x" . substr($bin, 8 , 8)) ^ $key->rd_key[1];
+		$s2 = librijndael2::parseInt("0x" . substr($bin, 16, 8)) ^ $key->rd_key[2];
+		$s3 = librijndael2::parseInt("0x" . substr($bin, 24, 8)) ^ $key->rd_key[3];
+ 	
+		// Using Full Unroll (Bigger more a bit faster :) )
+		$t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[4];  // ROUND 1
+		$t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[5];
+		$t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[6];
+		$t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[7];
+		$s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[8];  // ROUND 2
+		$s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[9];
+		$s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[10];
+		$s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[11];
+		$t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[12]; // ROUND 3
+		$t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[13];
+		$t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[14];
+		$t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[15];
+		$s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[16]; // ROUND 4
+		$s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[17];
+		$s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[18];
+		$s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[19];
+		$t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[20]; // ROUND 5
+		$t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[21];
+		$t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[22];
+		$t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[23];
+		$s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[24]; // ROUND 6
+		$s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[25];
+		$s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[26];
+		$s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[27];
+		$t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[28]; // ROUND 7
+		$t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[29];
+		$t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[30];
+		$t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[31];
+		$s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[32]; // ROUND 8
+		$s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[33];
+		$s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[34];
+		$s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[35];
+		$t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[36]; // ROUND 9
+		$t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[37];
+		$t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[38];
+		$t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[39];
+		
+		// FIXME: BAD: need $key->rounds
+		
+		if ($key->rounds > 10)
+		{
+			$s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[40]; // ROUND 10
+			$s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[41];
+			$s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[42];
+			$s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[43];
+			$t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[44]; // ROUND 11
+			$t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[45];
+			$t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[46];
+			$t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[47];  
+				
+			if ($key->rounds > 12)
+			{
+				$s0 = $this->Td0[($t0 >> 24) & 0xff] ^ $this->Td1[($t3 >> 16) & 0xff] ^ $this->Td2[($t2 >>  8) & 0xff] ^ $this->Td3[$t1 & 0xff] ^ $key->rd_key[48]; // ROUND 12
+				$s1 = $this->Td0[($t1 >> 24) & 0xff] ^ $this->Td1[($t0 >> 16) & 0xff] ^ $this->Td2[($t3 >>  8) & 0xff] ^ $this->Td3[$t2 & 0xff] ^ $key->rd_key[49];
+				$s2 = $this->Td0[($t2 >> 24) & 0xff] ^ $this->Td1[($t1 >> 16) & 0xff] ^ $this->Td2[($t0 >>  8) & 0xff] ^ $this->Td3[$t3 & 0xff] ^ $key->rd_key[50];
+				$s3 = $this->Td0[($t3 >> 24) & 0xff] ^ $this->Td1[($t2 >> 16) & 0xff] ^ $this->Td2[($t1 >>  8) & 0xff] ^ $this->Td3[$t0 & 0xff] ^ $key->rd_key[51];
+				$t0 = $this->Td0[($s0 >> 24) & 0xff] ^ $this->Td1[($s3 >> 16) & 0xff] ^ $this->Td2[($s2 >>  8) & 0xff] ^ $this->Td3[$s1 & 0xff] ^ $key->rd_key[52]; // ROUND 13
+				$t1 = $this->Td0[($s1 >> 24) & 0xff] ^ $this->Td1[($s0 >> 16) & 0xff] ^ $this->Td2[($s3 >>  8) & 0xff] ^ $this->Td3[$s2 & 0xff] ^ $key->rd_key[53];
+				$t2 = $this->Td0[($s2 >> 24) & 0xff] ^ $this->Td1[($s1 >> 16) & 0xff] ^ $this->Td2[($s0 >>  8) & 0xff] ^ $this->Td3[$s3 & 0xff] ^ $key->rd_key[54];
+				$t3 = $this->Td0[($s3 >> 24) & 0xff] ^ $this->Td1[($s2 >> 16) & 0xff] ^ $this->Td2[($s1 >>  8) & 0xff] ^ $this->Td3[$s0 & 0xff] ^ $key->rd_key[55];
+			}
+		}
+		
+		// This is correct
+		$rk = $key->rounds << 2;
 
-    /*
-    // No Full Unroll 
-    */
-  
-    // Apply last round and
-    // map cipher state to byte array block:
-     
-    $s0 =  ($this->Td4[($t0 >> 24) & 0xff] & 0xff000000) ^
-           ($this->Td4[($t3 >> 16) & 0xff] & 0x00ff0000) ^
-           ($this->Td4[($t2 >>  8) & 0xff] & 0x0000ff00) ^
-           ($this->Td4[($t1      ) & 0xff] & 0x000000ff) ^
-           $key->rd_key[$rk];
-  
-    $out  = librijndael2::ord2hex(($s0 >> 24) & 0xff);
-    $out .= librijndael2::ord2hex(($s0 >> 16) & 0xff);
-    $out .= librijndael2::ord2hex(($s0 >> 8) & 0xff);
-    $out .= librijndael2::ord2hex($s0 & 0xff);
+		/*
+		// No Full Unroll 
+		*/
+	
+		// Apply last round and
+		// map cipher state to byte array block:
+ 		
+		$s0 =  ($this->Td4[($t0 >> 24) & 0xff] & 0xff000000) ^
+ 					($this->Td4[($t3 >> 16) & 0xff] & 0x00ff0000) ^
+ 					($this->Td4[($t2 >>  8) & 0xff] & 0x0000ff00) ^
+ 					($this->Td4[($t1      ) & 0xff] & 0x000000ff) ^
+ 					$key->rd_key[$rk];
+	
+		$out  = librijndael2::ord2hex(($s0 >> 24) & 0xff);
+		$out .= librijndael2::ord2hex(($s0 >> 16) & 0xff);
+		$out .= librijndael2::ord2hex(($s0 >> 8) & 0xff);
+		$out .= librijndael2::ord2hex($s0 & 0xff);
 
-    $s1 =  ($this->Td4[($t1 >> 24) & 0xff] & 0xff000000) ^
-           ($this->Td4[($t0 >> 16) & 0xff] & 0x00ff0000) ^
-           ($this->Td4[($t3 >>  8) & 0xff] & 0x0000ff00) ^
-           ($this->Td4[($t2      ) & 0xff] & 0x000000ff) ^
-           $key->rd_key[$rk+1];
-      
-    $out .= librijndael2::ord2hex(($s1 >> 24) & 0xff);
-    $out .= librijndael2::ord2hex(($s1 >> 16) & 0xff);
-    $out .= librijndael2::ord2hex(($s1 >> 8) & 0xff);
-    $out .= librijndael2::ord2hex($s1 & 0xff);
-    
-    $s2 =  ($this->Td4[($t2 >> 24) & 0xff] & 0xff000000) ^
-           ($this->Td4[($t1 >> 16) & 0xff] & 0x00ff0000) ^
-           ($this->Td4[($t0 >>  8) & 0xff] & 0x0000ff00) ^
-           ($this->Td4[($t3      ) & 0xff] & 0x000000ff) ^
-           $key->rd_key[$rk+2];
+		$s1 =  ($this->Td4[($t1 >> 24) & 0xff] & 0xff000000) ^
+ 					($this->Td4[($t0 >> 16) & 0xff] & 0x00ff0000) ^
+ 					($this->Td4[($t3 >>  8) & 0xff] & 0x0000ff00) ^
+ 					($this->Td4[($t2      ) & 0xff] & 0x000000ff) ^
+ 					$key->rd_key[$rk+1];
+			
+		$out .= librijndael2::ord2hex(($s1 >> 24) & 0xff);
+		$out .= librijndael2::ord2hex(($s1 >> 16) & 0xff);
+		$out .= librijndael2::ord2hex(($s1 >> 8) & 0xff);
+		$out .= librijndael2::ord2hex($s1 & 0xff);
+		
+		$s2 =  ($this->Td4[($t2 >> 24) & 0xff] & 0xff000000) ^
+ 					($this->Td4[($t1 >> 16) & 0xff] & 0x00ff0000) ^
+ 					($this->Td4[($t0 >>  8) & 0xff] & 0x0000ff00) ^
+ 					($this->Td4[($t3      ) & 0xff] & 0x000000ff) ^
+ 					$key->rd_key[$rk+2];
 
-    $out .= librijndael2::ord2hex(($s2 >> 24) & 0xff);
-    $out .= librijndael2::ord2hex(($s2 >> 16) & 0xff);
-    $out .= librijndael2::ord2hex(($s2 >> 8) & 0xff);
-    $out .= librijndael2::ord2hex($s2 & 0xff);
-  
-    $s3 =  ($this->Td4[($t3 >> 24) & 0xff] & 0xff000000) ^
-           ($this->Td4[($t2 >> 16) & 0xff] & 0x00ff0000) ^
-           ($this->Td4[($t1 >>  8) & 0xff] & 0x0000ff00) ^
-           ($this->Td4[($t0      ) & 0xff] & 0x000000ff) ^
-           $key->rd_key[$rk+3];
-    
-    $out .= librijndael2::ord2hex(($s3 >> 24) & 0xff);
-    $out .= librijndael2::ord2hex(($s3 >> 16) & 0xff);
-    $out .= librijndael2::ord2hex(($s3 >> 8) & 0xff);
-    $out .= librijndael2::ord2hex($s3 & 0xff);
-  
-    return $out;
-  }
+		$out .= librijndael2::ord2hex(($s2 >> 24) & 0xff);
+		$out .= librijndael2::ord2hex(($s2 >> 16) & 0xff);
+		$out .= librijndael2::ord2hex(($s2 >> 8) & 0xff);
+		$out .= librijndael2::ord2hex($s2 & 0xff);
+	
+		$s3 =  ($this->Td4[($t3 >> 24) & 0xff] & 0xff000000) ^
+ 					($this->Td4[($t2 >> 16) & 0xff] & 0x00ff0000) ^
+ 					($this->Td4[($t1 >>  8) & 0xff] & 0x0000ff00) ^
+ 					($this->Td4[($t0      ) & 0xff] & 0x000000ff) ^
+ 					$key->rd_key[$rk+3];
+		
+		$out .= librijndael2::ord2hex(($s3 >> 24) & 0xff);
+		$out .= librijndael2::ord2hex(($s3 >> 16) & 0xff);
+		$out .= librijndael2::ord2hex(($s3 >> 8) & 0xff);
+		$out .= librijndael2::ord2hex($s3 & 0xff);
+	
+		return $out;
+	}
 
 
-  /////////////////////////////// MODES //////////////////////////////////
-   
-  /* ECB MODE (AES_ecb_encrypt) 
-   * bin:  128 bit block (32 hex digits)
-   * key:  AES_KEY object (use AES_set_encrypt_key or AES_set_decrypt_key)
-   * enc:  "AES_ENCRYPT" or "AES_DECRYPT"
-   * Return value: 128 bit block (32 hex digits) 
-   */
+	/////////////////////////////// MODES //////////////////////////////////
+ 	
+	/* ECB MODE (AES_ecb_encrypt) 
+ 	* bin:  128 bit block (32 hex digits)
+ 	* key:  AES_KEY object (use AES_set_encrypt_key or AES_set_decrypt_key)
+ 	* enc:  "AES_ENCRYPT" or "AES_DECRYPT"
+ 	* Return value: 128 bit block (32 hex digits) 
+ 	*/
 
-  function AES_ecb_encrypt($bin, $key, $enc)
-  {
+	function AES_ecb_encrypt($bin, $key, $enc)
+	{
 
-    if ($bin == '' || $key == '')
-    {
-      librijndael2::trace("AES_ecb_encrypt: bin/key undefined");
-      return;
-    }
-    
-    if ($enc !== "AES_ENCRYPT" && $enc !== "AES_DECRYPT")
-    {
-      librijndael2::trace("AES_ecb_encrypt: enc isn't AES_ENCRYPT/AES_DECRYPT");
-      return;
-    }
-    
-    if ($enc == "AES_ENCRYPT")
-        $bout = $this->AES_encrypt($bin, $key);
-    else
-        $bout = $this->AES_decrypt($bin, $key);
-  
-    return $bout;
-  }
+		if ($bin == '' || $key == '')
+		{
+			librijndael2::trace("AES_ecb_encrypt: bin/key undefined");
+			return;
+		}
+		
+		if ($enc !== "AES_ENCRYPT" && $enc !== "AES_DECRYPT")
+		{
+			librijndael2::trace("AES_ecb_encrypt: enc isn't AES_ENCRYPT/AES_DECRYPT");
+			return;
+		}
+		
+		if ($enc == "AES_ENCRYPT")
+				$bout = $this->AES_encrypt($bin, $key);
+		else
+				$bout = $this->AES_decrypt($bin, $key);
+	
+		return $bout;
+	}
 
 
-  /* CBC MODE (AES_cbc_encrypt) 
-   * bin:  128 bit block (32 hex digits)
-   * key:  AES_KEY object (use AES_set_encrypt_key or AES_set_decrypt_key)
-   * ivec: Initialization Vector (32 hex digits)
-   * enc:  "AES_ENCRYPT" or "AES_DECRYPT"
-   * Return value: 128 bit block (32 hex digits) 
-   */
+	/* CBC MODE (AES_cbc_encrypt) 
+ 	* bin:  128 bit block (32 hex digits)
+ 	* key:  AES_KEY object (use AES_set_encrypt_key or AES_set_decrypt_key)
+ 	* ivec: Initialization Vector (32 hex digits)
+ 	* enc:  "AES_ENCRYPT" or "AES_DECRYPT"
+ 	* Return value: 128 bit block (32 hex digits) 
+ 	*/
 
-  function AES_cbc_encrypt($bin, $key, $ivec, $enc)
-  {
+	function AES_cbc_encrypt($bin, $key, $ivec, $enc)
+	{
 
-    // if ($bin == '' || $key == '' || $ivec == '')
-    // {
-    //   librijndael2::trace("AES_cbc_encrypt: bin/key/ivec undefined.");
-    //   return;
-    // }
-        
-    $len = strlen($bin);
-    
-    if ($len % 32 != 0)
-    {
-      librijndael2::trace("AES_cbc_encrypt: data isn't multiple of 32 (32 hex = 128bits).");
-      return;
-    }
-        
-    if ($enc !== "AES_ENCRYPT" && $enc !== "AES_DECRYPT")
-    {
-      librijndael2::trace("AES_ecb_encrypt: enc isn't AES_ENCRYPT/AES_DECRYPT");
-      return;
-    }
+		// if ($bin == '' || $key == '' || $ivec == '')
+		// {
+		//   librijndael2::trace("AES_cbc_encrypt: bin/key/ivec undefined.");
+		//   return;
+		// }
+				
+		$len = strlen($bin);
+		
+		if ($len % 32 != 0)
+		{
+			librijndael2::trace("AES_cbc_encrypt: data isn't multiple of 32 (32 hex = 128bits).");
+			return;
+		}
+				
+		if ($enc !== "AES_ENCRYPT" && $enc !== "AES_DECRYPT")
+		{
+			librijndael2::trace("AES_ecb_encrypt: enc isn't AES_ENCRYPT/AES_DECRYPT");
+			return;
+		}
 
-    $tin = $bin;
-    $tmp = '';
-    $bout = '';
-    
-    if ($enc == "AES_ENCRYPT")
-      while ($len > 0)
-      {
-        $tmp = "";
-        for($n=0; $n < 32; $n += 2)
-        {
-          $aux  = librijndael2::parseInt("0x" . substr($tin, $n , 2)) ^ librijndael2::parseInt("0x" . substr($ivec, $n, 2));
-          $tmp .= librijndael2::ord2hex($aux);
-        }
-        
-        $tout = $this->AES_encrypt($tmp, $key);
-        $ivec = $tout; 
-        $len -= 32;
-        $tin = substr($tin, 32);
-        $bout .= $tout;
-      }
-    else
-      while ($len > 0) {
-        $tmp = $this->AES_decrypt($tin, $key);
-        $tout = "";
-        for($n=0; $n < 32; $n+=2)
-        {
-          $aux   = librijndael2::parseInt("0x" . substr($tmp, $n , 2)) ^ librijndael2::parseInt("0x" . substr($ivec, $n, 2)); 
-          $tout .= librijndael2::ord2hex($aux);
-        }
-          
-        $ivec = $tin;
-        $len -= 32;
-        $tin = substr($tin, 32);
-        $bout .= $tout;
-      }
-  
-    return $bout;
-  }
+		$tin = $bin;
+		$tmp = '';
+		$bout = '';
+		
+		if ($enc == "AES_ENCRYPT")
+			while ($len > 0)
+			{
+				$tmp = "";
+				for($n=0; $n < 32; $n += 2)
+				{
+					$aux  = librijndael2::parseInt("0x" . substr($tin, $n , 2)) ^ librijndael2::parseInt("0x" . substr($ivec, $n, 2));
+					$tmp .= librijndael2::ord2hex($aux);
+				}
+				
+				$tout = $this->AES_encrypt($tmp, $key);
+				$ivec = $tout; 
+				$len -= 32;
+				$tin = substr($tin, 32);
+				$bout .= $tout;
+			}
+		else
+			while ($len > 0) {
+				$tmp = $this->AES_decrypt($tin, $key);
+				$tout = "";
+				for($n=0; $n < 32; $n+=2)
+				{
+					$aux   = librijndael2::parseInt("0x" . substr($tmp, $n , 2)) ^ librijndael2::parseInt("0x" . substr($ivec, $n, 2)); 
+					$tout .= librijndael2::ord2hex($aux);
+				}
+					
+				$ivec = $tin;
+				$len -= 32;
+				$tin = substr($tin, 32);
+				$bout .= $tout;
+			}
+	
+		return $bout;
+	}
 
 }
 
 class Crypt_Rijndael_Key extends Crypt_Rijndael
 {
-  var $rounds = 12;
-  var $rd_key = array();
-  var $key = '';
-  var $bits = 0;
-  var $key_state = 'none';
-  
-  function __construct($key)
-  {
-    $this->key = $key;
-    $this->bits = ( strlen($key) * 4 );
-  }
-  
-  // Expand the cipher key into the encryption key schedule.
-  function set_encrypt()
-  {
-    $i = 0;
-    $rk = 0;
-    
-    if ( $this->key_state === 'encrypt' )
-    {
-      return 0;
-    }
-    
-    $this->key_state = 'encrypt';
-    
-    $userkey =& $this->key;
-    $bits =& $this->bits;
-    
-    if ($bits != 128 && $bits != 192 && $bits != 256)
-    {
-      librijndael2::trace("AES_set_encrypt_key: key size isn't 128/192/256");
-      return -2;
-    }
+	var $rounds = 12;
+	var $rd_key = array();
+	var $key = '';
+	var $bits = 0;
+	var $key_state = 'none';
+	
+	function __construct($key)
+	{
+		$this->key = $key;
+		$this->bits = ( strlen($key) * 4 );
+	}
+	
+	// Expand the cipher key into the encryption key schedule.
+	function set_encrypt()
+	{
+		$i = 0;
+		$rk = 0;
+		
+		if ( $this->key_state === 'encrypt' )
+		{
+			return 0;
+		}
+		
+		$this->key_state = 'encrypt';
+		
+		$userkey =& $this->key;
+		$bits =& $this->bits;
+		
+		if ($bits != 128 && $bits != 192 && $bits != 256)
+		{
+			librijndael2::trace("AES_set_encrypt_key: key size isn't 128/192/256");
+			return -2;
+		}
 
-    if ($bits==128)
-      $this->rounds = 10;
-    else if ($bits==192)
-      $this->rounds = 12;
-    else
-      $this->rounds = 14;
-        
-    
-    $this->rd_key = array();
-    $this->rd_key[0] = librijndael2::parseInt("0x" . substr($userkey, 0 , 8));
-    $this->rd_key[1] = librijndael2::parseInt("0x" . substr($userkey, 8 , 8));
-    $this->rd_key[2] = librijndael2::parseInt("0x" . substr($userkey, 16, 8));
-    $this->rd_key[3] = librijndael2::parseInt("0x" . substr($userkey, 24, 8));
-    
-    if ($bits == 128) {
-        for (;;) {
-            $temp  = $this->rd_key[3+$rk];
-        // (temp >> 24) & 0xff was difficult bug to tracking...
-            $this->rd_key[4+$rk] = $this->rd_key[$rk] ^
-              ($this->Te4[($temp >> 16) & 0xff] & 0xff000000) ^
-              ($this->Te4[($temp >>  8) & 0xff] & 0x00ff0000) ^
-              ($this->Te4[ $temp        & 0xff] & 0x0000ff00) ^
-              ($this->Te4[($temp >> 24) & 0xff] & 0x000000ff) ^
-              $this->rcon[$i];
-           
-      $this->rd_key[5+$rk] = $this->rd_key[1+$rk] ^ $this->rd_key[4+$rk];
-            $this->rd_key[6+$rk] = $this->rd_key[2+$rk] ^ $this->rd_key[5+$rk];
-            $this->rd_key[7+$rk] = $this->rd_key[3+$rk] ^ $this->rd_key[6+$rk];
-            if (++$i == 10) return 0;
-          $rk += 4;
-        }
-    }
+		if ($bits==128)
+			$this->rounds = 10;
+		else if ($bits==192)
+			$this->rounds = 12;
+		else
+			$this->rounds = 14;
+				
+		
+		$this->rd_key = array();
+		$this->rd_key[0] = librijndael2::parseInt("0x" . substr($userkey, 0 , 8));
+		$this->rd_key[1] = librijndael2::parseInt("0x" . substr($userkey, 8 , 8));
+		$this->rd_key[2] = librijndael2::parseInt("0x" . substr($userkey, 16, 8));
+		$this->rd_key[3] = librijndael2::parseInt("0x" . substr($userkey, 24, 8));
+		
+		if ($bits == 128) {
+				for (;;) {
+						$temp  = $this->rd_key[3+$rk];
+				// (temp >> 24) & 0xff was difficult bug to tracking...
+						$this->rd_key[4+$rk] = $this->rd_key[$rk] ^
+							($this->Te4[($temp >> 16) & 0xff] & 0xff000000) ^
+							($this->Te4[($temp >>  8) & 0xff] & 0x00ff0000) ^
+							($this->Te4[ $temp        & 0xff] & 0x0000ff00) ^
+							($this->Te4[($temp >> 24) & 0xff] & 0x000000ff) ^
+							$this->rcon[$i];
+ 					
+			$this->rd_key[5+$rk] = $this->rd_key[1+$rk] ^ $this->rd_key[4+$rk];
+						$this->rd_key[6+$rk] = $this->rd_key[2+$rk] ^ $this->rd_key[5+$rk];
+						$this->rd_key[7+$rk] = $this->rd_key[3+$rk] ^ $this->rd_key[6+$rk];
+						if (++$i == 10) return 0;
+					$rk += 4;
+				}
+		}
  
-    $this->rd_key[4] = librijndael2::parseInt("0x" . @substr($userkey, 32, 8));
-    $this->rd_key[5] = librijndael2::parseInt("0x" . @substr($userkey, 40, 8));
-    
-    if ($bits == 192) {
-        for (;;) {
-            $temp  = $this->rd_key[5+$rk];
-            $this->rd_key[6+$rk] = $this->rd_key[$rk] ^
-            ($this->Te4[($temp >> 16) & 0xff] & 0xff000000) ^
-            ($this->Te4[($temp >>  8) & 0xff] & 0x00ff0000) ^
-            ($this->Te4[($temp      ) & 0xff] & 0x0000ff00) ^
-            ($this->Te4[($temp >> 24) & 0xff] & 0x000000ff) ^
-            $this->rcon[$i];
-    
-        $this->rd_key[7+$rk] = $this->rd_key[1+$rk] ^ $this->rd_key[6 + $rk];
-            $this->rd_key[8+$rk] = $this->rd_key[2 + $rk] ^ $this->rd_key[7 + $rk];
-            $this->rd_key[9+$rk] = $this->rd_key[3 + $rk] ^ $this->rd_key[8 + $rk];
-            if (++$i == 8) {
-                return 0;
-            }
-        $this->rd_key[10 + $rk] = $this->rd_key[4 + $rk] ^ $this->rd_key[9 + $rk];
-            $this->rd_key[11 + $rk] = $this->rd_key[5 + $rk] ^ $this->rd_key[10 + $rk];
-            $rk += 6;
-        }
-    }
-    
-    $this->rd_key[6] = librijndael2::parseInt("0x" . @substr($userkey, 48, 8));
-    $this->rd_key[7] = librijndael2::parseInt("0x" . @substr($userkey, 56, 8));
-    
-    if ($bits == 256) {
-        for (;;) {
-            $temp  = $this->rd_key[7+$rk];
-            $this->rd_key[8+$rk] = $this->rd_key[$rk] ^
-            ($this->Te4[($temp >> 16) & 0xff] & 0xff000000) ^
-            ($this->Te4[($temp >>  8) & 0xff] & 0x00ff0000) ^
-            ($this->Te4[($temp      ) & 0xff] & 0x0000ff00) ^
-            ($this->Te4[($temp >> 24) & 0xff] & 0x000000ff) ^
-            $this->rcon[$i];
-      
-            $this->rd_key[9 + $rk] = $this->rd_key[1 + $rk] ^ $this->rd_key[8 + $rk];
-            $this->rd_key[10 + $rk] = $this->rd_key[2 + $rk] ^ $this->rd_key[9 + $rk];
-            $this->rd_key[11 + $rk] = $this->rd_key[3 + $rk] ^ $this->rd_key[10 + $rk];
-            if (++$i == 7) {
-                return 0;
-            }
-            
-            $temp  = $this->rd_key[11 + $rk];
-            $this->rd_key[12 + $rk] = $this->rd_key[4 + $rk] ^
-            ($this->Te4[($temp >> 24) & 0xff] & 0xff000000) ^
-            ($this->Te4[($temp >> 16) & 0xff] & 0x00ff0000) ^
-            ($this->Te4[($temp >>  8) & 0xff] & 0x0000ff00) ^
-            ($this->Te4[($temp      ) & 0xff] & 0x000000ff);
-      
-      $this->rd_key[13 + $rk] = $this->rd_key[5 + $rk] ^ $this->rd_key[12 + $rk];
-            $this->rd_key[14 + $rk] = $this->rd_key[6 + $rk] ^ $this->rd_key[13 + $rk];
-            $this->rd_key[15 + $rk] = $this->rd_key[7 + $rk] ^ $this->rd_key[14 + $rk];
-            $rk += 8;
-        }
-    }
-    return 0;
-  }
+		$this->rd_key[4] = librijndael2::parseInt("0x" . @substr($userkey, 32, 8));
+		$this->rd_key[5] = librijndael2::parseInt("0x" . @substr($userkey, 40, 8));
+		
+		if ($bits == 192) {
+				for (;;) {
+						$temp  = $this->rd_key[5+$rk];
+						$this->rd_key[6+$rk] = $this->rd_key[$rk] ^
+						($this->Te4[($temp >> 16) & 0xff] & 0xff000000) ^
+						($this->Te4[($temp >>  8) & 0xff] & 0x00ff0000) ^
+						($this->Te4[($temp      ) & 0xff] & 0x0000ff00) ^
+						($this->Te4[($temp >> 24) & 0xff] & 0x000000ff) ^
+						$this->rcon[$i];
+		
+				$this->rd_key[7+$rk] = $this->rd_key[1+$rk] ^ $this->rd_key[6 + $rk];
+						$this->rd_key[8+$rk] = $this->rd_key[2 + $rk] ^ $this->rd_key[7 + $rk];
+						$this->rd_key[9+$rk] = $this->rd_key[3 + $rk] ^ $this->rd_key[8 + $rk];
+						if (++$i == 8) {
+								return 0;
+						}
+				$this->rd_key[10 + $rk] = $this->rd_key[4 + $rk] ^ $this->rd_key[9 + $rk];
+						$this->rd_key[11 + $rk] = $this->rd_key[5 + $rk] ^ $this->rd_key[10 + $rk];
+						$rk += 6;
+				}
+		}
+		
+		$this->rd_key[6] = librijndael2::parseInt("0x" . @substr($userkey, 48, 8));
+		$this->rd_key[7] = librijndael2::parseInt("0x" . @substr($userkey, 56, 8));
+		
+		if ($bits == 256) {
+				for (;;) {
+						$temp  = $this->rd_key[7+$rk];
+						$this->rd_key[8+$rk] = $this->rd_key[$rk] ^
+						($this->Te4[($temp >> 16) & 0xff] & 0xff000000) ^
+						($this->Te4[($temp >>  8) & 0xff] & 0x00ff0000) ^
+						($this->Te4[($temp      ) & 0xff] & 0x0000ff00) ^
+						($this->Te4[($temp >> 24) & 0xff] & 0x000000ff) ^
+						$this->rcon[$i];
+			
+						$this->rd_key[9 + $rk] = $this->rd_key[1 + $rk] ^ $this->rd_key[8 + $rk];
+						$this->rd_key[10 + $rk] = $this->rd_key[2 + $rk] ^ $this->rd_key[9 + $rk];
+						$this->rd_key[11 + $rk] = $this->rd_key[3 + $rk] ^ $this->rd_key[10 + $rk];
+						if (++$i == 7) {
+								return 0;
+						}
+						
+						$temp  = $this->rd_key[11 + $rk];
+						$this->rd_key[12 + $rk] = $this->rd_key[4 + $rk] ^
+						($this->Te4[($temp >> 24) & 0xff] & 0xff000000) ^
+						($this->Te4[($temp >> 16) & 0xff] & 0x00ff0000) ^
+						($this->Te4[($temp >>  8) & 0xff] & 0x0000ff00) ^
+						($this->Te4[($temp      ) & 0xff] & 0x000000ff);
+			
+			$this->rd_key[13 + $rk] = $this->rd_key[5 + $rk] ^ $this->rd_key[12 + $rk];
+						$this->rd_key[14 + $rk] = $this->rd_key[6 + $rk] ^ $this->rd_key[13 + $rk];
+						$this->rd_key[15 + $rk] = $this->rd_key[7 + $rk] ^ $this->rd_key[14 + $rk];
+						$rk += 8;
+				}
+		}
+		return 0;
+	}
 
 
-  // Expand the cipher key into the decryption key schedule. 
-  function set_decrypt()
-  {
-    $i = 0;
-    $j = 0;
-    $rk = 0;
-    $bits =& $this->bits;
-    
-    if ( $this->key_state === 'decrypt' )
-    {
-      return 0;
-    }
-    
-    $this->key_state = 'decrypt';
-    
-    // first, start with an encryption schedule 
-    $status = $this->set_encrypt();
-    
-    // set the state again because set_encrypt() will change it
-    $this->key_state = 'decrypt';
-    
-    if ($status < 0) {
-      librijndael2::trace("AES_set_decrypt_key: AES_set_encrypt_key error");
-      return;
-    }
-  
-    // invert the order of the round keys: 
-    for ($i = 0, $j = 4*($this->rounds); $i < $j; $i += 4, $j -= 4) {
-      $temp = $this->rd_key[$i];   $this->rd_key[$i]   = $this->rd_key[$j];   $this->rd_key[$j]   = $temp;
-      $temp = $this->rd_key[$i+1]; $this->rd_key[$i+1] = $this->rd_key[$j+1]; $this->rd_key[$j+1] = $temp;
-      $temp = $this->rd_key[$i+2]; $this->rd_key[$i+2] = $this->rd_key[$j+2]; $this->rd_key[$j+2] = $temp;
-      $temp = $this->rd_key[$i+3]; $this->rd_key[$i+3] = $this->rd_key[$j+3]; $this->rd_key[$j+3] = $temp;
-    }
-    
-    // apply the inverse MixColumn transform to all round keys but the first and the last: 
-    for ($i = 1; $i < ($this->rounds); $i++) {
+	// Expand the cipher key into the decryption key schedule. 
+	function set_decrypt()
+	{
+		$i = 0;
+		$j = 0;
+		$rk = 0;
+		$bits =& $this->bits;
+		
+		if ( $this->key_state === 'decrypt' )
+		{
+			return 0;
+		}
+		
+		$this->key_state = 'decrypt';
+		
+		// first, start with an encryption schedule 
+		$status = $this->set_encrypt();
+		
+		// set the state again because set_encrypt() will change it
+		$this->key_state = 'decrypt';
+		
+		if ($status < 0) {
+			librijndael2::trace("AES_set_decrypt_key: AES_set_encrypt_key error");
+			return;
+		}
+	
+		// invert the order of the round keys: 
+		for ($i = 0, $j = 4*($this->rounds); $i < $j; $i += 4, $j -= 4) {
+			$temp = $this->rd_key[$i];   $this->rd_key[$i]   = $this->rd_key[$j];   $this->rd_key[$j]   = $temp;
+			$temp = $this->rd_key[$i+1]; $this->rd_key[$i+1] = $this->rd_key[$j+1]; $this->rd_key[$j+1] = $temp;
+			$temp = $this->rd_key[$i+2]; $this->rd_key[$i+2] = $this->rd_key[$j+2]; $this->rd_key[$j+2] = $temp;
+			$temp = $this->rd_key[$i+3]; $this->rd_key[$i+3] = $this->rd_key[$j+3]; $this->rd_key[$j+3] = $temp;
+		}
+		
+		// apply the inverse MixColumn transform to all round keys but the first and the last: 
+		for ($i = 1; $i < ($this->rounds); $i++) {
 
-        $rk += 4;
-        $this->rd_key[$rk] =
-        $this->Td0[$this->Te4[($this->rd_key[$rk] >> 24) & 0xff] & 0xff] ^
-        $this->Td1[$this->Te4[($this->rd_key[$rk] >> 16) & 0xff] & 0xff] ^
-        $this->Td2[$this->Te4[($this->rd_key[$rk] >>  8) & 0xff] & 0xff] ^
-        $this->Td3[$this->Te4[($this->rd_key[$rk]      ) & 0xff] & 0xff];
-       
-        $this->rd_key[1+$rk] =
-        $this->Td0[$this->Te4[($this->rd_key[1+$rk] >> 24) & 0xff] & 0xff] ^
-        $this->Td1[$this->Te4[($this->rd_key[1+$rk] >> 16) & 0xff] & 0xff] ^
-        $this->Td2[$this->Te4[($this->rd_key[1+$rk] >>  8) & 0xff] & 0xff] ^
-        $this->Td3[$this->Te4[($this->rd_key[1+$rk]      ) & 0xff] & 0xff];
+				$rk += 4;
+				$this->rd_key[$rk] =
+				$this->Td0[$this->Te4[($this->rd_key[$rk] >> 24) & 0xff] & 0xff] ^
+				$this->Td1[$this->Te4[($this->rd_key[$rk] >> 16) & 0xff] & 0xff] ^
+				$this->Td2[$this->Te4[($this->rd_key[$rk] >>  8) & 0xff] & 0xff] ^
+				$this->Td3[$this->Te4[($this->rd_key[$rk]      ) & 0xff] & 0xff];
+ 			
+				$this->rd_key[1+$rk] =
+				$this->Td0[$this->Te4[($this->rd_key[1+$rk] >> 24) & 0xff] & 0xff] ^
+				$this->Td1[$this->Te4[($this->rd_key[1+$rk] >> 16) & 0xff] & 0xff] ^
+				$this->Td2[$this->Te4[($this->rd_key[1+$rk] >>  8) & 0xff] & 0xff] ^
+				$this->Td3[$this->Te4[($this->rd_key[1+$rk]      ) & 0xff] & 0xff];
 
-        $this->rd_key[2+$rk] =
-        $this->Td0[$this->Te4[($this->rd_key[2+$rk] >> 24) & 0xff] & 0xff] ^
-        $this->Td1[$this->Te4[($this->rd_key[2+$rk] >> 16) & 0xff] & 0xff] ^
-        $this->Td2[$this->Te4[($this->rd_key[2+$rk] >>  8) & 0xff] & 0xff] ^
-        $this->Td3[$this->Te4[($this->rd_key[2+$rk]      ) & 0xff] & 0xff];
+				$this->rd_key[2+$rk] =
+				$this->Td0[$this->Te4[($this->rd_key[2+$rk] >> 24) & 0xff] & 0xff] ^
+				$this->Td1[$this->Te4[($this->rd_key[2+$rk] >> 16) & 0xff] & 0xff] ^
+				$this->Td2[$this->Te4[($this->rd_key[2+$rk] >>  8) & 0xff] & 0xff] ^
+				$this->Td3[$this->Te4[($this->rd_key[2+$rk]      ) & 0xff] & 0xff];
  
-        $this->rd_key[3+$rk] =
-        $this->Td0[$this->Te4[($this->rd_key[3+$rk] >> 24) & 0xff] & 0xff] ^
-        $this->Td1[$this->Te4[($this->rd_key[3+$rk] >> 16) & 0xff] & 0xff] ^
-        $this->Td2[$this->Te4[($this->rd_key[3+$rk] >>  8) & 0xff] & 0xff] ^
-        $this->Td3[$this->Te4[($this->rd_key[3+$rk]      ) & 0xff] & 0xff];
-    } 
-    return 0;
-  }
+				$this->rd_key[3+$rk] =
+				$this->Td0[$this->Te4[($this->rd_key[3+$rk] >> 24) & 0xff] & 0xff] ^
+				$this->Td1[$this->Te4[($this->rd_key[3+$rk] >> 16) & 0xff] & 0xff] ^
+				$this->Td2[$this->Te4[($this->rd_key[3+$rk] >>  8) & 0xff] & 0xff] ^
+				$this->Td3[$this->Te4[($this->rd_key[3+$rk]      ) & 0xff] & 0xff];
+		} 
+		return 0;
+	}
 }
 
 /**
@@ -1410,381 +1410,381 @@
 
 class AESCrypt
 {
-  
-  /**
-   * Fetches a Crypt_Rijndael_Key object for the given hex or binary key.
-   * @param string Key, binary or hex format
-   * @return object
-   * @access protected
-   */
-   
-  protected static function fetch_key($key)
-  {
-    static $objects = array();
-    
-    if ( !preg_match('/^[a-f0-9]+$/i', $key) )
-      $key = librijndael2::string2hex($key);
-    
-    if ( isset($objects[$key]) )
-    {
-      return $objects[$key];
-    }
-    else
-    {
-      $objects[$key] = new Crypt_Rijndael_Key($key);
-      $ret =& $objects[$key];
-      return $ret;
-    }
-  }
-  
-  /**
-   * Fetches a Crypt_Rijndael object.
-   * @return object
-   * @access protected
-   */
-   
-  protected static function fetch_cr_singleton()
-  {
-    static $o = null;
-    
-    if ( is_object($o) )
-    {
-      return $o;
-    }
-    else
-    {
-      $o = new Crypt_Rijndael();
-      $r =& $o;
-      return $r;
-    }
-  }
-  
-  /**
-   * Pads a string with nul bytes until it reaches a multiple of 16 bytes.
-   * @param string
-   * @return string
-   */
-  
-  protected function pad_string($string)
-  {
-    while ( strlen($string) % 32 > 0 )
-    {
-      $string .= "00";
-    }
-    return $string;
-  }
-  
-  /**
-   * Constructor. Currently does not take parameters and ignores options from the old API.
-   */
-  
-  public function __construct($key_size = 192, $block_size = 128, $debug = false)
-  {
-  }
-  
-  /**
-   * Wrapper for encryption.
-   * @param string Plain text to encrypt
-   * @param string Binary or hex key
-   * @param int Format to return result in - ENC_BINARY, ENC_HEX, or ENC_BASE64
-   * @return string
-   */
-  
-  public function encrypt($plaintext, $key, $return_format = ENC_HEX)
-  {
-    return $this->encrypt_cbc($plaintext, $key, '00000000000000000000000000000000', ENC_HEX);
-  }
-  
-  /**
-   * Wrapper for decryption.
-   * @param string Encrypted text
-   * @param string Binary or hex key
-   * @param int Format that the encrypted text is in - ENC_BINARY, ENC_HEX, or ENC_BASE64
-   * @param bool If true, avoids caching the decryption result.
-   * @return string
-   */
-  
-  public function decrypt($cryptext, $key, $input_format = ENC_HEX, $no_cache = false)
-  {
-    return $this->decrypt_cbc($cryptext, $key, '00000000000000000000000000000000', ENC_HEX, $no_cache);
-  }
-  
-  /**
-   * Wrapper for CBC encryption.
-   * @param string Plain text to encrypt
-   * @param string Binary or hex key
-   * @param string 128-bit initialization vector, either hex or binary will do
-   * @param int Format to return result in - ENC_BINARY, ENC_HEX, or ENC_BASE64
-   */
-  
-  public function encrypt_cbc($plaintext, $key, $ivec, $return_format = ENC_HEX)
-  {
-    $okey = self::fetch_key($key);
-    $okey->set_encrypt();
-    $aes = self::fetch_cr_singleton();
-    $plaintext = librijndael2::string2hex($plaintext);
-    
-    // init ivec
-    if ( strlen($ivec) == '16' )
-    {
-      $ivec = librijndael2::string2hex($ivec);
-    }
-    else if ( preg_match('/^[a-f0-9]+$/i', $ivec) && strlen($ivec) == 32 )
-    {
-      // ivec is good
-    }
-    else
-    {
-      // invalid ivec
-      return false;
-    }
-    
-    $result = '';
-    $plaintext = str_split($this->pad_string($plaintext), 32);
-    foreach ( $plaintext as $block )
-    {
-      $block = $aes->AES_cbc_encrypt($block, $okey, $ivec, 'AES_ENCRYPT');
-      $result .= $block;
-    }
-    
-    switch ( $return_format )
-    {
-      case ENC_BINARY:
-        return librijndael2::hex2string($result);
-      case ENC_HEX:
-      default:
-        return $result;
-      case ENC_BASE64:
-        return base64_encode(librijndael2::hex2string($result));
-    }
-  }
-  
-  /**
-   * Wrapper for CBC decryption.
-   * @param string Encrypted text
-   * @param string Binary or hex key
-   * @param string 128-bit initialization vector, either hex or binary will do
-   * @param int Format that the encrypted text is in - ENC_BINARY, ENC_HEX, or ENC_BASE64
-   * @param bool If true, avoids caching the decryption result.
-   * @return string
-   */
-  
-  public function decrypt_cbc($cryptext, $key, $ivec, $input_format = ENC_HEX, $no_cache = false)
-  {
-    // AES_cbc_encrypt() expects hex
-    switch ( $input_format )
-    {
-      case ENC_BINARY:
-        $cryptext = librijndael2::string2hex($cryptext);
-        break;
-      case ENC_BASE64:
-        $cryptext = librijndael2::string2hex(base64_decode($cryptext));
-        break;
-    }
-    
-    $hash = sha1("{$cryptext}::{$key}");
-    if ( $cache_result = aes_decrypt_cache_fetch($hash) )
-      return $cache_result;
-    
-    // load objects
-    profiler_log("AES: Started decryption of string $hash");
-    $okey = self::fetch_key($key);
-    $okey->set_decrypt();
-    $aes = self::fetch_cr_singleton();
-    
-    // init ivec
-    if ( strlen($ivec) == '16' )
-    {
-      $ivec = librijndael2::string2hex($ivec);
-    }
-    else if ( preg_match('/^[a-f0-9]+$/i', $ivec) && strlen($ivec) == 32 )
-    {
-      // ivec is good
-    }
-    else
-    {
-      // invalid ivec
-      return false;
-    }
-    
-    // perform decryption
-    $result = '';
-    $cryptext_orig = $cryptext;
-    $cryptext = enano_str_split($cryptext, 32);
-    foreach ( $cryptext as $block )
-    {
-      $block = $aes->AES_cbc_encrypt($block, $okey, $ivec, 'AES_DECRYPT');
-      $result .= $block;
-    }
-    
-    // decode result and trim nul bytes
-    $result = librijndael2::hex2string($result);
-    $result = rtrim($result, "\000");
-    
-    if ( !$no_cache )
-      aes_decrypt_cache_store($cryptext_orig, $result, $key);
-    
-    profiler_log("AES: Finished decryption of string $hash");
-    
-    // done! :)
-    return $result;
-  }
-  
-  /**
-   * Factory.
-   * @param int Key size in bits
-   * @param int Block size in bits - ONLY 128-bit supported.
-   * @return object Instance of AESCrypt
-   */
-  
-  public static function singleton($key_size = AES_BITS, $block_size = 128)
-  {
-    static $instance = false;
-    if ( !$instance )
-    {
-      $class = __CLASS__;
-      $instance = new $class();
-    }
-    return $instance;
-  }
-  
-  #
-  # Utility functions
-  #
-  
-  /**
-   * Generates a random key suitable for encryption
-   * @param int $len the length of the key, in bytes
-   * @return string a BINARY key
-   */
-  
-  static function randkey($len = 32)
-  {
-    if ( @file_exists('/dev/urandom') && @is_readable('/dev/urandom') )
-    {
-      // Let's use something a little more secure
-      $ur = @fopen('/dev/urandom', 'r');
-      if ( !$ur )
-        return self::randkey_safe($len);
-      $ukey = @fread($ur, $len);
-      fclose($ur);
-      if ( strlen($ukey) != $len )
-        return self::randkey_safe($len);
-      return $ukey;
-    }
-    return self::randkey_safe($len);
-  }
-  
-  static function randkey_safe($len = 32)
-  {
-    $key = '';
-    for($i=0;$i<$len;$i++)
-    {
-      $key .= chr(mt_rand(0, 255));
-    }
-    return $key;
-  }
-  
-  function gen_readymade_key()
-  {
-    $key = librijndael2::string2hex($this->randkey(AES_BITS / 8));
-    return $key;
-  }
+	
+	/**
+ 	* Fetches a Crypt_Rijndael_Key object for the given hex or binary key.
+ 	* @param string Key, binary or hex format
+ 	* @return object
+ 	* @access protected
+ 	*/
+ 	
+	protected static function fetch_key($key)
+	{
+		static $objects = array();
+		
+		if ( !preg_match('/^[a-f0-9]+$/i', $key) )
+			$key = librijndael2::string2hex($key);
+		
+		if ( isset($objects[$key]) )
+		{
+			return $objects[$key];
+		}
+		else
+		{
+			$objects[$key] = new Crypt_Rijndael_Key($key);
+			$ret =& $objects[$key];
+			return $ret;
+		}
+	}
+	
+	/**
+ 	* Fetches a Crypt_Rijndael object.
+ 	* @return object
+ 	* @access protected
+ 	*/
+ 	
+	protected static function fetch_cr_singleton()
+	{
+		static $o = null;
+		
+		if ( is_object($o) )
+		{
+			return $o;
+		}
+		else
+		{
+			$o = new Crypt_Rijndael();
+			$r =& $o;
+			return $r;
+		}
+	}
+	
+	/**
+ 	* Pads a string with nul bytes until it reaches a multiple of 16 bytes.
+ 	* @param string
+ 	* @return string
+ 	*/
+	
+	protected function pad_string($string)
+	{
+		while ( strlen($string) % 32 > 0 )
+		{
+			$string .= "00";
+		}
+		return $string;
+	}
+	
+	/**
+ 	* Constructor. Currently does not take parameters and ignores options from the old API.
+ 	*/
+	
+	public function __construct($key_size = 192, $block_size = 128, $debug = false)
+	{
+	}
+	
+	/**
+ 	* Wrapper for encryption.
+ 	* @param string Plain text to encrypt
+ 	* @param string Binary or hex key
+ 	* @param int Format to return result in - ENC_BINARY, ENC_HEX, or ENC_BASE64
+ 	* @return string
+ 	*/
+	
+	public function encrypt($plaintext, $key, $return_format = ENC_HEX)
+	{
+		return $this->encrypt_cbc($plaintext, $key, '00000000000000000000000000000000', ENC_HEX);
+	}
+	
+	/**
+ 	* Wrapper for decryption.
+ 	* @param string Encrypted text
+ 	* @param string Binary or hex key
+ 	* @param int Format that the encrypted text is in - ENC_BINARY, ENC_HEX, or ENC_BASE64
+ 	* @param bool If true, avoids caching the decryption result.
+ 	* @return string
+ 	*/
+	
+	public function decrypt($cryptext, $key, $input_format = ENC_HEX, $no_cache = false)
+	{
+		return $this->decrypt_cbc($cryptext, $key, '00000000000000000000000000000000', ENC_HEX, $no_cache);
+	}
+	
+	/**
+ 	* Wrapper for CBC encryption.
+ 	* @param string Plain text to encrypt
+ 	* @param string Binary or hex key
+ 	* @param string 128-bit initialization vector, either hex or binary will do
+ 	* @param int Format to return result in - ENC_BINARY, ENC_HEX, or ENC_BASE64
+ 	*/
+	
+	public function encrypt_cbc($plaintext, $key, $ivec, $return_format = ENC_HEX)
+	{
+		$okey = self::fetch_key($key);
+		$okey->set_encrypt();
+		$aes = self::fetch_cr_singleton();
+		$plaintext = librijndael2::string2hex($plaintext);
+		
+		// init ivec
+		if ( strlen($ivec) == '16' )
+		{
+			$ivec = librijndael2::string2hex($ivec);
+		}
+		else if ( preg_match('/^[a-f0-9]+$/i', $ivec) && strlen($ivec) == 32 )
+		{
+			// ivec is good
+		}
+		else
+		{
+			// invalid ivec
+			return false;
+		}
+		
+		$result = '';
+		$plaintext = str_split($this->pad_string($plaintext), 32);
+		foreach ( $plaintext as $block )
+		{
+			$block = $aes->AES_cbc_encrypt($block, $okey, $ivec, 'AES_ENCRYPT');
+			$result .= $block;
+		}
+		
+		switch ( $return_format )
+		{
+			case ENC_BINARY:
+				return librijndael2::hex2string($result);
+			case ENC_HEX:
+			default:
+				return $result;
+			case ENC_BASE64:
+				return base64_encode(librijndael2::hex2string($result));
+		}
+	}
+	
+	/**
+ 	* Wrapper for CBC decryption.
+ 	* @param string Encrypted text
+ 	* @param string Binary or hex key
+ 	* @param string 128-bit initialization vector, either hex or binary will do
+ 	* @param int Format that the encrypted text is in - ENC_BINARY, ENC_HEX, or ENC_BASE64
+ 	* @param bool If true, avoids caching the decryption result.
+ 	* @return string
+ 	*/
+	
+	public function decrypt_cbc($cryptext, $key, $ivec, $input_format = ENC_HEX, $no_cache = false)
+	{
+		// AES_cbc_encrypt() expects hex
+		switch ( $input_format )
+		{
+			case ENC_BINARY:
+				$cryptext = librijndael2::string2hex($cryptext);
+				break;
+			case ENC_BASE64:
+				$cryptext = librijndael2::string2hex(base64_decode($cryptext));
+				break;
+		}
+		
+		$hash = sha1("{$cryptext}::{$key}");
+		if ( $cache_result = aes_decrypt_cache_fetch($hash) )
+			return $cache_result;
+		
+		// load objects
+		profiler_log("AES: Started decryption of string $hash");
+		$okey = self::fetch_key($key);
+		$okey->set_decrypt();
+		$aes = self::fetch_cr_singleton();
+		
+		// init ivec
+		if ( strlen($ivec) == '16' )
+		{
+			$ivec = librijndael2::string2hex($ivec);
+		}
+		else if ( preg_match('/^[a-f0-9]+$/i', $ivec) && strlen($ivec) == 32 )
+		{
+			// ivec is good
+		}
+		else
+		{
+			// invalid ivec
+			return false;
+		}
+		
+		// perform decryption
+		$result = '';
+		$cryptext_orig = $cryptext;
+		$cryptext = enano_str_split($cryptext, 32);
+		foreach ( $cryptext as $block )
+		{
+			$block = $aes->AES_cbc_encrypt($block, $okey, $ivec, 'AES_DECRYPT');
+			$result .= $block;
+		}
+		
+		// decode result and trim nul bytes
+		$result = librijndael2::hex2string($result);
+		$result = rtrim($result, "\000");
+		
+		if ( !$no_cache )
+			aes_decrypt_cache_store($cryptext_orig, $result, $key);
+		
+		profiler_log("AES: Finished decryption of string $hash");
+		
+		// done! :)
+		return $result;
+	}
+	
+	/**
+ 	* Factory.
+ 	* @param int Key size in bits
+ 	* @param int Block size in bits - ONLY 128-bit supported.
+ 	* @return object Instance of AESCrypt
+ 	*/
+	
+	public static function singleton($key_size = AES_BITS, $block_size = 128)
+	{
+		static $instance = false;
+		if ( !$instance )
+		{
+			$class = __CLASS__;
+			$instance = new $class();
+		}
+		return $instance;
+	}
+	
+	#
+	# Utility functions
+	#
+	
+	/**
+ 	* Generates a random key suitable for encryption
+ 	* @param int $len the length of the key, in bytes
+ 	* @return string a BINARY key
+ 	*/
+	
+	static function randkey($len = 32)
+	{
+		if ( @file_exists('/dev/urandom') && @is_readable('/dev/urandom') )
+		{
+			// Let's use something a little more secure
+			$ur = @fopen('/dev/urandom', 'r');
+			if ( !$ur )
+				return self::randkey_safe($len);
+			$ukey = @fread($ur, $len);
+			fclose($ur);
+			if ( strlen($ukey) != $len )
+				return self::randkey_safe($len);
+			return $ukey;
+		}
+		return self::randkey_safe($len);
+	}
+	
+	static function randkey_safe($len = 32)
+	{
+		$key = '';
+		for($i=0;$i<$len;$i++)
+		{
+			$key .= chr(mt_rand(0, 255));
+		}
+		return $key;
+	}
+	
+	function gen_readymade_key()
+	{
+		$key = librijndael2::string2hex($this->randkey(AES_BITS / 8));
+		return $key;
+	}
 }
 
 function aes_decrypt_cache_store($encrypted, $decrypted, $key)
 {
-  if ( getConfig('cache_thumbs') != '1' )
-    return false;
-  
-  $cache_file = ENANO_ROOT . '/cache/aes_decrypt.php';
-  // only cache if $decrypted is long enough to actually warrant caching
-  if ( strlen($decrypted) < 32 )
-  {
-    profiler_log("AES: Skipped caching a string (probably a password, we dunno) because it's too short");
-    return false;
-  }
-  if ( file_exists($cache_file) )
-  {
-    require_once($cache_file);
-    global $aes_decrypt_cache;
-    $cachekey = sha1($encrypted . '::' . $key);
-    $aes_decrypt_cache[$cachekey] = $decrypted;
-    
-    if ( count($aes_decrypt_cache) > 5000 )
-    {
-      // we've got a lot of strings in the cache, clear out a few
-      $keys = array_keys($aes_decrypt_cache);
-      for ( $i = 0; $i < 2500; $i++ )
-      {
-        unset($aes_decrypt_cache[$keys[$i]]);
-        unset($aes_decrypt_cache[$keys[$i]]);
-      }
-    }
-  }
-  else
-  {
-    $aes_decrypt_cache = array(
-      sha1($encrypted . '::' . $key) => $decrypted
-    );
-  }
-  // call var_export and collect contents
-  ob_start();
-  var_export($aes_decrypt_cache);
-  $dec_cache_string = ob_get_contents();
-  ob_end_clean();
-  $f = @fopen($cache_file, 'w');
-  if ( !$f )
-    return false;
-  fwrite($f, "<?php
+	if ( getConfig('cache_thumbs') != '1' )
+		return false;
+	
+	$cache_file = ENANO_ROOT . '/cache/aes_decrypt.php';
+	// only cache if $decrypted is long enough to actually warrant caching
+	if ( strlen($decrypted) < 32 )
+	{
+		profiler_log("AES: Skipped caching a string (probably a password, we dunno) because it's too short");
+		return false;
+	}
+	if ( file_exists($cache_file) )
+	{
+		require_once($cache_file);
+		global $aes_decrypt_cache;
+		$cachekey = sha1($encrypted . '::' . $key);
+		$aes_decrypt_cache[$cachekey] = $decrypted;
+		
+		if ( count($aes_decrypt_cache) > 5000 )
+		{
+			// we've got a lot of strings in the cache, clear out a few
+			$keys = array_keys($aes_decrypt_cache);
+			for ( $i = 0; $i < 2500; $i++ )
+			{
+				unset($aes_decrypt_cache[$keys[$i]]);
+				unset($aes_decrypt_cache[$keys[$i]]);
+			}
+		}
+	}
+	else
+	{
+		$aes_decrypt_cache = array(
+			sha1($encrypted . '::' . $key) => $decrypted
+		);
+	}
+	// call var_export and collect contents
+	ob_start();
+	var_export($aes_decrypt_cache);
+	$dec_cache_string = ob_get_contents();
+	ob_end_clean();
+	$f = @fopen($cache_file, 'w');
+	if ( !$f )
+		return false;
+	fwrite($f, "<?php
 \$GLOBALS['aes_decrypt_cache'] = $dec_cache_string;
 ");
-  fclose($f);
-  return true;
+	fclose($f);
+	return true;
 }
 
 function aes_decrypt_cache_fetch($hash)
 {
-  $cache_file = ENANO_ROOT . '/cache/aes_decrypt.php';
-  if ( !file_exists($cache_file) )
-    return false;
-  
-  require_once($cache_file);
-  global $aes_decrypt_cache;
-  if ( isset($aes_decrypt_cache[$hash]) )
-  {
-    profiler_log("AES: Loaded cached decrypted string, hash is $hash");
-    return $aes_decrypt_cache[$hash];
-  }
-  
-  return false;
+	$cache_file = ENANO_ROOT . '/cache/aes_decrypt.php';
+	if ( !file_exists($cache_file) )
+		return false;
+	
+	require_once($cache_file);
+	global $aes_decrypt_cache;
+	if ( isset($aes_decrypt_cache[$hash]) )
+	{
+		profiler_log("AES: Loaded cached decrypted string, hash is $hash");
+		return $aes_decrypt_cache[$hash];
+	}
+	
+	return false;
 }
 
 function aes_decrypt_cache_destroy($hash)
 {
-  $cache_file = ENANO_ROOT . '/cache/aes_decrypt.php';
-  if ( !file_exists($cache_file) )
-    return false;
-  
-  require_once($cache_file);
-  global $aes_decrypt_cache;
-  
-  if ( isset($aes_decrypt_cache[$hash]) )
-    unset($aes_decrypt_cache[$hash]);
-  
-  // call var_export and collect contents
-  ob_start();
-  var_export($aes_decrypt_cache);
-  $dec_cache_string = ob_get_contents();
-  ob_end_clean();
-  $f = @fopen($cache_file, 'w');
-  if ( !$f )
-    return false;
-  fwrite($f, "<?php
+	$cache_file = ENANO_ROOT . '/cache/aes_decrypt.php';
+	if ( !file_exists($cache_file) )
+		return false;
+	
+	require_once($cache_file);
+	global $aes_decrypt_cache;
+	
+	if ( isset($aes_decrypt_cache[$hash]) )
+		unset($aes_decrypt_cache[$hash]);
+	
+	// call var_export and collect contents
+	ob_start();
+	var_export($aes_decrypt_cache);
+	$dec_cache_string = ob_get_contents();
+	ob_end_clean();
+	$f = @fopen($cache_file, 'w');
+	if ( !$f )
+		return false;
+	fwrite($f, "<?php
 \$GLOBALS['aes_decrypt_cache'] = $dec_cache_string;
 ");
-  fclose($f);
-  return true;
+	fclose($f);
+	return true;
 }
 
 ?>
--- a/includes/search.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/search.php	Sun Mar 28 23:10:46 2010 -0400
@@ -25,63 +25,63 @@
 class Searcher
 {
 
-  var $results;
-  var $index;
-  var $warnings;
-  var $match_case = false;
+	var $results;
+	var $index;
+	var $warnings;
+	var $match_case = false;
 
-  function buildIndex($texts)
-  {
-    $this->index = Array();
-    $stopwords = get_stopwords();
+	function buildIndex($texts)
+	{
+		$this->index = Array();
+		$stopwords = get_stopwords();
 
-    foreach($texts as $i => $l)
-    {
-      $seed = md5(microtime(true) . mt_rand());
-      $texts[$i] = str_replace("'", 'xxxApoS'.$seed.'xxx', $texts[$i]);
-      $texts[$i] = preg_replace('#([\W_]+)#i', ' ', $texts[$i]);
-      $texts[$i] = preg_replace('#([ ]+?)#', ' ', $texts[$i]);
-      $texts[$i] = preg_replace('#([\']*){2,}#s', '', $texts[$i]);
-      $texts[$i] = str_replace('xxxApoS'.$seed.'xxx', "'", $texts[$i]);
-      $l = $texts[$i];
-      $words = Array();
-      $good_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\' ';
-      $good_chars = enano_str_split($good_chars, 1);
-      $letters = enano_str_split($l, 1);
-      foreach($letters as $x => $t)
-      {
-        if(!in_array($t, $good_chars))
-          unset($letters[$x]);
-      }
-      $letters = implode('', $letters);
-      $words = explode(' ', $letters);
-      foreach($words as $c => $w)
-      {
-        if(strlen($w) < 2 || in_array($w, $stopwords) || strlen($w) > 63 || preg_match('/[\']{2,}/', $w))
-          unset($words[$c]);
-        else
-          $words[$c] = $w;
-      }
-      $words = array_values($words);
-      foreach($words as $c => $w)
-      {
-        if(isset($this->index[$w]))
-        {
-          if(!in_array($i, $this->index[$w]))
-            $this->index[$w][] = $i;
-        }
-        else
-        {
-          $this->index[$w] = Array();
-          $this->index[$w][] = $i;
-        }
-      }
-    }
-    foreach($this->index as $k => $v)
-    {
-      $this->index[$k] = implode(',', $this->index[$k]);
-    }
-  }
+		foreach($texts as $i => $l)
+		{
+			$seed = md5(microtime(true) . mt_rand());
+			$texts[$i] = str_replace("'", 'xxxApoS'.$seed.'xxx', $texts[$i]);
+			$texts[$i] = preg_replace('#([\W_]+)#i', ' ', $texts[$i]);
+			$texts[$i] = preg_replace('#([ ]+?)#', ' ', $texts[$i]);
+			$texts[$i] = preg_replace('#([\']*){2,}#s', '', $texts[$i]);
+			$texts[$i] = str_replace('xxxApoS'.$seed.'xxx', "'", $texts[$i]);
+			$l = $texts[$i];
+			$words = Array();
+			$good_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\' ';
+			$good_chars = enano_str_split($good_chars, 1);
+			$letters = enano_str_split($l, 1);
+			foreach($letters as $x => $t)
+			{
+				if(!in_array($t, $good_chars))
+					unset($letters[$x]);
+			}
+			$letters = implode('', $letters);
+			$words = explode(' ', $letters);
+			foreach($words as $c => $w)
+			{
+				if(strlen($w) < 2 || in_array($w, $stopwords) || strlen($w) > 63 || preg_match('/[\']{2,}/', $w))
+					unset($words[$c]);
+				else
+					$words[$c] = $w;
+			}
+			$words = array_values($words);
+			foreach($words as $c => $w)
+			{
+				if(isset($this->index[$w]))
+				{
+					if(!in_array($i, $this->index[$w]))
+						$this->index[$w][] = $i;
+				}
+				else
+				{
+					$this->index[$w] = Array();
+					$this->index[$w][] = $i;
+				}
+			}
+		}
+		foreach($this->index as $k => $v)
+		{
+			$this->index[$k] = implode(',', $this->index[$k]);
+		}
+	}
 }
 
 /**
@@ -100,486 +100,486 @@
 
 function perform_search($query, &$warnings, $case_sensitive = false, &$word_list)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $warnings = array();
-  
-  //
-  // STAGE 0: PARSE SEARCH QUERY
-  // Identify all terms of the query. Separate between what is required and what is not, and what should be sent through the index as
-  // opposed to straight-out LIKE-selected.
-  //
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$warnings = array();
+	
+	//
+	// STAGE 0: PARSE SEARCH QUERY
+	// Identify all terms of the query. Separate between what is required and what is not, and what should be sent through the index as
+	// opposed to straight-out LIKE-selected.
+	//
 
-  $query = parse_search_query($query, $warnings);
+	$query = parse_search_query($query, $warnings);
 
-  // Segregate search terms containing spaces
-  $query_phrase = array(
-    'any' => array(),
-    'req' => array()
-    );
+	// Segregate search terms containing spaces
+	$query_phrase = array(
+		'any' => array(),
+		'req' => array()
+		);
 
-  foreach ( $query['any'] as $i => $_ )
-  {
-    $term =& $query['any'][$i];
-    $term = trim($term);
-    // the indexer only indexes words a-z with apostrophes
-    if ( preg_match('/[^A-Za-z\']/', $term) )
-    {
-      $query_phrase['any'][] = $term;
-      unset($term, $query['any'][$i]);
-    }
-  }
-  unset($term);
-  $query['any'] = array_values($query['any']);
+	foreach ( $query['any'] as $i => $_ )
+	{
+		$term =& $query['any'][$i];
+		$term = trim($term);
+		// the indexer only indexes words a-z with apostrophes
+		if ( preg_match('/[^A-Za-z\']/', $term) )
+		{
+			$query_phrase['any'][] = $term;
+			unset($term, $query['any'][$i]);
+		}
+	}
+	unset($term);
+	$query['any'] = array_values($query['any']);
 
-  foreach ( $query['req'] as $i => $_ )
-  {
-    $term =& $query['req'][$i];
-    $term = trim($term);
-    if ( preg_match('/[^A-Za-z\']/', $term) )
-    {
-      $query_phrase['req'][] = $term;
-      unset($term, $query['req'][$i]);
-    }
-  }
-  unset($term);
-  $query['req'] = array_values($query['req']);
+	foreach ( $query['req'] as $i => $_ )
+	{
+		$term =& $query['req'][$i];
+		$term = trim($term);
+		if ( preg_match('/[^A-Za-z\']/', $term) )
+		{
+			$query_phrase['req'][] = $term;
+			unset($term, $query['req'][$i]);
+		}
+	}
+	unset($term);
+	$query['req'] = array_values($query['req']);
 
-  $results = array();
-  $scores = array();
-  $ns_list = '(' . implode('|', array_keys($paths->nslist)) . ')';
+	$results = array();
+	$scores = array();
+	$ns_list = '(' . implode('|', array_keys($paths->nslist)) . ')';
 
-  // FIXME: Update to use FULLTEXT algo when available.
+	// FIXME: Update to use FULLTEXT algo when available.
 
-  // Build an SQL query to load from the index table
-  if ( count($query['any']) < 1 && count($query['req']) < 1 && count($query_phrase['any']) < 1 && count($query_phrase['req']) < 1 )
-  {
-    // This is both because of technical restrictions and devastation that would occur on shared servers/large sites.
-    $warnings[] = $lang->get('search_err_query_no_positive');
-    return array();
-  }
+	// Build an SQL query to load from the index table
+	if ( count($query['any']) < 1 && count($query['req']) < 1 && count($query_phrase['any']) < 1 && count($query_phrase['req']) < 1 )
+	{
+		// This is both because of technical restrictions and devastation that would occur on shared servers/large sites.
+		$warnings[] = $lang->get('search_err_query_no_positive');
+		return array();
+	}
 
-  //
-  // STAGE 1
-  // Get all possible result pages from the search index. Tally which pages have the most words, and later sort them by boolean relevance
-  //
+	//
+	// STAGE 1
+	// Get all possible result pages from the search index. Tally which pages have the most words, and later sort them by boolean relevance
+	//
 
-  // Skip this if no indexable words are included
+	// Skip this if no indexable words are included
 
-  if ( count($query['any']) > 0 || count($query['req']) > 0 )
-  {
-    $where_any = array();
-    foreach ( $query['any'] as $term )
-    {
-      $term = escape_string_like($term);
-      if ( !$case_sensitive )
-        $term = strtolower($term);
-      $where_any[] = $term;
-    }
-    foreach ( $query['req'] as $term )
-    {
-      $term = escape_string_like($term);
-      if ( !$case_sensitive )
-        $term = strtolower($term);
-      $where_any[] = $term;
-    }
+	if ( count($query['any']) > 0 || count($query['req']) > 0 )
+	{
+		$where_any = array();
+		foreach ( $query['any'] as $term )
+		{
+			$term = escape_string_like($term);
+			if ( !$case_sensitive )
+				$term = strtolower($term);
+			$where_any[] = $term;
+		}
+		foreach ( $query['req'] as $term )
+		{
+			$term = escape_string_like($term);
+			if ( !$case_sensitive )
+				$term = strtolower($term);
+			$where_any[] = $term;
+		}
 
-    $col_word = ( $case_sensitive ) ? 'word' : 'word_lcase';
-    $where_any_str = ( count($where_any) > 0 ) ? '( ' . $col_word . ' LIKE \'%' . implode('%\' OR ' . $col_word . ' LIKE \'%', $where_any) . '%\' )' : '';
+		$col_word = ( $case_sensitive ) ? 'word' : 'word_lcase';
+		$where_any_str = ( count($where_any) > 0 ) ? '( ' . $col_word . ' LIKE \'%' . implode('%\' OR ' . $col_word . ' LIKE \'%', $where_any) . '%\' )' : '';
 
-    // generate query
-    $sql = "SELECT word, page_names FROM " . table_prefix . "search_index WHERE {$where_any_str}";
-    if ( !($q = $db->sql_query($sql)) )
-      $db->_die('Error is in perform_search(), includes/search.php, query 1');
+		// generate query
+		$sql = "SELECT word, page_names FROM " . table_prefix . "search_index WHERE {$where_any_str}";
+		if ( !($q = $db->sql_query($sql)) )
+			$db->_die('Error is in perform_search(), includes/search.php, query 1');
 
-    $word_tracking = array();
-    if ( $row = $db->fetchrow($q) )
-    {
-      do
-      {
-        // get page list
-        $pages =& $row['page_names'];
-          
-        // Find page IDs that contain commas
-        // This should never happen because commas are escaped by sanitize_page_id(). Nevertheless for compatibility with older
-        // databases, and to alleviate the concerns of hackers, we'll accommodate for page IDs with commas here by checking for
-        // IDs that don't match the pattern for stringified page ID + namespace. If it doesn't match, that means it's a continuation
-        // of the previous ID and should be concatenated to the previous entry.
-        $matches = strpos($pages, ',') ? explode(',', $pages) : array($pages);
-        $prev = false;
-        foreach ( $matches as $i => $_ )
-        {
-          $match =& $matches[$i];
-          if ( !preg_match("/^ns=$ns_list;pid=(.+)$/", $match) && $prev )
-          {
-            $matches[$prev] .= ',' . $match;
-            unset($match, $matches[$i]);
-            continue;
-          }
-          $prev = $i;
-        }
-        unset($match);
+		$word_tracking = array();
+		if ( $row = $db->fetchrow($q) )
+		{
+			do
+			{
+				// get page list
+				$pages =& $row['page_names'];
+					
+				// Find page IDs that contain commas
+				// This should never happen because commas are escaped by sanitize_page_id(). Nevertheless for compatibility with older
+				// databases, and to alleviate the concerns of hackers, we'll accommodate for page IDs with commas here by checking for
+				// IDs that don't match the pattern for stringified page ID + namespace. If it doesn't match, that means it's a continuation
+				// of the previous ID and should be concatenated to the previous entry.
+				$matches = strpos($pages, ',') ? explode(',', $pages) : array($pages);
+				$prev = false;
+				foreach ( $matches as $i => $_ )
+				{
+					$match =& $matches[$i];
+					if ( !preg_match("/^ns=$ns_list;pid=(.+)$/", $match) && $prev )
+					{
+						$matches[$prev] .= ',' . $match;
+						unset($match, $matches[$i]);
+						continue;
+					}
+					$prev = $i;
+				}
+				unset($match);
 
-        // Iterate through each of the results, assigning scores based on how many times the page has shown up.
-        // This works because this phase of the search is strongly word-based not page-based. If a page shows up
-        // multiple times while fetching the result rows from the search_index table, it simply means that page
-        // contains more than one of the terms the user searched for.
+				// Iterate through each of the results, assigning scores based on how many times the page has shown up.
+				// This works because this phase of the search is strongly word-based not page-based. If a page shows up
+				// multiple times while fetching the result rows from the search_index table, it simply means that page
+				// contains more than one of the terms the user searched for.
 
-        foreach ( $matches as $match )
-        {
-          $word_cs = (( $case_sensitive ) ? $row['word'] : strtolower($row['word']));
-          if ( isset($word_tracking[$match]) && in_array($word_cs, $word_tracking[$match]) )
-          {
-            continue;
-          }
-          if ( isset($word_tracking[$match]) )
-          {
-            if ( isset($word_tracking[$match]) )
-            {
-              $word_tracking[$match][] = $word_cs;
-            }
-          }
-          else
-          {
-            $word_tracking[$match] = array($word_cs);
-          }
-          
-          // echo '<pre>' . print_r($word_tracking, true) . '</pre>';
-          
-          $inc = 1;
+				foreach ( $matches as $match )
+				{
+					$word_cs = (( $case_sensitive ) ? $row['word'] : strtolower($row['word']));
+					if ( isset($word_tracking[$match]) && in_array($word_cs, $word_tracking[$match]) )
+					{
+						continue;
+					}
+					if ( isset($word_tracking[$match]) )
+					{
+						if ( isset($word_tracking[$match]) )
+						{
+							$word_tracking[$match][] = $word_cs;
+						}
+					}
+					else
+					{
+						$word_tracking[$match] = array($word_cs);
+					}
+					
+					// echo '<pre>' . print_r($word_tracking, true) . '</pre>';
+					
+					$inc = 1;
 
-          // Is this search term present in the page's title? If so, give extra points
-          preg_match("/^ns=$ns_list;pid=(.+)$/", $match, $piecesparts);
-          $title = get_page_title_ns($piecesparts[2], $piecesparts[1]);
-          
-          $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr';
-          if ( $test_func($title, $row['word']) || $test_func($piecesparts[2], $row['word']) )
-          {
-            $inc = 1.5;
-          }
-          
-          // increase points if 2 or more words match a phrase in the title
-          for ( $i = 0; $i < count($where_any) - 1; $i++ )
-          {
-            $phrase = "{$where_any[$i]} {$where_any[$i + 1]}";
-            if ( $test_func($title, $phrase) )
-            {
-              $inc *= 1.25;
-            }
-          }
-          
-          // Deduct points if there are few similarities between the words
-          $lev_array = array();
-          foreach ( $where_any as $qword )
-          {
-            if ( strstr($word_cs, $qword) )
-              $lev_array[ $qword ] = levenshtein($qword, $word_cs);
-          }
-          if ( min($lev_array) > 3 )
-          {
-            $inc /= array_sum($lev_array) / count($lev_array);
-          }
-          
-          if ( isset($scores[$match]) )
-          {
-            $scores[$match] = $scores[$match] + $inc;
-          }
-          else
-          {
-            $scores[$match] = $inc;
-          }
-        }
-      }
-      while ( $row = $db->fetchrow($q) );
-    }
-    $db->free_result($q);
-    
-    //
-    // STAGE 2: FIRST ELIMINATION ROUND
-    // Iterate through the list of required terms. If a given page is not found to have the required term, eliminate it
-    //
+					// Is this search term present in the page's title? If so, give extra points
+					preg_match("/^ns=$ns_list;pid=(.+)$/", $match, $piecesparts);
+					$title = get_page_title_ns($piecesparts[2], $piecesparts[1]);
+					
+					$test_func = ( $case_sensitive ) ? 'strstr' : 'stristr';
+					if ( $test_func($title, $row['word']) || $test_func($piecesparts[2], $row['word']) )
+					{
+						$inc = 1.5;
+					}
+					
+					// increase points if 2 or more words match a phrase in the title
+					for ( $i = 0; $i < count($where_any) - 1; $i++ )
+					{
+						$phrase = "{$where_any[$i]} {$where_any[$i + 1]}";
+						if ( $test_func($title, $phrase) )
+						{
+							$inc *= 1.25;
+						}
+					}
+					
+					// Deduct points if there are few similarities between the words
+					$lev_array = array();
+					foreach ( $where_any as $qword )
+					{
+						if ( strstr($word_cs, $qword) )
+							$lev_array[ $qword ] = levenshtein($qword, $word_cs);
+					}
+					if ( min($lev_array) > 3 )
+					{
+						$inc /= array_sum($lev_array) / count($lev_array);
+					}
+					
+					if ( isset($scores[$match]) )
+					{
+						$scores[$match] = $scores[$match] + $inc;
+					}
+					else
+					{
+						$scores[$match] = $inc;
+					}
+				}
+			}
+			while ( $row = $db->fetchrow($q) );
+		}
+		$db->free_result($q);
+		
+		//
+		// STAGE 2: FIRST ELIMINATION ROUND
+		// Iterate through the list of required terms. If a given page is not found to have the required term, eliminate it
+		//
 
-    foreach ( $query['req'] as $term )
-    {
-      foreach ( $word_tracking as $i => $page )
-      {
-        if ( !in_array($term, $page) )
-        {
-          unset($word_tracking[$i], $scores[$i]);
-        }
-      }
-    }
-  }
-  
-  //
-  // STAGE 3: PHRASE SEARCHING
-  // Use LIKE to find pages with specified phrases. We can do a super-picky single query without another elimination round because
-  // at this stage we can search the full page_text column instead of relying on a word list.
-  //
+		foreach ( $query['req'] as $term )
+		{
+			foreach ( $word_tracking as $i => $page )
+			{
+				if ( !in_array($term, $page) )
+				{
+					unset($word_tracking[$i], $scores[$i]);
+				}
+			}
+		}
+	}
+	
+	//
+	// STAGE 3: PHRASE SEARCHING
+	// Use LIKE to find pages with specified phrases. We can do a super-picky single query without another elimination round because
+	// at this stage we can search the full page_text column instead of relying on a word list.
+	//
 
-  // We can skip this stage if none of these special terms apply
+	// We can skip this stage if none of these special terms apply
 
-  $text_col = ( $case_sensitive ) ? 'page_text' : ENANO_SQLFUNC_LOWERCASE . '(page_text)';
-  $name_col = ( $case_sensitive ) ? 'name' : ENANO_SQLFUNC_LOWERCASE . '(name)';
-  $text_col_join = ( $case_sensitive ) ? 't.page_text' : ENANO_SQLFUNC_LOWERCASE . '(t.page_text)';
-  $name_col_join = ( $case_sensitive ) ? 'p.name' : ENANO_SQLFUNC_LOWERCASE . '(p.name)';
-    
-  $concat_column = ( ENANO_DBLAYER == 'MYSQL' ) ?
-    'CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id)' :
-    "'ns=' || t.namespace || ';pid=' || t.page_id";
+	$text_col = ( $case_sensitive ) ? 'page_text' : ENANO_SQLFUNC_LOWERCASE . '(page_text)';
+	$name_col = ( $case_sensitive ) ? 'name' : ENANO_SQLFUNC_LOWERCASE . '(name)';
+	$text_col_join = ( $case_sensitive ) ? 't.page_text' : ENANO_SQLFUNC_LOWERCASE . '(t.page_text)';
+	$name_col_join = ( $case_sensitive ) ? 'p.name' : ENANO_SQLFUNC_LOWERCASE . '(p.name)';
+		
+	$concat_column = ( ENANO_DBLAYER == 'MYSQL' ) ?
+		'CONCAT(\'ns=\',t.namespace,\';pid=\',t.page_id)' :
+		"'ns=' || t.namespace || ';pid=' || t.page_id";
 
-  if ( count($query_phrase['any']) > 0 || count($query_phrase['req']) > 0 )
-  {
+	if ( count($query_phrase['any']) > 0 || count($query_phrase['req']) > 0 )
+	{
 
-    $where_any = array();
-    foreach ( $query_phrase['any'] as $term )
-    {
-      $term = escape_string_like($term);
-      if ( !$case_sensitive )
-        $term = strtolower($term);
-      $where_any[] = "( $text_col LIKE '%$term%' OR $name_col LIKE '%$term%' )";
-    }
+		$where_any = array();
+		foreach ( $query_phrase['any'] as $term )
+		{
+			$term = escape_string_like($term);
+			if ( !$case_sensitive )
+				$term = strtolower($term);
+			$where_any[] = "( $text_col LIKE '%$term%' OR $name_col LIKE '%$term%' )";
+		}
 
-    $where_any = ( count($where_any) > 0 ) ? implode(" OR\n  ", $where_any) : '';
+		$where_any = ( count($where_any) > 0 ) ? implode(" OR\n  ", $where_any) : '';
 
-    // Also do required terms, but use AND to ensure that all required terms are included
-    $where_req = array();
-    foreach ( $query_phrase['req'] as $term )
-    {
-      $term = escape_string_like($term);
-      if ( !$case_sensitive )
-        $term = strtolower($term);
-      $where_req[] = "( $text_col LIKE '%$term%' OR $name_col LIKE '%$term%' )";
-    }
-    $and_clause = ( $where_any != '' ) ? 'AND ' : '';
-    $where_req = ( count($where_req) > 0 ) ? "{$and_clause}" . implode(" AND\n  ", $where_req) : '';
+		// Also do required terms, but use AND to ensure that all required terms are included
+		$where_req = array();
+		foreach ( $query_phrase['req'] as $term )
+		{
+			$term = escape_string_like($term);
+			if ( !$case_sensitive )
+				$term = strtolower($term);
+			$where_req[] = "( $text_col LIKE '%$term%' OR $name_col LIKE '%$term%' )";
+		}
+		$and_clause = ( $where_any != '' ) ? 'AND ' : '';
+		$where_req = ( count($where_req) > 0 ) ? "{$and_clause}" . implode(" AND\n  ", $where_req) : '';
 
-    $sql = 'SELECT ' . $concat_column . ' AS id, p.name, t.page_text FROM ' . table_prefix . "page_text AS t\n"
-            . "  LEFT JOIN " . table_prefix . "pages AS p\n"
-            . "    ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
-            . "  WHERE p.visible = 1 AND (\n    $where_any\n    $where_req\n  );";
-    if ( !($q = $db->sql_query($sql)) )
-      $db->_die('Error is in perform_search(), includes/search.php, query 2. Parsed query dump follows:<pre>(indexable) ' . htmlspecialchars(print_r($query, true)) . '(non-indexable) ' . htmlspecialchars(print_r($query_phrase, true)) . '</pre>');
+		$sql = 'SELECT ' . $concat_column . ' AS id, p.name, t.page_text FROM ' . table_prefix . "page_text AS t\n"
+						. "  LEFT JOIN " . table_prefix . "pages AS p\n"
+						. "    ON ( p.urlname = t.page_id AND p.namespace = t.namespace )\n"
+						. "  WHERE p.visible = 1 AND (\n    $where_any\n    $where_req\n  );";
+		if ( !($q = $db->sql_query($sql)) )
+			$db->_die('Error is in perform_search(), includes/search.php, query 2. Parsed query dump follows:<pre>(indexable) ' . htmlspecialchars(print_r($query, true)) . '(non-indexable) ' . htmlspecialchars(print_r($query_phrase, true)) . '</pre>');
 
-    if ( $row = $db->fetchrow() )
-    {
-      do
-      {
-        $id =& $row['id'];
-        $inc = 0.0;
+		if ( $row = $db->fetchrow() )
+		{
+			do
+			{
+				$id =& $row['id'];
+				$inc = 0.0;
 
-        $title = $row['name'];
-        $test_func = ( $case_sensitive ) ? 'strstr' : 'stristr';
-        
-        // Is this search term present in the page's title? If so, give extra points
-        $word_list = array_merge($query_phrase['any'], $query_phrase['req']);
-        foreach ( $word_list as $word )
-        {
-          if ( $test_func($title, $word) )
-            $inc += 1.5;
-          else if ( $test_func($row['page_text'], $word) )
-            $inc += 1.0;
-        }
-        
-        // increase points if 2 or more words match a phrase in the title
-        for ( $i = 0; $i < count($word_list) - 1; $i++ )
-        {
-          $phrase = "{$word_list[$i]} {$word_list[$i + 1]}";
-          if ( $test_func($title, $phrase) )
-            $inc *= 1.25;
-          else if ( $test_func($row['page_text'], $phrase) )
-            $inc *= 1.125;
-        }
-        
-        if ( isset($scores[$id]) )
-        {
-          $scores[$id] = $scores[$id] + $inc;
-        }
-        else
-        {
-          $scores[$id] = $inc;
-        }
-      }
-      while ( $row = $db->fetchrow() );
-    }
-    $db->free_result();
-  }
+				$title = $row['name'];
+				$test_func = ( $case_sensitive ) ? 'strstr' : 'stristr';
+				
+				// Is this search term present in the page's title? If so, give extra points
+				$word_list = array_merge($query_phrase['any'], $query_phrase['req']);
+				foreach ( $word_list as $word )
+				{
+					if ( $test_func($title, $word) )
+						$inc += 1.5;
+					else if ( $test_func($row['page_text'], $word) )
+						$inc += 1.0;
+				}
+				
+				// increase points if 2 or more words match a phrase in the title
+				for ( $i = 0; $i < count($word_list) - 1; $i++ )
+				{
+					$phrase = "{$word_list[$i]} {$word_list[$i + 1]}";
+					if ( $test_func($title, $phrase) )
+						$inc *= 1.25;
+					else if ( $test_func($row['page_text'], $phrase) )
+						$inc *= 1.125;
+				}
+				
+				if ( isset($scores[$id]) )
+				{
+					$scores[$id] = $scores[$id] + $inc;
+				}
+				else
+				{
+					$scores[$id] = $inc;
+				}
+			}
+			while ( $row = $db->fetchrow() );
+		}
+		$db->free_result();
+	}
 
-  //
-  // STAGE 4 - SELECT PAGE TEXT AND ELIMINATE NOTS
-  // At this point, we have a complete list of all the possible pages. Now we want to obtain the page text, and within the same query
-  // eliminate any terms that shouldn't be in there.
-  //
+	//
+	// STAGE 4 - SELECT PAGE TEXT AND ELIMINATE NOTS
+	// At this point, we have a complete list of all the possible pages. Now we want to obtain the page text, and within the same query
+	// eliminate any terms that shouldn't be in there.
+	//
 
-  // Generate master word list for the highlighter
-  $word_list = array_values(array_merge($query['any'], $query['req'], $query_phrase['any'], $query_phrase['req']));
+	// Generate master word list for the highlighter
+	$word_list = array_values(array_merge($query['any'], $query['req'], $query_phrase['any'], $query_phrase['req']));
 
-  $text_where = array();
-  foreach ( $scores as $page_id => $_ )
-  {
-    $text_where[] = $db->escape($page_id);
-  }
-  $text_where = '( ' . $concat_column . ' = \'' . implode('\' OR ' . $concat_column . ' = \'', $text_where) . '\' )';
+	$text_where = array();
+	foreach ( $scores as $page_id => $_ )
+	{
+		$text_where[] = $db->escape($page_id);
+	}
+	$text_where = '( ' . $concat_column . ' = \'' . implode('\' OR ' . $concat_column . ' = \'', $text_where) . '\' )';
 
-  if ( count($query['not']) > 0 )
-    $text_where .= ' AND';
+	if ( count($query['not']) > 0 )
+		$text_where .= ' AND';
 
-  $where_not = array();
-  foreach ( $query['not'] as $term )
-  {
-    $term = escape_string_like($term);
-    if ( !$case_sensitive )
-      $term = strtolower($term);
-    $where_not[] = $term;
-  }
-  $where_not = ( count($where_not) > 0 ) ? "$text_col NOT LIKE '%" . implode("%' AND $text_col NOT LIKE '%", $where_not) . "%'" : '';
+	$where_not = array();
+	foreach ( $query['not'] as $term )
+	{
+		$term = escape_string_like($term);
+		if ( !$case_sensitive )
+			$term = strtolower($term);
+		$where_not[] = $term;
+	}
+	$where_not = ( count($where_not) > 0 ) ? "$text_col NOT LIKE '%" . implode("%' AND $text_col NOT LIKE '%", $where_not) . "%'" : '';
 
-  $sql = 'SELECT ' . $concat_column . ' AS id, t.page_id, t.namespace, CHAR_LENGTH(t.page_text) AS page_length, t.page_text, p.name AS page_name FROM ' . table_prefix . "page_text AS t
-            LEFT JOIN " . table_prefix . "pages AS p
-              ON ( p.urlname = t.page_id AND p.namespace = t.namespace )
-            WHERE p.visible = 1 AND ( $text_where $where_not );";
-  if ( !($q = $db->sql_unbuffered_query($sql)) )
-    $db->_die('Error is in perform_search(), includes/search.php, query 3');
+	$sql = 'SELECT ' . $concat_column . ' AS id, t.page_id, t.namespace, CHAR_LENGTH(t.page_text) AS page_length, t.page_text, p.name AS page_name FROM ' . table_prefix . "page_text AS t
+						LEFT JOIN " . table_prefix . "pages AS p
+							ON ( p.urlname = t.page_id AND p.namespace = t.namespace )
+						WHERE p.visible = 1 AND ( $text_where $where_not );";
+	if ( !($q = $db->sql_unbuffered_query($sql)) )
+		$db->_die('Error is in perform_search(), includes/search.php, query 3');
 
-  $page_data = array();
-  if ( $row = $db->fetchrow() )
-  {
-    do
-    {
-      $row['page_text'] = htmlspecialchars($row['page_text']);
-      $row['page_name'] = htmlspecialchars($row['page_name']);
+	$page_data = array();
+	if ( $row = $db->fetchrow() )
+	{
+		do
+		{
+			$row['page_text'] = htmlspecialchars($row['page_text']);
+			$row['page_name'] = htmlspecialchars($row['page_name']);
 
-      // Highlight results (this is wonderfully automated)
-      $row['page_text'] = highlight_and_clip_search_result($row['page_text'], $word_list, $case_sensitive);
-      if ( strlen($row['page_text']) > 250 && !preg_match('/^\.\.\.(.+)\.\.\.$/', $row['page_text']) )
-      {
-        $row['page_text'] = substr($row['page_text'], 0, 150) . '...';
-      }
-      $row['page_name'] = highlight_search_result($row['page_name'], $word_list, $case_sensitive);
+			// Highlight results (this is wonderfully automated)
+			$row['page_text'] = highlight_and_clip_search_result($row['page_text'], $word_list, $case_sensitive);
+			if ( strlen($row['page_text']) > 250 && !preg_match('/^\.\.\.(.+)\.\.\.$/', $row['page_text']) )
+			{
+				$row['page_text'] = substr($row['page_text'], 0, 150) . '...';
+			}
+			$row['page_name'] = highlight_search_result($row['page_name'], $word_list, $case_sensitive);
 
-      $page_data[$row['id']] = $row;
-    }
-    while ( $row = $db->fetchrow() );
-  }
-  $db->free_result();
-  
-  //
-  // STAGE 5 - SPECIAL PAGE TITLE SEARCH
-  // Iterate through $paths->pages and check the titles for search terms. Score accordingly.
-  //
+			$page_data[$row['id']] = $row;
+		}
+		while ( $row = $db->fetchrow() );
+	}
+	$db->free_result();
+	
+	//
+	// STAGE 5 - SPECIAL PAGE TITLE SEARCH
+	// Iterate through $paths->pages and check the titles for search terms. Score accordingly.
+	//
 
-  foreach ( $paths->pages as $id => $page )
-  {
-    if ( $page['namespace'] != 'Special' || $page['visible'] == 0 )
-      continue;
-    $idstring = 'ns=' . $page['namespace'] . ';pid=' . $page['urlname_nons'];
-    $any = array_values(array_unique(array_merge($query['any'], $query_phrase['any'])));
-    foreach ( $any as $term )
-    {
-      if ( $case_sensitive )
-      {
-        if ( strstr($page['name'], $term) || strstr($page['urlname_nons'], $term) )
-        {
-          ( isset($scores[$idstring]) ) ? $scores[$idstring] = $scores[$idstring] + 1.5 : $scores[$idstring] = 1.5;
-        }
-      }
-      else
-      {
-        if ( stristr($page['name'], $term) || stristr($page['urlname_nons'], $term) )
-        {
-          ( isset($scores[$idstring]) ) ? $scores[$idstring] = $scores[$idstring] + 1.5 : $scores[$idstring] = 1.5;
-        }
-      }
-    }
-    if ( isset($scores[$idstring]) )
-    {
-      $page_data[$idstring] = array(
-          'page_name' => highlight_search_result($page['name'], $word_list, $case_sensitive),
-          'page_text' => '',
-          'page_id' => $page['urlname_nons'],
-          'namespace' => $page['namespace'],
-          'score' => $scores[$idstring],
-          'page_length' => 1,
-          'page_note' => '[' . $lang->get('search_result_tag_special') . ']'
-        );
-    }
-  }
-  
-  //
-  // STAGE 6 - SECOND ELIMINATION ROUND
-  // Iterate through the list of required terms. If a given page is not found to have the required term, eliminate it
-  //
+	foreach ( $paths->pages as $id => $page )
+	{
+		if ( $page['namespace'] != 'Special' || $page['visible'] == 0 )
+			continue;
+		$idstring = 'ns=' . $page['namespace'] . ';pid=' . $page['urlname_nons'];
+		$any = array_values(array_unique(array_merge($query['any'], $query_phrase['any'])));
+		foreach ( $any as $term )
+		{
+			if ( $case_sensitive )
+			{
+				if ( strstr($page['name'], $term) || strstr($page['urlname_nons'], $term) )
+				{
+					( isset($scores[$idstring]) ) ? $scores[$idstring] = $scores[$idstring] + 1.5 : $scores[$idstring] = 1.5;
+				}
+			}
+			else
+			{
+				if ( stristr($page['name'], $term) || stristr($page['urlname_nons'], $term) )
+				{
+					( isset($scores[$idstring]) ) ? $scores[$idstring] = $scores[$idstring] + 1.5 : $scores[$idstring] = 1.5;
+				}
+			}
+		}
+		if ( isset($scores[$idstring]) )
+		{
+			$page_data[$idstring] = array(
+					'page_name' => highlight_search_result($page['name'], $word_list, $case_sensitive),
+					'page_text' => '',
+					'page_id' => $page['urlname_nons'],
+					'namespace' => $page['namespace'],
+					'score' => $scores[$idstring],
+					'page_length' => 1,
+					'page_note' => '[' . $lang->get('search_result_tag_special') . ']'
+				);
+		}
+	}
+	
+	//
+	// STAGE 6 - SECOND ELIMINATION ROUND
+	// Iterate through the list of required terms. If a given page is not found to have the required term, eliminate it
+	//
 
-  $required = array_merge($query['req'], $query_phrase['req']);
-  foreach ( $required as $term )
-  {
-    foreach ( $page_data as $id => $page )
-    {
-      if ( ( $page['namespace'] == 'Special' || ( $page['namespace'] != 'Special' && !strstr($page['page_text'], $term) ) ) && !strstr($page['page_id'], $term) && !strstr($page['page_name'], $term) )
-      {
-        unset($page_data[$id]);
-      }
-    }
-  }
+	$required = array_merge($query['req'], $query_phrase['req']);
+	foreach ( $required as $term )
+	{
+		foreach ( $page_data as $id => $page )
+		{
+			if ( ( $page['namespace'] == 'Special' || ( $page['namespace'] != 'Special' && !strstr($page['page_text'], $term) ) ) && !strstr($page['page_id'], $term) && !strstr($page['page_name'], $term) )
+			{
+				unset($page_data[$id]);
+			}
+		}
+	}
 
-  // At this point, all of our normal results are in. However, we can also allow plugins to hook into the system and score their own
-  // pages and add text, etc. as necessary.
-  // Plugins are COMPLETELY responsible for using the search terms and handling Boolean logic properly
+	// At this point, all of our normal results are in. However, we can also allow plugins to hook into the system and score their own
+	// pages and add text, etc. as necessary.
+	// Plugins are COMPLETELY responsible for using the search terms and handling Boolean logic properly
 
-  inject_custom_search_results($query, $query_phrase, $scores, $page_data, $case_sensitive, $word_list);
-  
-  $code = $plugins->setHook('search_global_inner');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
+	inject_custom_search_results($query, $query_phrase, $scores, $page_data, $case_sensitive, $word_list);
+	
+	$code = $plugins->setHook('search_global_inner');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
 
-  // a marvelous debugging aid :-)
-  // die('<pre>' . htmlspecialchars(print_r($page_data, true)) . '</pre>');
+	// a marvelous debugging aid :-)
+	// die('<pre>' . htmlspecialchars(print_r($page_data, true)) . '</pre>');
 
-  //
-  // STAGE 7 - HIGHLIGHT, TRIM, AND SCORE RESULTS
-  // We now have the complete results of the search. We need to trim text down to show only portions of the page containing search
-  // terms, highlight any search terms within the page, and sort the final results array in descending order of score.
-  //
+	//
+	// STAGE 7 - HIGHLIGHT, TRIM, AND SCORE RESULTS
+	// We now have the complete results of the search. We need to trim text down to show only portions of the page containing search
+	// terms, highlight any search terms within the page, and sort the final results array in descending order of score.
+	//
 
-  // Sort scores array
-  arsort($scores);
+	// Sort scores array
+	arsort($scores);
 
-  // Divisor for calculating relevance scores
-  $divisor = ( count($query['any']) + count($query_phrase['any']) + count($query['req']) + count($query['not']) ) * 1.5;
-  $divisor = max($divisor, max($scores));
-  
-  foreach ( $scores as $page_id => $score )
-  {
-    if ( !isset($page_data[$page_id]) )
-      // It's possible that $scores contains a score for a page that was later eliminated because it contained a disallowed term
-      continue;
+	// Divisor for calculating relevance scores
+	$divisor = ( count($query['any']) + count($query_phrase['any']) + count($query['req']) + count($query['not']) ) * 1.5;
+	$divisor = max($divisor, max($scores));
+	
+	foreach ( $scores as $page_id => $score )
+	{
+		if ( !isset($page_data[$page_id]) )
+			// It's possible that $scores contains a score for a page that was later eliminated because it contained a disallowed term
+			continue;
 
-    // Make a copy of the datum, then delete the original (it frees up a LOT of RAM)
-    $datum = $page_data[$page_id];
-    unset($page_data[$page_id]);
+		// Make a copy of the datum, then delete the original (it frees up a LOT of RAM)
+		$datum = $page_data[$page_id];
+		unset($page_data[$page_id]);
 
-    // This is an internal value used for sorting - it's no longer needed.
-    unset($datum['id']);
+		// This is an internal value used for sorting - it's no longer needed.
+		unset($datum['id']);
 
-    // Calculate score
-    // if ( $score > $divisor )
-    //   $score = $divisor;
-    $datum['score'] = round($score / $divisor, 2) * 100;
-    
-    // Highlight the URL
-    $datum['url_highlight'] = makeUrlComplete($datum['namespace'], $datum['page_id']);
-    $datum['url_highlight'] = preg_replace('/\?.+$/', '', $datum['url_highlight']);
-    $datum['url_highlight'] = highlight_search_result($datum['url_highlight'], $word_list, $case_sensitive);
+		// Calculate score
+		// if ( $score > $divisor )
+		//   $score = $divisor;
+		$datum['score'] = round($score / $divisor, 2) * 100;
+		
+		// Highlight the URL
+		$datum['url_highlight'] = makeUrlComplete($datum['namespace'], $datum['page_id']);
+		$datum['url_highlight'] = preg_replace('/\?.+$/', '', $datum['url_highlight']);
+		$datum['url_highlight'] = highlight_search_result($datum['url_highlight'], $word_list, $case_sensitive);
 
-    // Store it in our until-now-unused results array
-    $results[] = $datum;
-  }
+		// Store it in our until-now-unused results array
+		$results[] = $datum;
+	}
 
-  // Our work here is done. :-D
-  return $results;
+	// Our work here is done. :-D
+	return $results;
 }
 
 /**
@@ -594,166 +594,166 @@
 
 function parse_search_query($query, &$warnings)
 {
-  global $lang;
-  
-  $stopwords = get_stopwords();
-  $ret = array(
-    'any' => array(),
-    'req' => array(),
-    'not' => array()
-    );
-  $warnings = array();
-  $terms = array();
-  $in_quote = false;
-  $start_term = 0;
-  $just_finished = false;
-  for ( $i = 0; $i < strlen($query); $i++ )
-  {
-    $chr = $query{$i};
-    $prev = ( $i > 0 ) ? $query{ $i - 1 } : '';
-    $next = ( ( $i + 1 ) < strlen($query) ) ? $query{ $i + 1 } : '';
+	global $lang;
+	
+	$stopwords = get_stopwords();
+	$ret = array(
+		'any' => array(),
+		'req' => array(),
+		'not' => array()
+		);
+	$warnings = array();
+	$terms = array();
+	$in_quote = false;
+	$start_term = 0;
+	$just_finished = false;
+	for ( $i = 0; $i < strlen($query); $i++ )
+	{
+		$chr = $query{$i};
+		$prev = ( $i > 0 ) ? $query{ $i - 1 } : '';
+		$next = ( ( $i + 1 ) < strlen($query) ) ? $query{ $i + 1 } : '';
 
-    if ( ( $chr == ' ' && !$in_quote ) || ( $i + 1 == strlen ( $query ) ) )
-    {
-      $len = ( $next == '' ) ? $i + 1 : $i - $start_term;
-      $word = substr ( $query, $start_term, $len );
-      $terms[] = $word;
-      $start_term = $i + 1;
-    }
+		if ( ( $chr == ' ' && !$in_quote ) || ( $i + 1 == strlen ( $query ) ) )
+		{
+			$len = ( $next == '' ) ? $i + 1 : $i - $start_term;
+			$word = substr ( $query, $start_term, $len );
+			$terms[] = $word;
+			$start_term = $i + 1;
+		}
 
-    elseif ( $chr == '"' && $in_quote && $prev != '\\' )
-    {
-      $word = substr ( $query, $start_term, $i - $start_term + 1 );
-      $start_pos = ( $next == ' ' ) ? $i + 2 : $i + 1;
-      $in_quote = false;
-    }
+		elseif ( $chr == '"' && $in_quote && $prev != '\\' )
+		{
+			$word = substr ( $query, $start_term, $i - $start_term + 1 );
+			$start_pos = ( $next == ' ' ) ? $i + 2 : $i + 1;
+			$in_quote = false;
+		}
 
-    elseif ( $chr == '"' && !$in_quote )
-    {
-      $in_quote = true;
-      $start_pos = $i;
-    }
+		elseif ( $chr == '"' && !$in_quote )
+		{
+			$in_quote = true;
+			$start_pos = $i;
+		}
 
-  }
+	}
 
-  $ticker = 0;
+	$ticker = 0;
 
-  foreach ( $terms as $element => $__unused )
-  {
-    $atom =& $terms[$element];
+	foreach ( $terms as $element => $__unused )
+	{
+		$atom =& $terms[$element];
 
-    $ticker++;
+		$ticker++;
 
-    if ( $ticker == 20 )
-    {
-      $warnings[] = $lang->get('search_err_query_too_many_terms');
-      break;
-    }
+		if ( $ticker == 20 )
+		{
+			$warnings[] = $lang->get('search_err_query_too_many_terms');
+			break;
+		}
 
-    if ( substr ( $atom, 0, 2 ) == '+"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' )
-    {
-      $word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) );
-      if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
-      {
-        $warnings[] = $lang->get('search_err_query_has_stopwords');
-        $ticker--;
-        continue;
-      }
-      if(in_array($word, $ret['req']))
-      {
-        $warnings[] = $lang->get('search_err_query_dup_terms');
-        $ticker--;
-        continue;
-      }
-      $ret['req'][] = $word;
-    }
-    elseif ( substr ( $atom, 0, 2 ) == '-"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' )
-    {
-      $word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) );
-      if ( strlen ( $word ) < 4 )
-      {
-        $warnings[] = $lang->get('search_err_query_term_too_short');
-        $ticker--;
-        continue;
-      }
-      if(in_array($word, $ret['not']))
-      {
-        $warnings[] = $lang->get('search_err_query_dup_terms');
-        $ticker--;
-        continue;
-      }
-      $ret['not'][] = $word;
-    }
-    elseif ( substr ( $atom, 0, 1 ) == '+' )
-    {
-      $word = substr ( $atom, 1 );
-      if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
-      {
-        $warnings[] = $lang->get('search_err_query_has_stopwords');
-        $ticker--;
-        continue;
-      }
-      if(in_array($word, $ret['req']))
-      {
-        $warnings[] = $lang->get('search_err_query_dup_terms');
-        $ticker--;
-        continue;
-      }
-      $ret['req'][] = $word;
-    }
-    elseif ( substr ( $atom, 0, 1 ) == '-' )
-    {
-      $word = substr ( $atom, 1 );
-      if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
-      {
-        $warnings[] = $lang->get('search_err_query_has_stopwords');
-        $ticker--;
-        continue;
-      }
-      if(in_array($word, $ret['not']))
-      {
-        $warnings[] = $lang->get('search_err_query_dup_terms');
-        $ticker--;
-        continue;
-      }
-      $ret['not'][] = $word;
-    }
-    elseif ( substr ( $atom, 0, 1 ) == '"' && substr ( $atom, ( strlen($atom) - 1 ), 1 ) == '"' )
-    {
-      $word = substr ( $atom, 1, ( strlen ( $atom ) - 2 ) );
-      if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
-      {
-        $warnings[] = $lang->get('search_err_query_has_stopwords');
-        $ticker--;
-        continue;
-      }
-      if(in_array($word, $ret['any']))
-      {
-        $warnings[] = $lang->get('search_err_query_dup_terms');
-        $ticker--;
-        continue;
-      }
-      $ret['any'][] = $word;
-    }
-    else
-    {
-      $word = $atom;
-      if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
-      {
-        $warnings[] = $lang->get('search_err_query_has_stopwords');
-        $ticker--;
-        continue;
-      }
-      if(in_array($word, $ret['any']))
-      {
-        $warnings[] = $lang->get('search_err_query_dup_terms');
-        $ticker--;
-        continue;
-      }
-      $ret['any'][] = $word;
-    }
-  }
-  return $ret;
+		if ( substr ( $atom, 0, 2 ) == '+"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' )
+		{
+			$word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) );
+			if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
+			{
+				$warnings[] = $lang->get('search_err_query_has_stopwords');
+				$ticker--;
+				continue;
+			}
+			if(in_array($word, $ret['req']))
+			{
+				$warnings[] = $lang->get('search_err_query_dup_terms');
+				$ticker--;
+				continue;
+			}
+			$ret['req'][] = $word;
+		}
+		elseif ( substr ( $atom, 0, 2 ) == '-"' && substr ( $atom, ( strlen ( $atom ) - 1 ), 1 ) == '"' )
+		{
+			$word = substr ( $atom, 2, ( strlen( $atom ) - 3 ) );
+			if ( strlen ( $word ) < 4 )
+			{
+				$warnings[] = $lang->get('search_err_query_term_too_short');
+				$ticker--;
+				continue;
+			}
+			if(in_array($word, $ret['not']))
+			{
+				$warnings[] = $lang->get('search_err_query_dup_terms');
+				$ticker--;
+				continue;
+			}
+			$ret['not'][] = $word;
+		}
+		elseif ( substr ( $atom, 0, 1 ) == '+' )
+		{
+			$word = substr ( $atom, 1 );
+			if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
+			{
+				$warnings[] = $lang->get('search_err_query_has_stopwords');
+				$ticker--;
+				continue;
+			}
+			if(in_array($word, $ret['req']))
+			{
+				$warnings[] = $lang->get('search_err_query_dup_terms');
+				$ticker--;
+				continue;
+			}
+			$ret['req'][] = $word;
+		}
+		elseif ( substr ( $atom, 0, 1 ) == '-' )
+		{
+			$word = substr ( $atom, 1 );
+			if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
+			{
+				$warnings[] = $lang->get('search_err_query_has_stopwords');
+				$ticker--;
+				continue;
+			}
+			if(in_array($word, $ret['not']))
+			{
+				$warnings[] = $lang->get('search_err_query_dup_terms');
+				$ticker--;
+				continue;
+			}
+			$ret['not'][] = $word;
+		}
+		elseif ( substr ( $atom, 0, 1 ) == '"' && substr ( $atom, ( strlen($atom) - 1 ), 1 ) == '"' )
+		{
+			$word = substr ( $atom, 1, ( strlen ( $atom ) - 2 ) );
+			if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
+			{
+				$warnings[] = $lang->get('search_err_query_has_stopwords');
+				$ticker--;
+				continue;
+			}
+			if(in_array($word, $ret['any']))
+			{
+				$warnings[] = $lang->get('search_err_query_dup_terms');
+				$ticker--;
+				continue;
+			}
+			$ret['any'][] = $word;
+		}
+		else
+		{
+			$word = $atom;
+			if ( strlen ( $word ) < 2 || in_array($word, $stopwords) )
+			{
+				$warnings[] = $lang->get('search_err_query_has_stopwords');
+				$ticker--;
+				continue;
+			}
+			if(in_array($word, $ret['any']))
+			{
+				$warnings[] = $lang->get('search_err_query_dup_terms');
+				$ticker--;
+				continue;
+			}
+			$ret['any'][] = $word;
+		}
+	}
+	return $ret;
 }
 
 /**
@@ -764,10 +764,10 @@
 
 function escape_string_like($string)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  $string = $db->escape($string);
-  $string = str_replace(array('%', '_'), array('\%', '\_'), $string);
-  return $string;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	$string = $db->escape($string);
+	$string = str_replace(array('%', '_'), array('\%', '\_'), $string);
+	return $string;
 }
 
 /**
@@ -780,18 +780,18 @@
 
 function highlight_search_result($pt, $words, $case_sensitive = false)
 {
-  $words2 = array();
-  for ( $i = 0; $i < sizeof($words); $i++)
-  {
-    if(!empty($words[$i]))
-      $words2[] = preg_quote($words[$i]);
-  }
+	$words2 = array();
+	for ( $i = 0; $i < sizeof($words); $i++)
+	{
+		if(!empty($words[$i]))
+			$words2[] = preg_quote($words[$i]);
+	}
 
-  $flag = ( $case_sensitive ) ? '' : 'i';
-  $regex = '/(' . implode('|', str_replace('/', '\\/', $words2)) . ')/' . $flag;
-  $pt = preg_replace($regex, '<highlight>\\1</highlight>', $pt);
+	$flag = ( $case_sensitive ) ? '' : 'i';
+	$regex = '/(' . implode('|', str_replace('/', '\\/', $words2)) . ')/' . $flag;
+	$pt = preg_replace($regex, '<highlight>\\1</highlight>', $pt);
 
-  return $pt;
+	return $pt;
 }
 
 /**
@@ -805,95 +805,95 @@
 
 function highlight_and_clip_search_result($pt, $words, $case_sensitive = false)
 {
-  $cut_off = false;
+	$cut_off = false;
 
-  $space_chars = Array("\t", "\n", "\r", " ");
+	$space_chars = Array("\t", "\n", "\r", " ");
 
-  $pt = highlight_search_result($pt, $words, $case_sensitive);
+	$pt = highlight_search_result($pt, $words, $case_sensitive);
 
-  foreach ( $words as $word )
-  {
-    // Boldface searched words
-    $ptlen = strlen($pt);
-    for ( $i = 0; $i < $ptlen; $i++ )
-    {
-      $len = strlen($word);
-      if ( strtolower(substr($pt, $i, $len)) == strtolower($word) )
-      {
-        $chunk1 = substr($pt, 0, $i);
-        $chunk2 = substr($pt, $i, $len);
-        $chunk3 = substr($pt, ( $i + $len ));
-        $pt = $chunk1 . $chunk2 . $chunk3;
-        $ptlen = strlen($pt);
-        // Cut off text to 150 chars or so
-        if ( !$cut_off )
-        {
-          $cut_off = true;
-          if ( $i - 75 > 0 )
-          {
-            // Navigate backwards until a space character is found
-            $chunk = substr($pt, 0, ( $i - 75 ));
-            $final_chunk = $chunk;
-            for ( $j = strlen($chunk) - 1; $j > 0; $j = $j - 1 )
-            {
-              if ( in_array($chunk{$j}, $space_chars) )
-              {
-                $final_chunk = substr($chunk, $j + 1);
-                break;
-              }
-            }
-            $mid_chunk = substr($pt, ( $i - 75 ), 75);
+	foreach ( $words as $word )
+	{
+		// Boldface searched words
+		$ptlen = strlen($pt);
+		for ( $i = 0; $i < $ptlen; $i++ )
+		{
+			$len = strlen($word);
+			if ( strtolower(substr($pt, $i, $len)) == strtolower($word) )
+			{
+				$chunk1 = substr($pt, 0, $i);
+				$chunk2 = substr($pt, $i, $len);
+				$chunk3 = substr($pt, ( $i + $len ));
+				$pt = $chunk1 . $chunk2 . $chunk3;
+				$ptlen = strlen($pt);
+				// Cut off text to 150 chars or so
+				if ( !$cut_off )
+				{
+					$cut_off = true;
+					if ( $i - 75 > 0 )
+					{
+						// Navigate backwards until a space character is found
+						$chunk = substr($pt, 0, ( $i - 75 ));
+						$final_chunk = $chunk;
+						for ( $j = strlen($chunk) - 1; $j > 0; $j = $j - 1 )
+						{
+							if ( in_array($chunk{$j}, $space_chars) )
+							{
+								$final_chunk = substr($chunk, $j + 1);
+								break;
+							}
+						}
+						$mid_chunk = substr($pt, ( $i - 75 ), 75);
 
-            $clipped = '...' . $final_chunk . $mid_chunk . $chunk2;
+						$clipped = '...' . $final_chunk . $mid_chunk . $chunk2;
 
-            $chunk = substr($pt, ( $i + strlen($chunk2) + 75 ));
-            $final_chunk = $chunk;
-            for ( $j = 0; $j < strlen($chunk); $j++ )
-            {
-              if ( in_array($chunk{$j}, $space_chars) )
-              {
-                $final_chunk = substr($chunk, 0, $j);
-                break;
-              }
-            }
+						$chunk = substr($pt, ( $i + strlen($chunk2) + 75 ));
+						$final_chunk = $chunk;
+						for ( $j = 0; $j < strlen($chunk); $j++ )
+						{
+							if ( in_array($chunk{$j}, $space_chars) )
+							{
+								$final_chunk = substr($chunk, 0, $j);
+								break;
+							}
+						}
 
-            $end_chunk = substr($pt, ( $i + strlen($chunk2) ), 75 );
+						$end_chunk = substr($pt, ( $i + strlen($chunk2) ), 75 );
 
-            $clipped .= $end_chunk . $final_chunk . '...';
+						$clipped .= $end_chunk . $final_chunk . '...';
 
-            $pt = $clipped;
-          }
-          else if ( strlen($pt) > 200 )
-          {
-            $mid_chunk = substr($pt, ( $i - 75 ), 75);
+						$pt = $clipped;
+					}
+					else if ( strlen($pt) > 200 )
+					{
+						$mid_chunk = substr($pt, ( $i - 75 ), 75);
 
-            $clipped = $chunk1 . $chunk2;
+						$clipped = $chunk1 . $chunk2;
 
-            $chunk = substr($pt, ( $i + strlen($chunk2) + 75 ));
-            $final_chunk = $chunk;
-            for ( $j = 0; $j < strlen($chunk); $j++ )
-            {
-              if ( in_array($chunk{$j}, $space_chars) )
-              {
-                $final_chunk = substr($chunk, 0, $j);
-                break;
-              }
-            }
+						$chunk = substr($pt, ( $i + strlen($chunk2) + 75 ));
+						$final_chunk = $chunk;
+						for ( $j = 0; $j < strlen($chunk); $j++ )
+						{
+							if ( in_array($chunk{$j}, $space_chars) )
+							{
+								$final_chunk = substr($chunk, 0, $j);
+								break;
+							}
+						}
 
-            $end_chunk = substr($pt, ( $i + strlen($chunk2) ), 75 );
+						$end_chunk = substr($pt, ( $i + strlen($chunk2) ), 75 );
 
-            $clipped .= $end_chunk . $final_chunk . '...';
+						$clipped .= $end_chunk . $final_chunk . '...';
 
-            $pt = $clipped;
+						$pt = $clipped;
 
-          }
-          break 2;
-        }
-      }
-    }
-    $cut_off = false;
-  }
-  return $pt;
+					}
+					break 2;
+				}
+			}
+		}
+		$cut_off = false;
+	}
+	return $pt;
 }
 
 /**
@@ -903,15 +903,15 @@
 
 function get_stopwords()
 {
-  static $stopwords;
-  if ( is_array($stopwords) )
-    return $stopwords;
+	static $stopwords;
+	if ( is_array($stopwords) )
+		return $stopwords;
 
-  $stopwords = array('I', 'a', 'about', 'an', 'are', 'as', 'at', 'be', 'by', 'com', 'de', 'en', 'for', 'from', 'how', 'in', 'is', 'it',
-                     'la', 'of', 'on', 'or', 'that', 'the', 'this', 'to', 'was', 'what', 'when', 'where', 'who', 'will', 'with', 'and',
-                     'the');
-  
-  return $stopwords;
+	$stopwords = array('I', 'a', 'about', 'an', 'are', 'as', 'at', 'be', 'by', 'com', 'de', 'en', 'for', 'from', 'how', 'in', 'is', 'it',
+ 										'la', 'of', 'on', 'or', 'that', 'the', 'this', 'to', 'was', 'what', 'when', 'where', 'who', 'will', 'with', 'and',
+ 										'the');
+	
+	return $stopwords;
 }
 
 /**
@@ -920,182 +920,182 @@
 
 function inject_custom_search_results(&$query, &$query_phrase, &$scores, &$page_data, &$case_sensitive, &$word_list)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  global $search_handlers;
-  
-  // global functions
-  $terms = array(
-      'any' => array_merge($query['any'], $query_phrase['any']),
-      'req' => array_merge($query['req'], $query_phrase['req']),
-      'not' => $query['not']
-    );
-  
-  foreach ( $search_handlers as &$options )
-  {
-    $where = array('any' => array(), 'req' => array(), 'not' => array());
-    $where_any =& $where['any'];
-    $where_req =& $where['req'];
-    $where_not =& $where['not'];
-    $title_col = ( $case_sensitive ) ? $options['titlecolumn'] : ENANO_SQLFUNC_LOWERCASE . '(' . $options['titlecolumn'] . ')';
-    if ( isset($options['datacolumn']) )
-      $desc_col = ( $case_sensitive ) ? $options['datacolumn'] : ENANO_SQLFUNC_LOWERCASE . '(' . $options['datacolumn'] . ')';
-    else
-      $desc_col = "''";
-    foreach ( $terms['any'] as $term )
-    {
-      $term = escape_string_like($term);
-      if ( !$case_sensitive )
-        $term = strtolower($term);
-      $where_any[] = "( $title_col LIKE '%{$term}%' OR $desc_col LIKE '%{$term}%' )";
-    }
-    foreach ( $terms['req'] as $term )
-    {
-      $term = escape_string_like($term);
-      if ( !$case_sensitive )
-        $term = strtolower($term);
-      $where_req[] = "( $title_col LIKE '%{$term}%' OR $desc_col LIKE '%{$term}%' )";
-    }
-    foreach ( $terms['not'] as $term )
-    {
-      $term = escape_string_like($term);
-      if ( !$case_sensitive )
-        $term = strtolower($term);
-      $where_not[] = "$title_col NOT LIKE '%{$term}%' AND $desc_col NOT LIKE '%{$term}%'";
-    }
-    if ( empty($where_any) )
-      unset($where_any, $where['any']);
-    if ( empty($where_req) )
-      unset($where_req, $where['req']);
-    if ( empty($where_not) )
-      unset($where_not, $where['not']);
-    
-    $where_any = '(' . implode(' OR ', $where_any) . '' . ( isset($where['req']) || isset($where['not']) ? ' OR 1 = 1' : '' ) . ')';
-    
-    if ( isset($where_req) )
-      $where_req = implode(' AND ', $where_req);
-    if ( isset($where_not) )
-    $where_not = implode( 'AND ', $where_not);
-    
-    $where = implode(' AND ', $where);
-    
-    $columns = $options['titlecolumn'];
-    if ( isset($options['datacolumn']) )
-      $columns .= ", {$options['datacolumn']}";
-    if ( isset($options['additionalcolumns']) )
-      $columns .= ', ' . implode(', ', $options['additionalcolumns']);
-    
-    $additionalwhere = ( isset($options['additionalwhere']) ) ? $options['additionalwhere'] : '';
-    
-    $sql = "SELECT $columns FROM " . table_prefix . "{$options['table']} WHERE ( $where ) $additionalwhere;";
-  
-    if ( !($q = $db->sql_unbuffered_query($sql)) )
-    {
-      $db->_die('Automatically generated search query');
-    }
-    
-    if ( $row = $db->fetchrow() )
-    {
-      do
-      {
-        $parser = $template->makeParserText($options['uniqueid']);
-        $parser->assign_vars($row);
-        $idstring = $parser->run();
-        
-        // Score this result
-        foreach ( $word_list as $term )
-        {
-          if ( $case_sensitive )
-          {
-            if ( strstr($row[$options['titlecolumn']], $term) )
-            {
-              ( isset($scores[$idstring]) ) ? $scores[$idstring] += 1.5 : $scores[$idstring] = 1.5;
-            }
-            else if ( isset($options['datacolumn']) && strstr($row[$options['datacolumn']], $term) )
-            {
-              ( isset($scores[$idstring]) ) ? $scores[$idstring]++ : $scores[$idstring] = 1;
-            }
-          }
-          else
-          {
-            if ( stristr($row[$options['titlecolumn']], $term) )
-            {
-              ( isset($scores[$idstring]) ) ? $scores[$idstring] += 1.5 : $scores[$idstring] = 1.5;
-            }
-            else if ( isset($options['datacolumn']) && stristr($row[$options['datacolumn']], $term) )
-            {
-              ( isset($scores[$idstring]) ) ? $scores[$idstring]++ : $scores[$idstring] = 1;
-            }
-          }
-        }
-        // Generate text...
-        $text = '';
-        if ( isset($options['datacolumn']) && !isset($options['formatcallback']) )
-        {
-          $text = highlight_and_clip_search_result(htmlspecialchars($row[$options['datacolumn']]), $word_list);
-        }
-        else if ( isset($options['formatcallback']) )
-        {
-          if ( is_callable($options['formatcallback']) )
-          {
-            $text = call_user_func($options['formatcallback'], $row, $word_list);
-          }
-          else
-          {
-            $parser = $template->makeParserText($options['formatcallback']);
-            $parser->assign_vars($row);
-            $text = $parser->run();
-          }
-        }
-        
-        // Inject result
-        
-        if ( isset($scores[$idstring]) )
-        {
-          $parser = $template->makeParserText($options['linkformat']['page_id']);
-          $parser->assign_vars($row);
-          $page_id = $parser->run();
-          
-          $parser = $template->makeParserText($options['linkformat']['namespace']);
-          $parser->assign_vars($row);
-          $namespace = $parser->run();
-          
-          $page_data[$idstring] = array(
-            'page_name' => highlight_search_result(htmlspecialchars($row[$options['titlecolumn']]), $word_list),
-            'page_text' => $text,
-            'score' => $scores[$idstring],
-            'page_id' => $page_id,
-            'namespace' => $namespace,
-          );
-          
-          // Any additional flags that need to be added to the result?
-          // The small usually-bracketed text to the left of the title
-          if ( isset($options['resultnote']) )
-          {
-            $page_data[$idstring]['page_note'] = $options['resultnote'];
-          }
-          // Should we include the length?
-          if ( isset($options['datacolumn']) )
-          {
-            $page_data[$idstring]['page_length'] = strlen($row[$options['datacolumn']]);
-          }
-          else
-          {
-            $page_data[$idstring]['page_length'] = 0;
-            $page_data[$idstring]['zero_length'] = true;
-          }
-          // Anything to append to result links?
-          if ( isset($options['linkformat']['append']) )
-          {
-            $page_data[$idstring]['url_append'] = $options['linkformat']['append'];
-          }
-        }
-      }
-      while ( $row = $db->fetchrow($q) );
-      $db->free_result($q);
-    }
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	global $search_handlers;
+	
+	// global functions
+	$terms = array(
+			'any' => array_merge($query['any'], $query_phrase['any']),
+			'req' => array_merge($query['req'], $query_phrase['req']),
+			'not' => $query['not']
+		);
+	
+	foreach ( $search_handlers as &$options )
+	{
+		$where = array('any' => array(), 'req' => array(), 'not' => array());
+		$where_any =& $where['any'];
+		$where_req =& $where['req'];
+		$where_not =& $where['not'];
+		$title_col = ( $case_sensitive ) ? $options['titlecolumn'] : ENANO_SQLFUNC_LOWERCASE . '(' . $options['titlecolumn'] . ')';
+		if ( isset($options['datacolumn']) )
+			$desc_col = ( $case_sensitive ) ? $options['datacolumn'] : ENANO_SQLFUNC_LOWERCASE . '(' . $options['datacolumn'] . ')';
+		else
+			$desc_col = "''";
+		foreach ( $terms['any'] as $term )
+		{
+			$term = escape_string_like($term);
+			if ( !$case_sensitive )
+				$term = strtolower($term);
+			$where_any[] = "( $title_col LIKE '%{$term}%' OR $desc_col LIKE '%{$term}%' )";
+		}
+		foreach ( $terms['req'] as $term )
+		{
+			$term = escape_string_like($term);
+			if ( !$case_sensitive )
+				$term = strtolower($term);
+			$where_req[] = "( $title_col LIKE '%{$term}%' OR $desc_col LIKE '%{$term}%' )";
+		}
+		foreach ( $terms['not'] as $term )
+		{
+			$term = escape_string_like($term);
+			if ( !$case_sensitive )
+				$term = strtolower($term);
+			$where_not[] = "$title_col NOT LIKE '%{$term}%' AND $desc_col NOT LIKE '%{$term}%'";
+		}
+		if ( empty($where_any) )
+			unset($where_any, $where['any']);
+		if ( empty($where_req) )
+			unset($where_req, $where['req']);
+		if ( empty($where_not) )
+			unset($where_not, $where['not']);
+		
+		$where_any = '(' . implode(' OR ', $where_any) . '' . ( isset($where['req']) || isset($where['not']) ? ' OR 1 = 1' : '' ) . ')';
+		
+		if ( isset($where_req) )
+			$where_req = implode(' AND ', $where_req);
+		if ( isset($where_not) )
+		$where_not = implode( 'AND ', $where_not);
+		
+		$where = implode(' AND ', $where);
+		
+		$columns = $options['titlecolumn'];
+		if ( isset($options['datacolumn']) )
+			$columns .= ", {$options['datacolumn']}";
+		if ( isset($options['additionalcolumns']) )
+			$columns .= ', ' . implode(', ', $options['additionalcolumns']);
+		
+		$additionalwhere = ( isset($options['additionalwhere']) ) ? $options['additionalwhere'] : '';
+		
+		$sql = "SELECT $columns FROM " . table_prefix . "{$options['table']} WHERE ( $where ) $additionalwhere;";
+	
+		if ( !($q = $db->sql_unbuffered_query($sql)) )
+		{
+			$db->_die('Automatically generated search query');
+		}
+		
+		if ( $row = $db->fetchrow() )
+		{
+			do
+			{
+				$parser = $template->makeParserText($options['uniqueid']);
+				$parser->assign_vars($row);
+				$idstring = $parser->run();
+				
+				// Score this result
+				foreach ( $word_list as $term )
+				{
+					if ( $case_sensitive )
+					{
+						if ( strstr($row[$options['titlecolumn']], $term) )
+						{
+							( isset($scores[$idstring]) ) ? $scores[$idstring] += 1.5 : $scores[$idstring] = 1.5;
+						}
+						else if ( isset($options['datacolumn']) && strstr($row[$options['datacolumn']], $term) )
+						{
+							( isset($scores[$idstring]) ) ? $scores[$idstring]++ : $scores[$idstring] = 1;
+						}
+					}
+					else
+					{
+						if ( stristr($row[$options['titlecolumn']], $term) )
+						{
+							( isset($scores[$idstring]) ) ? $scores[$idstring] += 1.5 : $scores[$idstring] = 1.5;
+						}
+						else if ( isset($options['datacolumn']) && stristr($row[$options['datacolumn']], $term) )
+						{
+							( isset($scores[$idstring]) ) ? $scores[$idstring]++ : $scores[$idstring] = 1;
+						}
+					}
+				}
+				// Generate text...
+				$text = '';
+				if ( isset($options['datacolumn']) && !isset($options['formatcallback']) )
+				{
+					$text = highlight_and_clip_search_result(htmlspecialchars($row[$options['datacolumn']]), $word_list);
+				}
+				else if ( isset($options['formatcallback']) )
+				{
+					if ( is_callable($options['formatcallback']) )
+					{
+						$text = call_user_func($options['formatcallback'], $row, $word_list);
+					}
+					else
+					{
+						$parser = $template->makeParserText($options['formatcallback']);
+						$parser->assign_vars($row);
+						$text = $parser->run();
+					}
+				}
+				
+				// Inject result
+				
+				if ( isset($scores[$idstring]) )
+				{
+					$parser = $template->makeParserText($options['linkformat']['page_id']);
+					$parser->assign_vars($row);
+					$page_id = $parser->run();
+					
+					$parser = $template->makeParserText($options['linkformat']['namespace']);
+					$parser->assign_vars($row);
+					$namespace = $parser->run();
+					
+					$page_data[$idstring] = array(
+						'page_name' => highlight_search_result(htmlspecialchars($row[$options['titlecolumn']]), $word_list),
+						'page_text' => $text,
+						'score' => $scores[$idstring],
+						'page_id' => $page_id,
+						'namespace' => $namespace,
+					);
+					
+					// Any additional flags that need to be added to the result?
+					// The small usually-bracketed text to the left of the title
+					if ( isset($options['resultnote']) )
+					{
+						$page_data[$idstring]['page_note'] = $options['resultnote'];
+					}
+					// Should we include the length?
+					if ( isset($options['datacolumn']) )
+					{
+						$page_data[$idstring]['page_length'] = strlen($row[$options['datacolumn']]);
+					}
+					else
+					{
+						$page_data[$idstring]['page_length'] = 0;
+						$page_data[$idstring]['zero_length'] = true;
+					}
+					// Anything to append to result links?
+					if ( isset($options['linkformat']['append']) )
+					{
+						$page_data[$idstring]['url_append'] = $options['linkformat']['append'];
+					}
+				}
+			}
+			while ( $row = $db->fetchrow($q) );
+			$db->free_result($q);
+		}
+	}
 }
 
 ?>
--- a/includes/sessions.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/sessions.php	Sun Mar 28 23:10:46 2010 -0400
@@ -21,4132 +21,4132 @@
  */
 
 class sessionManager {
-  
-  # Variables
-  
-  /**
-   * Whether we're logged in or not
-   * @var bool
-   */
-   
-  var $user_logged_in = false;
-  
-  /**
-   * Our current low-privilege session key
-   * @var string
-   */
-  
-  var $sid;
-  
-  /**
-   * Username of currently logged-in user, or IP address if not logged in
-   * @var string
-   */
-  
-  var $username;
-  
-  /**
-   * User ID of currently logged-in user, or 1 if not logged in
-   * @var int
-   */
-  
-  var $user_id = 1;
-  
-  /**
-   * Real name of currently logged-in user, or blank if not logged in
-   * @var string
-   */
-  
-  var $real_name;
-  
-  /**
-   * E-mail address of currently logged-in user, or blank if not logged in
-   * @var string
-   */
-  
-  var $email;
-  
-  /**
-   * List of "extra" user information fields (IM handles, etc.)
-   * @var array (associative)
-   */
-  
-  var $user_extra;
-  
-  /**
-   * User level of current user
-   * USER_LEVEL_GUEST: guest
-   * USER_LEVEL_MEMBER: regular user
-   * USER_LEVEL_CHPREF: default - pseudo-level that allows changing password and e-mail address (requires re-authentication)
-   * USER_LEVEL_MOD: moderator
-   * USER_LEVEL_ADMIN: administrator
-   * @var int
-   */
-  
-  var $user_level;
-  
-  /**
-   * High-privilege session key
-   * @var string or false if not running on high-level authentication
-   */
-  
-  var $sid_super;
-  
-  /**
-   * The user's theme preference, defaults to $template->default_theme
-   * @var string
-   */
-  
-  var $theme;
-  
-  /**
-   * The user's style preference, or style auto-detected based on theme if not logged in
-   * @var string
-   */
-  
-  var $style;
-  
-  /**
-   * Signature of current user - appended to comments, etc.
-   * @var string
-   */
-  
-  var $signature;
-  
-  /**
-   * UNIX timestamp of when we were registered, or 0 if not logged in
-   * @var int
-   */
-  
-  var $reg_time;
-  
-  /**
-   * The number of unread private messages this user has.
-   * @var int
-   */
-  
-  var $unread_pms = 0;
-  
-  /**
-   * AES key used to encrypt passwords and session key info.
-   * @var string
-   * @access private
-   */
-   
-  protected $private_key;
-  
-  /**
-   * Regex that defines a valid username, minus the ^ and $, these are added later
-   * @var string
-   */
-   
-  var $valid_username = '([^<>&\?\'"%\n\r\t\a\/]+)';
-  
-  /**
-   * The current user's user title. Defaults to NULL.
-   * @var string
-   */
-  
-  var $user_title = null;
-   
-  /**
-   * What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
-   * @var string
-   */
-   
-  var $auth_level = 1;
-  
-  /**
-   * Preference for date formatting
-   * @var string
-   */
-  
-  var $date_format = DATE_4;
-  
-  /**
-   * Preference for time formatting
-   * @var string
-   */
-  
-  var $time_format = TIME_24_NS;
-  
-  /**
-   * State variable to track if a session timed out
-   * @var bool
-   */
-  
-  var $sw_timed_out = false;
-  
-  /**
-   * Token appended to some important forms to prevent CSRF.
-   * @var string
-   */
-  
-  var $csrf_token = false;
-  
-  /**
-   * Password change disabled, for auth plugins
-   * @var bool
-   */
-  
-  var $password_change_disabled = false;
-  
-  /**
-   * Password change page URL + title, for auth plugins
-   * @var array
-   */
-  
-  var $password_change_dest = array('url' => '', 'title' => '');
-  
-  /**
-   * Switch to track if we're started or not.
-   * @access private
-   * @var bool
-   */
-   
-  var $started = false;
-  
-  /**
-   * Switch to control compatibility mode (for older Enano websites being upgraded)
-   * @access private
-   * @var bool
-   */
-   
-  var $compat = false;
-  
-  /**
-   * Our list of permission types.
-   * @access private
-   * @var array
-   */
-   
-  var $acl_types = Array();
-  
-  /**
-   * The list of descriptions for the permission types
-   * @var array
-   */
-   
-  var $acl_descs = Array();
-  
-  /**
-   * A list of dependencies for ACL types.
-   * @var array
-   */
-   
-  var $acl_deps = Array();
-  
-  /**
-   * Our tell-all list of permissions. Do not even try to change this.
-   * @access private
-   * @var array
-   */
-   
-  var $perms = Array();
-  
-  /**
-   * A cache variable - saved after sitewide permissions are checked but before page-specific permissions.
-   * @var array
-   * @access private
-   */
-  
-  var $acl_base_cache = Array();
-  
-  /**
-   * Stores the scope information for ACL types.
-   * @var array
-   * @access private
-   */
-   
-  var $acl_scope = Array();
-  
-  /**
-   * Array to track which default permissions are being used
-   * @var array
-   * @access private
-   */
-   
-  var $acl_defaults_used = Array();
-  
-  /**
-   * Array to track group membership.
-   * @var array
-   */
-   
-  var $groups = Array();
-  
-  /**
-   * Associative array to track group modship.
-   * @var array
-   */
-   
-  var $group_mod = Array();
-  
-  /**
-   * A constant array of user-level-to-rank default associations.
-   * @var array
-   */
-  
-  var $level_rank_table = array(
-      USER_LEVEL_ADMIN  => RANK_ID_ADMIN,
-      USER_LEVEL_MOD    => RANK_ID_MOD,
-      USER_LEVEL_MEMBER => RANK_ID_MEMBER,
-      USER_LEVEL_CHPREF => RANK_ID_MEMBER,
-      USER_LEVEL_GUEST  => RANK_ID_GUEST
-    );
-  
-  /**
-   * A constant array that maps precedence constants to language strings
-   * @var array
-   */
-  
-  var $acl_inherit_lang_table = array(
-      ACL_INHERIT_ENANO_DEFAULT   => 'acl_inherit_enano_default',
-      ACL_INHERIT_GLOBAL_EVERYONE => 'acl_inherit_global_everyone',
-      ACL_INHERIT_GLOBAL_GROUP    => 'acl_inherit_global_group',
-      ACL_INHERIT_GLOBAL_USER     => 'acl_inherit_global_user',
-      ACL_INHERIT_PG_EVERYONE     => 'acl_inherit_pg_everyone',
-      ACL_INHERIT_PG_GROUP        => 'acl_inherit_pg_group',
-      ACL_INHERIT_PG_USER         => 'acl_inherit_pg_user',
-      ACL_INHERIT_LOCAL_EVERYONE  => 'acl_inherit_local_everyone',
-      ACL_INHERIT_LOCAL_GROUP     => 'acl_inherit_local_group',
-      ACL_INHERIT_LOCAL_USER      => 'acl_inherit_local_user'
-    );
-  
-  # Basic functions
-   
-  /**
-   * Constructor.
-   */
-   
-  function __construct()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
-    {
-      @include(ENANO_ROOT.'/config.new.php');
-    }
-    else
-    {
-      @include(ENANO_ROOT.'/config.php');
-    }
-    
-    unset($dbhost, $dbname, $dbuser, $dbpasswd);
-    if(isset($crypto_key))
-    {
-      $this->private_key = $crypto_key;
-      $this->private_key = hexdecode($this->private_key);
-    }
-    else
-    {
-      if(is_writable(ENANO_ROOT.'/config.php'))
-      {
-        // Generate and stash a private key
-        // This should only happen during an automated silent gradual migration to the new encryption platform.
-        $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-        $this->private_key = $aes->gen_readymade_key();
-        
-        $config = file_get_contents(ENANO_ROOT.'/config.php');
-        if(!$config)
-        {
-          die('$session->__construct(): can\'t get the contents of config.php');
-        }
-        
-        $config = str_replace("?>", "\$crypto_key = '{$this->private_key}';\n?>", $config);
-        // And while we're at it...
-        $config = str_replace('MIDGET_INSTALLED', 'ENANO_INSTALLED', $config);
-        $fh = @fopen(ENANO_ROOT.'/config.php', 'w');
-        if ( !$fh ) 
-        {
-          die('$session->__construct(): Couldn\'t open config file for writing to store the private key, I tried to avoid something like this...');
-        }
-        
-        fwrite($fh, $config);
-        fclose($fh);
-      }
-      else
-      {
-        die_semicritical('Crypto error', '<p>No private key was found in the config file, and we can\'t generate one because we don\'t have write access to the config file. Please CHMOD config.php to 666 or 777 and reload this page.</p>');
-      }
-    }
-    // Check for compatibility mode
-    if(defined('IN_ENANO_INSTALL'))
-    {
-      $q = $db->sql_query('SELECT old_encryption FROM '.table_prefix.'users LIMIT 1;');
-      if(!$q)
-      {
-        $error = mysql_error();
-        if(strstr($error, "Unknown column 'old_encryption'"))
-          $this->compat = true;
-        else
-          $db->_die('This should never happen and is a bug - the only error that was supposed to happen here didn\'t happen. (sessions.php in constructor, during compat mode check)');
-      }
-      $db->free_result();
-    }
-  }
-  
-  /**
-   * PHP 4 compatible constructor. Deprecated in 1.1.x.
-   */
-   
-  /*
-  function sessionManager()
-  {
-    $this->__construct();
-  }
-  */
-  
-  /**
-   * Wrapper function to sanitize strings for MySQL and HTML
-   * @param string $text The text to sanitize
-   * @return string
-   */
-  
-  function prepare_text($text)
-  {
-    global $db;
-    return $db->escape(htmlspecialchars($text));
-  }
-  
-  /**
-   * Makes a SQL query and handles error checking
-   * @param string $query The SQL query to make
-   * @return resource
-   */
-  
-  function sql($query)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $result = $db->sql_query($query);
-    if(!$result)
-    {
-      $db->_die('The error seems to have occurred somewhere in the session management code.');
-    }
-    return $result;
-  }
-  
-  /**
-   * Returns true if we're currently on a page that shouldn't be blocked even if we have an inactive or banned account
-   * @param bool strict - if true, whitelist of pages is even stricter (Login, Logout and CSS only). if false (default), admin access is allowed, assuming other factors allow it
-   * @return bool
-   */
-  
-  function on_critical_page($strict = false)
-  {
-    global $urlname;
-    list($page_id, $namespace) = RenderMan::strToPageID($urlname);
-    list($page_id) = explode('/', $page_id);
-    
-    if ( $strict )
-    {
-      return $namespace == 'Special' && in_array($page_id, array('CSS', 'Login', 'Logout', 'LangExportJSON', 'ActivateAccount'));
-    }
-    else
-    {
-      return $namespace == 'Admin' || ($namespace == 'Special' && in_array($page_id, array('CSS', 'Login', 'Logout', 'Administration', 'LangExportJSON', 'ActivateAccount')));
-    }
-  }
-  
-  # Session restoration and permissions
-  
-  /**
-   * Initializes the basic state of things, including most user prefs, login data, cookie stuff
-   */
-  
-  function start()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $timezone;
-    if($this->started) return;
-    $this->started = true;
-    $user = false;
-    if ( isset($_COOKIE['sid']) )
-    {
-      if ( $this->compat )
-      {
-        $userdata = $this->compat_validate_session($_COOKIE['sid']);
-      }
-      else
-      {
-        $userdata = $this->validate_session($_COOKIE['sid']);
-      }
-      if ( is_array($userdata) )
-      {
-        $this->sid = $_COOKIE['sid'];
-        $this->user_logged_in = true;
-        $this->user_id =       intval($userdata['user_id']);
-        $this->username =      $userdata['username'];
-        $this->user_level =    intval($userdata['user_level']);
-        $this->real_name =     $userdata['real_name'];
-        $this->email =         $userdata['email'];
-        $this->unread_pms =    $userdata['num_pms'];
-        $this->user_title =    ( isset($userdata['user_title']) ) ? $userdata['user_title'] : null;
-        if(!$this->compat)
-        {
-          $this->theme =         $userdata['theme'];
-          $this->style =         $userdata['style'];
-          $this->signature =     $userdata['signature'];
-          $this->reg_time =      $userdata['reg_time'];
-        }
-        $this->auth_level =    USER_LEVEL_MEMBER;
-        // generate an anti-CSRF token
-        $this->csrf_token =    sha1($this->username . $this->sid . $this->user_id);
-        if(!isset($template->named_theme_list[$this->theme]))
-        {
-          if($this->compat || !is_object($template))
-          {
-            $this->theme = 'oxygen';
-            $this->style = 'bleu';
-          }
-          else
-          {
-            $this->theme = $template->default_theme;
-            $this->style = $template->default_style;
-          }
-        }
-        $user = true;
-        
-        // set timezone params
-        $GLOBALS['timezone'] = $userdata['user_timezone'];
-        $GLOBALS['dst_params'] = explode(';', $userdata['user_dst']);
-        foreach ( $GLOBALS['dst_params'] as &$parm )
-        {
-          if ( substr($parm, -1) != 'd' )
-            $parm = intval($parm);
-        }
-        
-        // Set language
-        if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
-        {
-          $lang_id = intval($userdata['user_lang']);
-          $lang = new Language($lang_id);
-          @setlocale(LC_ALL, $lang->lang_code);
-        }
-        
-        if(isset($_REQUEST['auth']) && !$this->sid_super)
-        {
-          // Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth.
-          if($this->compat)
-          {
-            $key = $_REQUEST['auth'];
-            $super = $this->compat_validate_session($key);
-          }
-          else
-          {
-            $key = $_REQUEST['auth'];
-            if ( !empty($key) && ( strlen($key) / 2 ) % 4 == 0 )
-            {
-              $super = $this->validate_session($key);
-            }
-          }
-          if(is_array(@$super))
-          {
-            $this->auth_level = intval($super['auth_level']);
-            $this->sid_super = $_REQUEST['auth'];
-          }
-        }
-      }
-    }
-    if(!$user)
-    {
-      //exit;
-      $this->register_guest_session();
-    }
-    if(!$this->compat)
-    {
-      // init groups
-      $q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g' . "\n"
-        . '  LEFT JOIN '.table_prefix.'group_members AS m' . "\n"
-        . '    ON g.group_id=m.group_id' . "\n"
-        . '  WHERE ( m.user_id='.$this->user_id.'' . "\n" 
-        . '    OR g.group_name=\'Everyone\')' . "\n"
-        . '    ' . ( /* quick hack for upgrade compatibility reasons */ enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . '' . "\n"
-        . '  ORDER BY group_id ASC;'); // The ORDER BY is to make sure "Everyone" comes first so the permissions can be overridden
-      if($row = $db->fetchrow())
-      {
-        do {
-          $this->groups[$row['group_id']] = $row['group_name'];
-          $this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 );
-        } while($row = $db->fetchrow());
-      }
-      else
-      {
-        die('No group info');
-      }
-      profiler_log('Fetched group memberships');
-    }
-    
-    // make sure we aren't banned
-    $this->check_banlist();
-    
-    // make sure the account is active
-    if ( !$this->compat && $this->user_logged_in && $userdata['account_active'] < 1 && !$this->on_critical_page() )
-    {
-      $this->show_inactive_error($userdata);
-    }
-    
-    // Printable page view? Probably the wrong place to control
-    // it but $template is pretty dumb, it will just about always
-    // do what you ask it to do, which isn't always what we want
-    if ( isset ( $_GET['printable'] ) )
-    {
-      $this->theme = 'printable';
-      $this->style = 'default';
-    }
-    
-    // setup theme ACLs
-    $template->process_theme_acls();
-    
-    profiler_log('Sessions started. Banlist and theme ACLs initialized');
-  }
-  
-  # Logins
-  
-  /**
-   * Attempts to perform a login using crypto functions
-   * @param string $username The username
-   * @param string $aes_data The encrypted password, hex-encoded
-   * @param string $aes_key The MD5 hash of the encryption key, hex-encoded
-   * @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
-   * @param int $level The privilege level we're authenticating for, defaults to 0
-   * @param string $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
-   * @param string $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
-   * @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
-   * @param bool $lookup_key Optional. If true (default) this queries the database for the "real" encryption key. Else, uses what is given.
-   * @return string 'success' on success, or error string on failure
-   */
-   
-  function login_with_crypto($username, $aes_data, $aes_key_id, $challenge, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false, $lookup_key = true)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Instanciate the Rijndael encryption object
-    $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-    
-    // Fetch our decryption key
-    
-    if ( $lookup_key )
-    {
-      $aes_key = $this->fetch_public_key($aes_key_id);
-      if ( !$aes_key )
-      {
-        // It could be that our key cache is full. If it seems larger than 65KB, clear it
-        if ( strlen(getConfig('login_key_cache')) > 65000 )
-        {
-          setConfig('login_key_cache', '');
-          return array(
-            'success' => false,
-            'error' => 'key_not_found_cleared',
-            );
-        }
-        return array(
-          'success' => false,
-          'error' => 'key_not_found'
-          );
-      }
-    }
-    else
-    {
-      $aes_key =& $aes_key_id;
-    }
-    
-    // Convert the key to a binary string
-    $bin_key = hexdecode($aes_key);
-    
-    if(strlen($bin_key) != AES_BITS / 8)
-      return array(
-        'success' => false,
-        'error' => 'key_wrong_length'
-        );
-    
-    // Decrypt our password
-    $password = $aes->decrypt($aes_data, $bin_key, ENC_HEX);
-    
-    // Let the LoginAPI do the rest.
-    return $this->login_without_crypto($username, $password, false, $level, $captcha_hash, $captcha_code, $remember);
-  }
-  
-  /**
-   * Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript
-   * This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox
-   * Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication
-   * @param string $username The username
-   * @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true
-   * @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false.
-   * @param int $level The privilege level we're authenticating for, defaults to 0
-   * @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
-   */
-  
-  function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $remember = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( $already_md5ed )
-    {
-      // No longer supported
-      return array(
-          'mode' => 'error',
-          'error' => '$already_md5ed is no longer supported (set this parameter to false and make sure the password you send to $session->login_without_crypto() is not hashed)'
-        );
-    }
-    
-    // Replace underscores with spaces in username
-    // (Added in 1.0.2)
-    $username = str_replace('_', ' ', $username);
-    
-    // Perhaps we're upgrading Enano?
-    if($this->compat)
-    {
-      return $this->login_compat($username, md5($password), $level);
-    }
-    
-    // Instanciate the Rijndael encryption object
-    $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-    
-    // Initialize our success switch
-    $success = false;
-    
-    // Retrieve the real password from the database
-    $username_db = $db->escape(strtolower($username));
-    $username_db_upper = $db->escape($username);
-    if ( !$db->sql_query('SELECT password,password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n"
-                       . "  WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db' OR username = '$username_db_upper' );") )
-    {
-      $this->sql('SELECT password,\'\' AS password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n"
-               . "  WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db' OR username = '$username_db_upper' );");
-    }
-    if ( $db->numrows() < 1 )
-    {
-      // This wasn't logged in <1.0.2, dunno how it slipped through
-      if ( $level > USER_LEVEL_MEMBER )
-        $this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES\n"
-                   . '  (\'security\', \'admin_auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
-                      . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
-      else
-        $this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary) VALUES\n"
-                   . '  (\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
-                      . '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
-      
-      // Do we also need to increment the lockout countdown?
-      if ( !defined('IN_ENANO_INSTALL') )
-        $lockout_data = $this->get_lockout_info();
-      else
-        $lockout_data = array(
-          'lockout_policy' => 'disable'
-          );
-      
-      if ( $lockout_data['policy'] != 'disable' && !defined('IN_ENANO_INSTALL') )
-      {
-        $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
-        // increment fail count
-        $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
-        $lockout_data['fails']++;
-        return array(
-            'success' => false,
-            'error' => ( $lockout_data['fails'] >= $lockout_data['threshold'] ) ? 'locked_out' : 'invalid_credentials',
-            'lockout_threshold' => $lockout_data['threshold'],
-            'lockout_duration' => ( $lockout_data['duration'] ),
-            'lockout_fails' => $lockout_data['fails'],
-            'lockout_policy' => $lockout_data['policy']
-          );
-      }
-      
-      return array(
-        'success' => false,
-        'error' => 'invalid_credentials'
-      );
-    }
-    $row = $db->fetchrow();
-    
-    // Check to see if we're logging in using a temporary password
-    
-    if((intval($row['temp_password_time']) + 3600*24) > time() )
-    {
-      $temp_pass = hmac_sha1($password, $row['password_salt']);
-      if( $temp_pass === $row['temp_password'] )
-      {
-        $code = $plugins->setHook('login_password_reset');
-        foreach ( $code as $cmd )
-        {
-          eval($cmd);
-        }
-        
-        return array(
-            'success' => false,
-            'error' => 'valid_reset',
-            'redirect_url' => makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $this->pk_encrypt($password))
-          );
-      }
-    }
-    
-    if ( $row['old_encryption'] == 1 )
-    {
-      // The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password
-      if(md5($password) === $row['password'])
-      {
-        if ( !defined('IN_ENANO_UPGRADE') )
-        {
-          $hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
-          $password_hmac = hmac_sha1($password, $hmac_secret);
-          $this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};");
-        }
-        $success = true;
-      }
-    }
-    else if ( $row['old_encryption'] == 2 || ( defined('ENANO_UPGRADE_USE_AES_PASSWORDS') ) && strlen($row['password']) != 40 )
-    {
-      // Our password field uses the 1.0RC1-1.1.5 encryption format
-      $real_pass = $aes->decrypt($row['password'], $this->private_key);
-      if($password === $real_pass)
-      {
-        if ( !defined('IN_ENANO_UPGRADE') )
-        {
-          $hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
-          $password_hmac = hmac_sha1($password, $hmac_secret);
-          $this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};");
-        }
-        $success = true;
-      }
-    }
-    else
-    {
-      // Password uses HMAC-SHA1
-      $user_challenge = hmac_sha1($password, $row['password_salt']);
-      $password_hmac =& $row['password'];
-      if ( $user_challenge === $password_hmac )
-      {
-        $success = true;
-      }
-    }
-    if($success)
-    {
-      if((int)$level > (int)$row['user_level'])
-        return array(
-          'success' => false,
-          'error' => 'too_big_for_britches'
-        );
-      
-      // grant session
-      $sess = $this->register_session($row['user_id'], $username, ( isset($password_hmac) ? $password_hmac : $password ), $level, $remember);
-      
-      if($sess)
-      {
-        if($level > USER_LEVEL_MEMBER)
-          $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', ' . $row['user_id'] . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
-        else
-          $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', ' . $row['user_id'] . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
-        
-        $code = $plugins->setHook('login_success');
-        foreach ( $code as $cmd )
-        {
-          eval($cmd);
-        }
-        
-        return array(
-          'success' => true
-          );
-      }
-      else
-        return array(
-          'success' => false,
-          'error' => 'backend_fail'
-        );
-    }
-    else
-    {
-      if($level > USER_LEVEL_MEMBER)
-        $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
-      else
-        $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
-        
-      // Do we also need to increment the lockout countdown?
-      if ( !defined('IN_ENANO_INSTALL') && getConfig('lockout_policy', 'lockout') !== 'disable' )
-      {
-        $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
-        // increment fail count
-        $this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
-      }
-        
-      return array(
-        'success' => false,
-        'error' => 'invalid_credentials'
-      );
-    }
-  }
-  
-  /**
-   * Attempts to log in using the old table structure and algorithm. This is for upgrades from old 1.0.x releases.
-   * @param string $username
-   * @param string $password This should be an MD5 hash
-   * @return string 'success' if successful, or error message on failure
-   */
-  
-  function login_compat($username, $password, $level = 0)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $pass_hashed =& $password;
-    $this->sql('SELECT password,user_id,user_level FROM '.table_prefix.'users WHERE username=\''.$this->prepare_text($username).'\';');
-    if($db->numrows() < 1)
-      return 'The username and/or password is incorrect.';
-    $row = $db->fetchrow();
-    if($row['password'] == $password)
-    {
-      if((int)$level > (int)$row['user_level'])
-        return 'You are not authorized for this level of access.';
-      $sess = $this->register_session_compat(intval($row['user_id']), $username, $password, $level);
-      if($sess)
-        return 'success';
-      else
-        return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.';
-    }
-    else
-    {
-      return 'The username and/or password is incorrect.';
-    }
-  }
-  
-  /**
-   * Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated!
-   * Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]"
-   * @param int $user_id
-   * @param string $username
-   * @param string $password_hmac The HMAC of the user's password, right from the database
-   * @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER
-   * @param bool $remember Whether the session should be long-term (true) or not (false). Defaults to short-term.
-   * @return bool
-   */
-   
-  function register_session($user_id, $username, $password_hmac, $level = USER_LEVEL_MEMBER, $remember = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Random key identifier
-    $salt = '';
-    for ( $i = 0; $i < 32; $i++ )
-    {
-      $salt .= chr(mt_rand(32, 126));
-    }
-    
-    // Session key
-    if ( defined('ENANO_UPGRADE_USE_AES_PASSWORDS') )
-    {
-      $session_key = $this->pk_encrypt("u=$username;p=" . sha1($password_hmac) . ";s=$salt");
-    }
-    else
-    {
-      $key_pieces = array($password_hmac);
-      $sk_mode = 'generate';
-      $code = $plugins->setHook('session_key_calc');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      $key_pieces = implode("\xFF", $key_pieces);
-      
-      $session_key = hmac_sha1($key_pieces, $salt);
-    }
-    
-    // Minimum level
-    $level = max(array($level, USER_LEVEL_MEMBER));
-    
-    // Type of key
-    $key_type = ( $level > USER_LEVEL_MEMBER ) ? SK_ELEV : ( $remember ? SK_LONG : SK_SHORT );
-    
-    // If we're registering an elevated-privilege key, it needs to be on GET
-    if($level > USER_LEVEL_MEMBER)
-    {
-      $this->sid_super = $session_key;
-      $_GET['auth'] = $session_key;
-    }
-    else
-    {
-      // Stash it in a cookie
-      // For now, make the cookie last forever, we can change this in 1.1.x
-      setcookie( 'sid', $session_key, time()+15552000, scriptPath.'/', null, $GLOBALS['is_https']);
-      $_COOKIE['sid'] = $session_key;
-      $this->sid = $session_key;
-    }
-    // $keyhash is stored in the database, this is for compatibility with the older DB structure
-    $keyhash = md5($session_key);
-    // Record the user's IP
-    $ip = $_SERVER['REMOTE_ADDR'];
-    if(!is_valid_ip($ip))
-      die('$session->register_session: Remote-Addr was spoofed');
-    // The time needs to be stashed to enforce the 15-minute limit on elevated session keys
-    $time = time();
-    
-    // Sanity check
-    if(!is_int($user_id))
-      die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
-    if(!is_int($level))
-      die(var_dump($level) . '<br />Somehow an SQL injection attempt crawled into our session registrar! (2)');
-    
-    // Update RAM
-    $this->user_id = $user_id;
-    $this->user_level = max(array($this->user_level, $level));
-    
-    // All done!
-    $query = $db->sql_query('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time, key_type) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.', ' . $key_type . ');');
-    if ( !$query && defined('IN_ENANO_UPGRADE') )
-      // we're trying to upgrade so the key_type column is probably missing - try it again without specifying the key type
-      $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
-      
-    return true;
-  }
-  
-  /**
-   * Identical to register_session in nature, but uses the old login/table structure. DO NOT use this except in the upgrade script under very controlled circumstances.
-   * @see sessionManager::register_session()
-   * @access private
-   */
-  
-  function register_session_compat($user_id, $username, $password, $level = 0)
-  {
-    $salt = md5(microtime() . mt_rand());
-    $thekey = md5($password . $salt);
-    if($level > 0)
-    {
-      $this->sid_super = $thekey;
-    }
-    else
-    {
-      setcookie( 'sid', $thekey, time()+315360000, scriptPath.'/' );
-      $_COOKIE['sid'] = $thekey;
-    }
-    $ip = ip2hex($_SERVER['REMOTE_ADDR']);
-    if(!$ip)
-      die('$session->register_session: Remote-Addr was spoofed');
-    $time = time();
-    if(!is_int($user_id))
-      die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
-    if(!is_int($level))
-      die('Somehow an SQL injection attempt crawled into our session registrar! (2)');
-    $query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$thekey.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
-    return true;
-  }
-  
-  /**
-   * Tells us if we're locked out from logging in or not.
-   * @param reference will be filled with information regarding in-progress lockout
-   * @return bool True if locked out, false otherwise
-   */
-  
-  function get_lockout_info()
-  {
-    global $db;
-    
-    // this has to be initialized to hide warnings
-    $lockdata = null;
-    
-    // Query database for lockout info
-    $locked_out = false;
-    $threshold = ( $_ = getConfig('lockout_threshold') ) ? intval($_) : 5;
-    $duration  = ( $_ = getConfig('lockout_duration') ) ? intval($_) : 15;
-    // convert to seconds
-    $duration  = $duration * 60;
-    // decide on policy
-    $policy = ( $x = getConfig('lockout_policy') && in_array(getConfig('lockout_policy'), array('lockout', 'disable', 'captcha')) ) ? getConfig('lockout_policy') : 'lockout';
-    if ( $policy != 'disable' )
-    {
-      // enabled; make decision
-      $ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
-      $timestamp_cutoff = time() - $duration;
-      $q = $this->sql('SELECT timestamp FROM ' . table_prefix . 'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
-      $fails = $db->numrows($q);
-      $row = $db->fetchrow($q);
-      $locked_out = ( $fails >= $threshold );
-      $lockdata = array(
-          'active' => $locked_out,
-          'threshold' => $threshold,
-          'duration' => ( $duration / 60 ),
-          'fails' => $fails,
-          'policy' => $policy,
-          'last_time' => $row['timestamp'],
-          'time_rem' => $locked_out ? ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ) : 0,
-          'captcha' => $policy == 'captcha' ? $this->make_captcha() : ''
-        );
-      $db->free_result();
-    }
-    else
-    {
-      // disabled; send back default dataset
-      $lockdata = array(
-        'active' => false,
-        'threshold' => $threshold,
-        'duration' => ( $duration / 60 ),
-        'fails' => 0,
-        'policy' => $policy,
-        'last_time' => 0,
-        'time_rem' => 0,
-        'captcha' => ''
-      );
-    }
-    return $lockdata;
-  }
-  
-  /**
-   * Creates/restores a guest session
-   * @todo implement real session management for guests
-   */
-   
-  function register_guest_session()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    $this->username = $_SERVER['REMOTE_ADDR'];
-    $this->user_level = USER_LEVEL_GUEST;
-    if($this->compat || defined('IN_ENANO_INSTALL'))
-    {
-      $this->theme = 'oxygen';
-      $this->style = 'bleu';
-    }
-    else
-    {
-      $this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme;
-      $this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : preg_replace('/\.css$/', '', $template->named_theme_list[$this->theme]['default_style']);
-    }
-    $this->user_id = 1;
-    // This is a VERY special case we are allowing. It lets the installer create languages using the Enano API.
-    if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
-    {
-      $language = ( isset($_GET['lang']) && preg_match('/^[a-z0-9-_]+$/', @$_GET['lang']) ) ? $_GET['lang'] : intval(getConfig('default_language'));
-      $lang = new Language($language);
-      @setlocale(LC_ALL, $lang->lang_code);
-    }
-    // make a CSRF token
-    $this->csrf_token = hmac_sha1($_SERVER['REMOTE_ADDR'], sha1($this->private_key));
-  }
-  
-  /**
-   * Validates a session key, and returns the userdata associated with the key or false
-   * @param string $key The session key to validate
-   * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides.
-   */
-   
-  function validate_session($key)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    profiler_log("SessionManager: checking session: " . sha1($key));
-    
-    if ( strlen($key) > 48 )
-    {
-      return $this->validate_aes_session($key);
-    }
-    
-    profiler_log("SessionManager: checking session: " . $key);
-    
-    return $this->validate_session_shared($key, '');
-  }
-  
-  /**
-   * Validates an old-format AES session key. DO NOT USE THIS. Will return false if called outside of an upgrade.
-   * @param string Session key
-   * @return array
-   */
-  
-  protected function validate_aes_session($key)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // No valid use except during upgrades
-    if ( !preg_match('/^upg-/', enano_version()) && !defined('IN_ENANO_UPGRADE') )
-      return false;
-    
-    $decrypted_key = $this->pk_decrypt($key);
-    if ( !$decrypted_key )
-    {
-      // die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>');
-      return false;
-    }
-    
-    $n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=(.{32})$/', $decrypted_key, $keydata);
-    if($n < 1)
-    {
-      echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key;
-      return false;
-    }
-    $keyhash = md5($key);
-    $salt = $db->escape($keydata[3]);
-    
-    return $this->validate_session_shared($keyhash, $salt, true);
-  }
-  
-  /**
-   * Shared portion of session validation. Do not try to call this.
-   * @return array
-   * @access private
-   */
-  
-  protected function validate_session_shared($key, $salt, $loose_call = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // using a normal call to $db->sql_query to avoid failing on errors here
-    $columns_select  = "u.user_id AS uid, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme,\n"
-                      . "  u.style,u.signature, u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_title, k.salt, k.source_ip,\n"
-                      . "  k.time, k.auth_level, k.key_type, COUNT(p.message_id) AS num_pms, u.user_timezone, u.user_dst, x.*";
-    
-    $columns_groupby = "u.user_id, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme, u.style, u.signature,\n"
-                      . "           u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_timezone, u.user_title, u.user_dst,\n"
-                      . "           k.salt, k.source_ip, k.time, k.auth_level, k.key_type, x.user_id, x.user_aim, x.user_yahoo, x.user_msn,\n"
-                      . "           x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public,\n"
-                      . "           x.disable_js_fx, x.date_format, x.time_format";
-    
-    $joins = "  LEFT JOIN " . table_prefix . "users AS u\n"
-            . "    ON ( u.user_id=k.user_id )\n"
-            . "  LEFT JOIN " . table_prefix . "users_extra AS x\n"
-            . "    ON ( u.user_id=x.user_id OR x.user_id IS NULL )\n"
-            . "  LEFT JOIN " . table_prefix . "privmsgs AS p\n"
-            . "    ON ( p.message_to=u.username AND p.message_read=0 )\n";
-    if ( !$loose_call )
-    {
-      $key_md5 = md5($key);
-      $query = $db->sql_query("SELECT $columns_select\n"
-                            . "FROM " . table_prefix . "session_keys AS k\n"
-                            . $joins
-                            . "  WHERE k.session_key='$key_md5'\n"
-                            . "  GROUP BY $columns_groupby;");
-    }
-    else
-    {
-      $query = $db->sql_query("SELECT $columns_select\n"
-                            . "FROM " . table_prefix . "session_keys AS k\n"
-                            . $joins
-                            . "  WHERE k.session_key='$key'\n"
-                            . "    AND k.salt='$salt'\n"
-                            . "  GROUP BY $columns_groupby;");
-    }
-    
-    if ( !$query && ( defined('IN_ENANO_INSTALL') or defined('IN_ENANO_UPGRADE') ) )
-    {
-      $key_md5 = $loose_call ? $key : md5($key);
-      $query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,\'\' AS password_salt,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone, \'0;0;0;0;60\' AS user_dst, ' . SK_SHORT . ' AS key_type, k.salt FROM '.table_prefix.'session_keys AS k
-                             LEFT JOIN '.table_prefix.'users AS u
-                               ON ( u.user_id=k.user_id )
-                             LEFT JOIN '.table_prefix.'privmsgs AS p
-                               ON ( p.message_to=u.username AND p.message_read=0 )
-                             WHERE k.session_key=\''.$key_md5.'\'
-                             GROUP BY u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,k.salt;');
-    }
-    else if ( !$query )
-    {
-      $db->_die();
-    }
-    if($db->numrows() < 1)
-    {
-      // echo '(debug) $session->validate_session: Key was not found in database: ' . $key_md5 . '<br />';
-      return false;
-    }
-    $row = $db->fetchrow();
-    profiler_log("SessionManager: session check: selected and fetched results");
-    
-    $row['user_id'] =& $row['uid'];
-    $ip = $_SERVER['REMOTE_ADDR'];
-    if($row['auth_level'] > $row['user_level'])
-    {
-      // Failed authorization check
-      // echo '(debug) $session->validate_session: access to this auth level denied<br />';
-      return false;
-    }
-    if($ip != $row['source_ip'])
-    {
-      // Special exception for 1.1.x upgrade - the 1.1.3 upgrade changes the size of the column and this is what validate_session
-      // expects, but if the column size hasn't changed yet just check the first 10 digits of the IP.
-      $fail = true;
-      if ( defined('IN_ENANO_UPGRADE') )
-      {
-        if ( substr($ip, 0, 10) == substr($row['source_ip'], 0, 10) )
-          $fail = false;
-      }
-      // Failed IP address check
-      // echo '(debug) $session->validate_session: IP address mismatch<br />';
-      if ( $fail )
-        return false;
-    }
-    
-    // $loose_call is turned on only from validate_aes_session
-    if ( !$loose_call )
-    {
-      $key_pieces = array($row['password']);
-      $user_id =& $row['uid'];
-      $sk_mode = 'validate';
-      $code = $plugins->setHook('session_key_calc');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      $key_pieces = implode("\xFF", $key_pieces);
-      $correct_key = hexdecode(hmac_sha1($key_pieces, $row['salt']));
-      $user_key = hexdecode($key);
-      if ( $correct_key !== $user_key || !is_string($user_key) )
-      {
-        return false;
-      }
-    }
-    else
-    {
-      // if this is a "loose call", this only works once (during the final upgrade stage). Destroy the contents of session_keys.
-      if ( $row['auth_level'] == USER_LEVEL_ADMIN && preg_match('/^upg-/', enano_version()) )
-        $this->sql('DELETE FROM ' . table_prefix . "session_keys;");
-    }
-    
-    // timestamp check
-    switch ( $row['key_type'] )
-    {
-      case SK_SHORT:
-        $time_now = time();
-        $time_key = $row['time'] + ( 60 * intval(getConfig('session_short', '720')) );
-        if ( $time_now > $time_key )
-        {
-          // Session timed out
-          return false;
-        }
-        break;
-      case SK_LONG:
-        if ( intval(getConfig('session_remember_time', '0')) === 0 )
-        {
-          // sessions last infinitely, timestamp validation is therefore successful
-          break;
-        }
-        $time_now = time();
-        $time_key = $row['time'] + ( 86400 * intval(getConfig('session_remember_time', '30')) );
-        if ( $time_now > $time_key )
-        {
-          // Session timed out
-          return false;
-        }
-        break;
-      case SK_ELEV:
-        $time_now = time();
-        $time_key = $row['time'] + 900;
-        if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER)
-        {
-          // Session timed out
-          // echo '(debug) $session->validate_session: super session timed out<br />';
-          $this->sw_timed_out = true;
-          return false;
-        }
-        break;
-    }
-        
-    // If this is an elevated-access or short-term session key, update the time
-    if( $row['key_type'] == SK_ELEV || $row['key_type'] == SK_SHORT )
-    {
-      $this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.md5($key).'\';');
-    }
-    
-    $user_extra = array();
-    foreach ( array('user_aim', 'user_yahoo', 'user_msn', 'user_xmpp', 'user_homepage', 'user_location', 'user_job', 'user_hobbies', 'email_public', 'disable_js_fx') as $column )
-    {
-      if ( isset($row[$column]) )
-        $user_extra[$column] = $row[$column];
-      else
-        $user_extra[$column] = '';
-    }
-    
-    if ( isset($row['date_format']) )
-      $this->date_format = $row['date_format'];
-    if ( isset($row['time_format']) )
-      $this->time_format = $row['time_format'];
-    
-    $this->user_extra = $user_extra;
-    // Leave the rest to PHP's automatic garbage collector ;-)
-    
-    $row['password'] = '';
-    $row['user_timezone'] = intval($row['user_timezone']) - 1440;
-    
-    profiler_log("SessionManager: finished session check");
-    
-    return $row;
-  }
-  
-  /**
-   * Validates a session key, and returns the userdata associated with the key or false. Optimized for compatibility with the old MD5-based auth system.
-   * @param string $key The session key to validate
-   * @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides.
-   */
-   
-  function compat_validate_session($key)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $key = $db->escape($key);
-    
-    $query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level,1440 AS user_timezone FROM '.table_prefix.'session_keys AS k
-                           LEFT JOIN '.table_prefix.'users AS u
-                             ON u.user_id=k.user_id
-                           WHERE k.session_key=\''.$key.'\';');
-    if($db->numrows() < 1)
-    {
-      // echo '(debug) $session->validate_session: Key '.$key.' was not found in database<br />';
-      return false;
-    }
-    $row = $db->fetchrow();
-    $ip = ip2hex($_SERVER['REMOTE_ADDR']);
-    if($row['auth_level'] > $row['user_level'])
-    {
-      // Failed authorization check
-      // echo '(debug) $session->validate_session: user not authorized for this access level';
-      return false;
-    }
-    if($ip != $row['source_ip'])
-    {
-      // Failed IP address check
-      // echo '(debug) $session->validate_session: IP address mismatch; IP in table: '.$row['source_ip'].'; reported IP: '.$ip.'';
-      return false;
-    }
-    
-    // Do the password validation
-    $real_key = md5($row['password'] . $row['salt']);
-    
-    //die('<pre>'.print_r($keydata, true).'</pre>');
-    if($real_key != $key)
-    {
-      // Failed password check
-      // echo '(debug) $session->validate_session: supplied password is wrong<br />Real key: '.$real_key.'<br />User key: '.$key;
-      return false;
-    }
-    
-    $time_now = time();
-    $time_key = $row['time'] + 900;
-    if($time_now > $time_key && $row['auth_level'] >= 1)
-    {
-      $this->sw_timed_out = true;
-      // Session timed out
-      // echo '(debug) $session->validate_session: super session timed out<br />';
-      return false;
-    }
-    
-    $row['user_timezone'] = intval($row['user_timezone']) - 1440;
-    
-    return $row;
-  }
-   
-  /**
-   * Demotes us to one less than the specified auth level. AKA destroys elevated authentication and/or logs out the user, depending on $level
-   * @param int $level How low we should go - USER_LEVEL_MEMBER means demote to USER_LEVEL_GUEST, and anything more powerful than USER_LEVEL_MEMBER means demote to USER_LEVEL_MEMBER
-   * @return string 'success' if successful, or error on failure
-   */
-   
-  function logout($level = USER_LEVEL_MEMBER)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $ou = $this->username;
-    $oid = $this->user_id;
-    if($level > USER_LEVEL_MEMBER)
-    {
-      $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-      if(!$this->user_logged_in || $this->auth_level < ( USER_LEVEL_MEMBER + 1))
-      {
-        return 'success';
-      }
-      // Destroy elevated privileges
-      $keyhash = md5($this->sid_super);
-      $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';');
-      $this->sid_super = false;
-      $this->auth_level = USER_LEVEL_MEMBER;
-    }
-    else
-    {
-      if($this->user_logged_in)
-      {
-        $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-        // Completely destroy our session
-        if($this->auth_level > USER_LEVEL_MEMBER)
-        {
-          $this->logout(USER_LEVEL_ADMIN);
-        }
-        $this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($this->sid).'\';');
-        setcookie( 'sid', '', time()-(3600*24), scriptPath.'/' );
-      }
-    }
-    $code = $plugins->setHook('logout_success'); // , Array('level'=>$level,'old_username'=>$ou,'old_user_id'=>$oid));
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    return 'success';
-  }
-  
-  # Miscellaneous stuff
-  
-  /**
-   * Alerts the user that their account is inactive, and tells them appropriate steps to remedy the situation. Halts execution.
-   * @param array Return from validate_session()
-   */
-  
-  function show_inactive_error($userdata)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    global $title;
-    $paths->init($title);
-    
-    $language = intval(getConfig('default_language'));
-    $lang = new Language($language);
-    @setlocale(LC_ALL, $lang->lang_code);
-    
-    $a = getConfig('account_activation');
-    switch($a)
-    {
-      case 'none':
-      default:
-        $solution = $lang->get('user_login_noact_solution_none');
-        break;
-      case 'user':
-        $solution = $lang->get('user_login_noact_solution_user');
-        break;
-      case 'admin':
-        $solution = $lang->get('user_login_noact_solution_admin');
-        break;
-    }
-    
-    // admin activation request opportunity
-    $q = $db->sql_query('SELECT 1 FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\' AND edit_summary=\'' . $db->escape($userdata['username']) . '\';');
-    if ( !$q )
-      $db->_die();
-    
-    $can_request = ( $db->numrows() < 1 );
-    $db->free_result();
-    
-    if ( isset($_POST['logout']) )
-    {
-      $this->sid = $_COOKIE['sid'];
-      $this->user_logged_in = true;
-      $this->user_id =       intval($userdata['user_id']);
-      $this->username =      $userdata['username'];
-      $this->auth_level =    USER_LEVEL_MEMBER;
-      $this->user_level =    USER_LEVEL_MEMBER;
-      $this->logout();
-      redirect(scriptPath . '/', $lang->get('user_login_noact_msg_logout_success_title'), $lang->get('user_login_noact_msg_logout_success_body'), 5);
-    }
-    
-    if ( $can_request && !isset($_POST['activation_request']) )
-    {
-      $form = '<p>' . $lang->get('user_login_noact_msg_ask_admins') . '</p>
-               <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
-                 <p><input type="submit" name="activation_request" value="' . $lang->get('user_login_noact_btn_request_activation') . '" /> <input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
-               </form>';
-    }
-    else
-    {
-      if ( $can_request && isset($_POST['activation_request']) )
-      {
-        $this->admin_activation_request($userdata['username']);
-        $form = '<p>' . $lang->get('user_login_noact_msg_admins_just_asked') . '</p>
-                 <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
-                   <p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
-                 </form>';
-      }
-      else
-      {
-        $form = '<p>' . $lang->get('user_login_noact_msg_admins_asked') . '</p>
-                 <form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
-                   <p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
-                 </form>';
-      }
-    }
-    
-    global $output;
-    $output = new Output_HTML();
-    $output->set_title($lang->get('user_login_noact_title'));
-    die_friendly($lang->get('user_login_noact_title'), '<p>' . $lang->get('user_login_noact_msg_intro') . ' '.$solution.'</p>' . $form);
-  }
-  
-  /**
-   * Appends the high-privilege session key to the URL if we are authorized to do high-privilege stuff
-   * @param string $url The URL to add session data to
-   * @return string
-   */
-  
-  function append_sid($url)
-  {
-    $sep = ( strstr($url, '?') ) ? '&' : '?';
-    if ( $this->sid_super )
-    {
-      $url = $url . $sep . 'auth=' . urlencode($this->sid_super);
-      // echo($this->sid_super.'<br/>');
-    }
-    return $url;
-  }
-  
-  /**
-   * Prevent the user from changing their password. Authentication plugins may call this to enforce single sign-on.
-   * @param string URL to page where the user may change their password
-   * @param string Title of the page where the user may change their password
-   * @return null
-   */
-  
-  function disable_password_change($change_url = false, $change_title = false)
-  {
-    if ( $this->password_change_disabled )
-    {
-      // don't allow calling twice. if we have two plugins doing this, somebody is bad at configuring websites.
-      return false;
-    }
-    
-    if ( is_string($change_url) && is_string($change_title) )
-    {
-      $this->password_change_dest = array(
-          'url' => $change_url,
-          'title' => $change_title
-        );
-    }
-    else
-    {
-      $this->password_change_dest = array(
-          'url' => false,
-          'title' => false
-        );
-    }
-    
-    $this->password_change_disabled = true;
-  }
-  
-  /**
-   * Grabs the user's password MD5 - NOW DEPRECATED AND DISABLED.
-   * @return bool false
-   */
-   
-  function grab_password_hash()
-  {
-    return false;
-  }
-  
-  /**
-   * Destroys the user's password MD5 in memory
-   */
-  
-  function disallow_password_grab()
-  {
-    $this->password_hash = false;
-    return false;
-  }
-  
-  /**
-   * Generates an AES key and stashes it in the database
-   * @return string Hex-encoded AES key
-   */
-   
-  function rijndael_genkey()
-  {
-    $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-    $key = $aes->gen_readymade_key();
-    $keys = getConfig('login_key_cache');
-    if(is_string($keys))
-      $keys .= $key;
-    else
-      $keys = $key;
-    setConfig('login_key_cache', $keys);
-    return $key;
-  }
-  
-  /**
-   * Generate a totally random 128-bit value for MD5 challenges
-   * @return string
-   */
-   
-  function dss_rand()
-  {
-    $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-    $random = $aes->randkey(128);
-    unset($aes);
-    return md5(microtime() . $random);
-  }
-  
-  /**
-   * Fetch a cached login public key using the MD5sum as an identifier. Each key can only be fetched once before it is destroyed.
-   * @param string $md5 The MD5 sum of the key
-   * @return string, or bool false on failure
-   */
-   
-  function fetch_public_key($md5)
-  {
-    $keys = getConfig('login_key_cache');
-    $keys = enano_str_split($keys, AES_BITS / 4);
-    
-    foreach($keys as $i => $k)
-    {
-      if(md5($k) == $md5)
-      {
-        unset($keys[$i]);
-        if(count($keys) > 0)
-        {
-          if ( strlen(getConfig('login_key_cache') ) > 64000 )
-          {
-            // This should only need to be done once every month or so for an average-size site
-            setConfig('login_key_cache', '');
-          }
-          else
-          {
-            $keys = implode('', array_values($keys));
-            setConfig('login_key_cache', $keys);
-          }
-        }
-        else
-        {
-          setConfig('login_key_cache', '');
-        }
-        return $k;
-      }
-    }
-    // Couldn't find the key...
-    return false;
-  }
-  
-  /**
-   * Adds a user to a group.
-   * @param int User ID
-   * @param int Group ID
-   * @param bool Group moderator - defaults to false
-   * @return bool True on success, false on failure
-   */
-  
-  function add_user_to_group($user_id, $group_id, $is_mod = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Validation
-    if ( !is_int($user_id) || !is_int($group_id) || !is_bool($is_mod) )
-      return false;
-    if ( $user_id < 1 || $group_id < 1 )
-      return false;
-    
-    $mod_switch = ( $is_mod ) ? '1' : '0';
-    $q = $this->sql('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $user_id . ' AND group_id=' . $group_id . ';');
-    if ( !$q )
-      $db->_die();
-    if ( $db->numrows() < 1 )
-    {
-      // User is not in group
-      $this->sql('INSERT INTO '.table_prefix.'group_members(user_id,group_id,is_mod) VALUES(' . $user_id . ', ' . $group_id . ', ' . $mod_switch . ');');
-      return true;
-    }
-    else
-    {
-      $row = $db->fetchrow();
-      // Update modship status
-      if ( strval($row['is_mod']) == $mod_switch )
-      {
-        // Modship unchanged
-        return true;
-      }
-      else
-      {
-        // Modship changed
-        $this->sql('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod_switch . ' WHERE member_id=' . $row['member_id'] . ';');
-        return true;
-      }
-    }
-    return false;
-  }
-  
-  /**
-   * Removes a user from a group.
-   * @param int User ID
-   * @param int Group ID
-   * @return bool True on success, false on failure
-   * @todo put a little more error checking in...
-   */
-  
-  function remove_user_from_group($user_id, $group_id)
-  {
-    if ( !is_int($user_id) || !is_int($group_id) )
-      return false;
-    $this->sql('DELETE FROM '.table_prefix."group_members WHERE user_id=$user_id AND group_id=$group_id;");
-    return true;
-  }
-  
-  /**
-   * Checks the banlist to ensure that we're an allowed user. Doesn't return anything because it dies if the user is banned.
-   */
-   
-  function check_banlist()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $col_reason = ( $this->compat ) ? '\'No reason available (session manager is in compatibility mode)\' AS reason' : 'reason';
-    $remote_addr = ( strstr($_SERVER['REMOTE_ADDR'], ':') ) ? expand_ipv6_address($_SERVER['REMOTE_ADDR']) : $_SERVER['REMOTE_ADDR'];
-    
-    $banned = false;
-    if ( $this->user_logged_in )
-    {
-      // check by IP, email, and username
-      if ( ENANO_DBLAYER == 'MYSQL' )
-      {
-        $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
-              . "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
-              . "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value ) OR \n"
-              . "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
-              . "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' REGEXP ban_value ) OR \n"
-              . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
-              . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' REGEXP ban_value ) \n"
-              . "  ORDER BY ban_type ASC;";
-      }
-      else if ( ENANO_DBLAYER == 'PGSQL' )
-      {
-        $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
-              . "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
-              . "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value ) OR \n"
-              . "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
-              . "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' ~ ban_value ) OR \n"
-              . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
-              . "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' ~ ban_value ) \n"
-              . "  ORDER BY ban_type ASC;";
-      }
-      $q = $this->sql($sql);
-      if ( $db->numrows() > 0 )
-      {
-        while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
-        {
-          if ( $ban_type == BAN_IP && $is_regex != 1 )
-          {
-            // check range
-            $regexp = parse_ip_range_regex($ban_value);
-            if ( !$regexp )
-            {
-              continue;
-            }
-            if ( preg_match("/$regexp/", $remote_addr) )
-            {
-              $reason = $reason_temp;
-              $banned = true;
-            }
-          }
-          else
-          {
-            // User is banned
-            $banned = true;
-            $reason = $reason_temp;
-          }
-        }
-      }
-      $db->free_result();
-    }
-    else
-    {
-      // check by IP only
-      if ( ENANO_DBLAYER == 'MYSQL' )
-      {
-        $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
-                  ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
-                  ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value )
-                ORDER BY ban_type ASC;";
-      }
-      else if ( ENANO_DBLAYER == 'PGSQL' )
-      {
-        $sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
-                  ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
-                  ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value )
-                ORDER BY ban_type ASC;";
-      }
-      $q = $this->sql($sql);
-      if ( $db->numrows() > 0 )
-      {
-        while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
-        {
-          if ( $ban_type == BAN_IP && $is_regex != 1 )
-          {
-            // check range
-            $regexp = parse_ip_range_regex($ban_value);
-            if ( !$regexp )
-            {
-              die("bad regexp for $ban_value");
-              continue;
-            }
-            if ( preg_match("/$regexp/", $remote_addr) )
-            {
-              $reason = $reason_temp;
-              $banned = true;
-            }
-          }
-          else
-          {
-            // User is banned
-            $reason = $reason_temp;
-            $banned = true;
-          }
-        }
-      }
-      $db->free_result();
-    }
-    if ( $banned && !$this->on_critical_page(true) )
-    {
-      // This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it
-      die_semicritical($lang->get('user_ban_msg_title'), '<p>' . $lang->get('user_ban_msg_body') . '</p><div class="error-box"><b>' . $lang->get('user_ban_lbl_reason') . '</b><br />' . $reason . '</div>');
-      exit;
-    }
-  }
-  
-  # Registration
-  
-  /**
-   * Registers a user. This does not perform any type of login.
-   * @param string New user's username
-   * @param string This should be unencrypted.
-   * @param string E-mail address.
-   * @param string Optional, defaults to ''.
-   * @param bool Optional. If true, the account is not activated initially and an admin activation request is sent. The caller is responsible for sending the address info and notice.
-   */
-   
-  function create_user($username, $password, $email, $real_name = '', $coppa = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    // Initialize AES
-    $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-    
-    // Since we're recording IP addresses, make sure the user's IP is safe.
-    $ip =& $_SERVER['REMOTE_ADDR'];
-    if ( !is_valid_ip($ip) )
-      return 'Invalid IP';
-    
-    if ( !preg_match('#^'.$this->valid_username.'$#', $username) )
-      return $lang->get('user_reg_err_username_banned_chars');
-    
-    $username = str_replace('_', ' ', $username);
-    $user_orig = $username;
-    $username = $this->prepare_text($username);
-    $email = $this->prepare_text($email);
-    $real_name = $this->prepare_text($real_name);
-    
-    $nameclause = ( $real_name != '' ) ? ' OR real_name=\''.$real_name.'\'' : '';
-    $q = $this->sql('SELECT * FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=\''.strtolower($username).'\' OR email=\''.$email.'\''.$nameclause.';');
-    if($db->numrows() > 0)
-    {
-      $row = $db->fetchrow();
-      $str = 'user_reg_err_dupe';
-      
-      if ( $row['username'] == $username )
-      {
-        $str .= '_username';
-      }
-      if ( $row['email'] == $email )
-      {
-        $str .= '_email';
-      }
-      if ( $row['real_name'] == $real_name && $real_name != '' )
-      {
-        $str .= '_realname';
-      }
-      
-      return $lang->get($str);
-    }
-    
-    // Is the password strong enough?
-    if ( getConfig('pw_strength_enable') )
-    {
-      $min_score = intval( getConfig('pw_strength_minimum') );
-      $pass_score = password_score($password);
-      if ( $pass_score < $min_score )
-      {
-        return $lang->get('user_reg_err_password_too_weak');
-      }
-    }
-    
-    // Require the account to be activated?
-    switch(getConfig('account_activation'))
-    {
-      case 'none':
-      default:
-        $active = '1';
-        break;
-      case 'user':
-        $active = '0';
-        break;
-      case 'admin':
-        $active = '0';
-        break;
-    }
-    if ( $coppa )
-      $active = '0';
-    
-    $coppa_col = ( $coppa ) ? '1' : '0';
-    
-    // Generate a totally random activation key
-    $actkey = sha1 ( microtime() . mt_rand() );
+	
+	# Variables
+	
+	/**
+ 	* Whether we're logged in or not
+ 	* @var bool
+ 	*/
+ 	
+	var $user_logged_in = false;
+	
+	/**
+ 	* Our current low-privilege session key
+ 	* @var string
+ 	*/
+	
+	var $sid;
+	
+	/**
+ 	* Username of currently logged-in user, or IP address if not logged in
+ 	* @var string
+ 	*/
+	
+	var $username;
+	
+	/**
+ 	* User ID of currently logged-in user, or 1 if not logged in
+ 	* @var int
+ 	*/
+	
+	var $user_id = 1;
+	
+	/**
+ 	* Real name of currently logged-in user, or blank if not logged in
+ 	* @var string
+ 	*/
+	
+	var $real_name;
+	
+	/**
+ 	* E-mail address of currently logged-in user, or blank if not logged in
+ 	* @var string
+ 	*/
+	
+	var $email;
+	
+	/**
+ 	* List of "extra" user information fields (IM handles, etc.)
+ 	* @var array (associative)
+ 	*/
+	
+	var $user_extra;
+	
+	/**
+ 	* User level of current user
+ 	* USER_LEVEL_GUEST: guest
+ 	* USER_LEVEL_MEMBER: regular user
+ 	* USER_LEVEL_CHPREF: default - pseudo-level that allows changing password and e-mail address (requires re-authentication)
+ 	* USER_LEVEL_MOD: moderator
+ 	* USER_LEVEL_ADMIN: administrator
+ 	* @var int
+ 	*/
+	
+	var $user_level;
+	
+	/**
+ 	* High-privilege session key
+ 	* @var string or false if not running on high-level authentication
+ 	*/
+	
+	var $sid_super;
+	
+	/**
+ 	* The user's theme preference, defaults to $template->default_theme
+ 	* @var string
+ 	*/
+	
+	var $theme;
+	
+	/**
+ 	* The user's style preference, or style auto-detected based on theme if not logged in
+ 	* @var string
+ 	*/
+	
+	var $style;
+	
+	/**
+ 	* Signature of current user - appended to comments, etc.
+ 	* @var string
+ 	*/
+	
+	var $signature;
+	
+	/**
+ 	* UNIX timestamp of when we were registered, or 0 if not logged in
+ 	* @var int
+ 	*/
+	
+	var $reg_time;
+	
+	/**
+ 	* The number of unread private messages this user has.
+ 	* @var int
+ 	*/
+	
+	var $unread_pms = 0;
+	
+	/**
+ 	* AES key used to encrypt passwords and session key info.
+ 	* @var string
+ 	* @access private
+ 	*/
+ 	
+	protected $private_key;
+	
+	/**
+ 	* Regex that defines a valid username, minus the ^ and $, these are added later
+ 	* @var string
+ 	*/
+ 	
+	var $valid_username = '([^<>&\?\'"%\n\r\t\a\/]+)';
+	
+	/**
+ 	* The current user's user title. Defaults to NULL.
+ 	* @var string
+ 	*/
+	
+	var $user_title = null;
+ 	
+	/**
+ 	* What we're allowed to do as far as permissions go. This changes based on the value of the "auth" URI param.
+ 	* @var string
+ 	*/
+ 	
+	var $auth_level = 1;
+	
+	/**
+ 	* Preference for date formatting
+ 	* @var string
+ 	*/
+	
+	var $date_format = DATE_4;
+	
+	/**
+ 	* Preference for time formatting
+ 	* @var string
+ 	*/
+	
+	var $time_format = TIME_24_NS;
+	
+	/**
+ 	* State variable to track if a session timed out
+ 	* @var bool
+ 	*/
+	
+	var $sw_timed_out = false;
+	
+	/**
+ 	* Token appended to some important forms to prevent CSRF.
+ 	* @var string
+ 	*/
+	
+	var $csrf_token = false;
+	
+	/**
+ 	* Password change disabled, for auth plugins
+ 	* @var bool
+ 	*/
+	
+	var $password_change_disabled = false;
+	
+	/**
+ 	* Password change page URL + title, for auth plugins
+ 	* @var array
+ 	*/
+	
+	var $password_change_dest = array('url' => '', 'title' => '');
+	
+	/**
+ 	* Switch to track if we're started or not.
+ 	* @access private
+ 	* @var bool
+ 	*/
+ 	
+	var $started = false;
+	
+	/**
+ 	* Switch to control compatibility mode (for older Enano websites being upgraded)
+ 	* @access private
+ 	* @var bool
+ 	*/
+ 	
+	var $compat = false;
+	
+	/**
+ 	* Our list of permission types.
+ 	* @access private
+ 	* @var array
+ 	*/
+ 	
+	var $acl_types = Array();
+	
+	/**
+ 	* The list of descriptions for the permission types
+ 	* @var array
+ 	*/
+ 	
+	var $acl_descs = Array();
+	
+	/**
+ 	* A list of dependencies for ACL types.
+ 	* @var array
+ 	*/
+ 	
+	var $acl_deps = Array();
+	
+	/**
+ 	* Our tell-all list of permissions. Do not even try to change this.
+ 	* @access private
+ 	* @var array
+ 	*/
+ 	
+	var $perms = Array();
+	
+	/**
+ 	* A cache variable - saved after sitewide permissions are checked but before page-specific permissions.
+ 	* @var array
+ 	* @access private
+ 	*/
+	
+	var $acl_base_cache = Array();
+	
+	/**
+ 	* Stores the scope information for ACL types.
+ 	* @var array
+ 	* @access private
+ 	*/
+ 	
+	var $acl_scope = Array();
+	
+	/**
+ 	* Array to track which default permissions are being used
+ 	* @var array
+ 	* @access private
+ 	*/
+ 	
+	var $acl_defaults_used = Array();
+	
+	/**
+ 	* Array to track group membership.
+ 	* @var array
+ 	*/
+ 	
+	var $groups = Array();
+	
+	/**
+ 	* Associative array to track group modship.
+ 	* @var array
+ 	*/
+ 	
+	var $group_mod = Array();
+	
+	/**
+ 	* A constant array of user-level-to-rank default associations.
+ 	* @var array
+ 	*/
+	
+	var $level_rank_table = array(
+			USER_LEVEL_ADMIN  => RANK_ID_ADMIN,
+			USER_LEVEL_MOD    => RANK_ID_MOD,
+			USER_LEVEL_MEMBER => RANK_ID_MEMBER,
+			USER_LEVEL_CHPREF => RANK_ID_MEMBER,
+			USER_LEVEL_GUEST  => RANK_ID_GUEST
+		);
+	
+	/**
+ 	* A constant array that maps precedence constants to language strings
+ 	* @var array
+ 	*/
+	
+	var $acl_inherit_lang_table = array(
+			ACL_INHERIT_ENANO_DEFAULT   => 'acl_inherit_enano_default',
+			ACL_INHERIT_GLOBAL_EVERYONE => 'acl_inherit_global_everyone',
+			ACL_INHERIT_GLOBAL_GROUP    => 'acl_inherit_global_group',
+			ACL_INHERIT_GLOBAL_USER     => 'acl_inherit_global_user',
+			ACL_INHERIT_PG_EVERYONE     => 'acl_inherit_pg_everyone',
+			ACL_INHERIT_PG_GROUP        => 'acl_inherit_pg_group',
+			ACL_INHERIT_PG_USER         => 'acl_inherit_pg_user',
+			ACL_INHERIT_LOCAL_EVERYONE  => 'acl_inherit_local_everyone',
+			ACL_INHERIT_LOCAL_GROUP     => 'acl_inherit_local_group',
+			ACL_INHERIT_LOCAL_USER      => 'acl_inherit_local_user'
+		);
+	
+	# Basic functions
+ 	
+	/**
+ 	* Constructor.
+ 	*/
+ 	
+	function __construct()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( defined('IN_ENANO_INSTALL') && !defined('IN_ENANO_UPGRADE') )
+		{
+			@include(ENANO_ROOT.'/config.new.php');
+		}
+		else
+		{
+			@include(ENANO_ROOT.'/config.php');
+		}
+		
+		unset($dbhost, $dbname, $dbuser, $dbpasswd);
+		if(isset($crypto_key))
+		{
+			$this->private_key = $crypto_key;
+			$this->private_key = hexdecode($this->private_key);
+		}
+		else
+		{
+			if(is_writable(ENANO_ROOT.'/config.php'))
+			{
+				// Generate and stash a private key
+				// This should only happen during an automated silent gradual migration to the new encryption platform.
+				$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+				$this->private_key = $aes->gen_readymade_key();
+				
+				$config = file_get_contents(ENANO_ROOT.'/config.php');
+				if(!$config)
+				{
+					die('$session->__construct(): can\'t get the contents of config.php');
+				}
+				
+				$config = str_replace("?>", "\$crypto_key = '{$this->private_key}';\n?>", $config);
+				// And while we're at it...
+				$config = str_replace('MIDGET_INSTALLED', 'ENANO_INSTALLED', $config);
+				$fh = @fopen(ENANO_ROOT.'/config.php', 'w');
+				if ( !$fh ) 
+				{
+					die('$session->__construct(): Couldn\'t open config file for writing to store the private key, I tried to avoid something like this...');
+				}
+				
+				fwrite($fh, $config);
+				fclose($fh);
+			}
+			else
+			{
+				die_semicritical('Crypto error', '<p>No private key was found in the config file, and we can\'t generate one because we don\'t have write access to the config file. Please CHMOD config.php to 666 or 777 and reload this page.</p>');
+			}
+		}
+		// Check for compatibility mode
+		if(defined('IN_ENANO_INSTALL'))
+		{
+			$q = $db->sql_query('SELECT old_encryption FROM '.table_prefix.'users LIMIT 1;');
+			if(!$q)
+			{
+				$error = mysql_error();
+				if(strstr($error, "Unknown column 'old_encryption'"))
+					$this->compat = true;
+				else
+					$db->_die('This should never happen and is a bug - the only error that was supposed to happen here didn\'t happen. (sessions.php in constructor, during compat mode check)');
+			}
+			$db->free_result();
+		}
+	}
+	
+	/**
+ 	* PHP 4 compatible constructor. Deprecated in 1.1.x.
+ 	*/
+ 	
+	/*
+	function sessionManager()
+	{
+		$this->__construct();
+	}
+	*/
+	
+	/**
+ 	* Wrapper function to sanitize strings for MySQL and HTML
+ 	* @param string $text The text to sanitize
+ 	* @return string
+ 	*/
+	
+	function prepare_text($text)
+	{
+		global $db;
+		return $db->escape(htmlspecialchars($text));
+	}
+	
+	/**
+ 	* Makes a SQL query and handles error checking
+ 	* @param string $query The SQL query to make
+ 	* @return resource
+ 	*/
+	
+	function sql($query)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$result = $db->sql_query($query);
+		if(!$result)
+		{
+			$db->_die('The error seems to have occurred somewhere in the session management code.');
+		}
+		return $result;
+	}
+	
+	/**
+ 	* Returns true if we're currently on a page that shouldn't be blocked even if we have an inactive or banned account
+ 	* @param bool strict - if true, whitelist of pages is even stricter (Login, Logout and CSS only). if false (default), admin access is allowed, assuming other factors allow it
+ 	* @return bool
+ 	*/
+	
+	function on_critical_page($strict = false)
+	{
+		global $urlname;
+		list($page_id, $namespace) = RenderMan::strToPageID($urlname);
+		list($page_id) = explode('/', $page_id);
+		
+		if ( $strict )
+		{
+			return $namespace == 'Special' && in_array($page_id, array('CSS', 'Login', 'Logout', 'LangExportJSON', 'ActivateAccount'));
+		}
+		else
+		{
+			return $namespace == 'Admin' || ($namespace == 'Special' && in_array($page_id, array('CSS', 'Login', 'Logout', 'Administration', 'LangExportJSON', 'ActivateAccount')));
+		}
+	}
+	
+	# Session restoration and permissions
+	
+	/**
+ 	* Initializes the basic state of things, including most user prefs, login data, cookie stuff
+ 	*/
+	
+	function start()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $timezone;
+		if($this->started) return;
+		$this->started = true;
+		$user = false;
+		if ( isset($_COOKIE['sid']) )
+		{
+			if ( $this->compat )
+			{
+				$userdata = $this->compat_validate_session($_COOKIE['sid']);
+			}
+			else
+			{
+				$userdata = $this->validate_session($_COOKIE['sid']);
+			}
+			if ( is_array($userdata) )
+			{
+				$this->sid = $_COOKIE['sid'];
+				$this->user_logged_in = true;
+				$this->user_id =       intval($userdata['user_id']);
+				$this->username =      $userdata['username'];
+				$this->user_level =    intval($userdata['user_level']);
+				$this->real_name =     $userdata['real_name'];
+				$this->email =         $userdata['email'];
+				$this->unread_pms =    $userdata['num_pms'];
+				$this->user_title =    ( isset($userdata['user_title']) ) ? $userdata['user_title'] : null;
+				if(!$this->compat)
+				{
+					$this->theme =         $userdata['theme'];
+					$this->style =         $userdata['style'];
+					$this->signature =     $userdata['signature'];
+					$this->reg_time =      $userdata['reg_time'];
+				}
+				$this->auth_level =    USER_LEVEL_MEMBER;
+				// generate an anti-CSRF token
+				$this->csrf_token =    sha1($this->username . $this->sid . $this->user_id);
+				if(!isset($template->named_theme_list[$this->theme]))
+				{
+					if($this->compat || !is_object($template))
+					{
+						$this->theme = 'oxygen';
+						$this->style = 'bleu';
+					}
+					else
+					{
+						$this->theme = $template->default_theme;
+						$this->style = $template->default_style;
+					}
+				}
+				$user = true;
+				
+				// set timezone params
+				$GLOBALS['timezone'] = $userdata['user_timezone'];
+				$GLOBALS['dst_params'] = explode(';', $userdata['user_dst']);
+				foreach ( $GLOBALS['dst_params'] as &$parm )
+				{
+					if ( substr($parm, -1) != 'd' )
+						$parm = intval($parm);
+				}
+				
+				// Set language
+				if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
+				{
+					$lang_id = intval($userdata['user_lang']);
+					$lang = new Language($lang_id);
+					@setlocale(LC_ALL, $lang->lang_code);
+				}
+				
+				if(isset($_REQUEST['auth']) && !$this->sid_super)
+				{
+					// Now he thinks he's a moderator. Or maybe even an administrator. Let's find out if he's telling the truth.
+					if($this->compat)
+					{
+						$key = $_REQUEST['auth'];
+						$super = $this->compat_validate_session($key);
+					}
+					else
+					{
+						$key = $_REQUEST['auth'];
+						if ( !empty($key) && ( strlen($key) / 2 ) % 4 == 0 )
+						{
+							$super = $this->validate_session($key);
+						}
+					}
+					if(is_array(@$super))
+					{
+						$this->auth_level = intval($super['auth_level']);
+						$this->sid_super = $_REQUEST['auth'];
+					}
+				}
+			}
+		}
+		if(!$user)
+		{
+			//exit;
+			$this->register_guest_session();
+		}
+		if(!$this->compat)
+		{
+			// init groups
+			$q = $this->sql('SELECT g.group_name,g.group_id,m.is_mod FROM '.table_prefix.'groups AS g' . "\n"
+				. '  LEFT JOIN '.table_prefix.'group_members AS m' . "\n"
+				. '    ON g.group_id=m.group_id' . "\n"
+				. '  WHERE ( m.user_id='.$this->user_id.'' . "\n" 
+				. '    OR g.group_name=\'Everyone\')' . "\n"
+				. '    ' . ( /* quick hack for upgrade compatibility reasons */ enano_version() == '1.0RC1' ? '' : 'AND ( m.pending != 1 OR m.pending IS NULL )' ) . '' . "\n"
+				. '  ORDER BY group_id ASC;'); // The ORDER BY is to make sure "Everyone" comes first so the permissions can be overridden
+			if($row = $db->fetchrow())
+			{
+				do {
+					$this->groups[$row['group_id']] = $row['group_name'];
+					$this->group_mod[$row['group_id']] = ( intval($row['is_mod']) == 1 );
+				} while($row = $db->fetchrow());
+			}
+			else
+			{
+				die('No group info');
+			}
+			profiler_log('Fetched group memberships');
+		}
+		
+		// make sure we aren't banned
+		$this->check_banlist();
+		
+		// make sure the account is active
+		if ( !$this->compat && $this->user_logged_in && $userdata['account_active'] < 1 && !$this->on_critical_page() )
+		{
+			$this->show_inactive_error($userdata);
+		}
+		
+		// Printable page view? Probably the wrong place to control
+		// it but $template is pretty dumb, it will just about always
+		// do what you ask it to do, which isn't always what we want
+		if ( isset ( $_GET['printable'] ) )
+		{
+			$this->theme = 'printable';
+			$this->style = 'default';
+		}
+		
+		// setup theme ACLs
+		$template->process_theme_acls();
+		
+		profiler_log('Sessions started. Banlist and theme ACLs initialized');
+	}
+	
+	# Logins
+	
+	/**
+ 	* Attempts to perform a login using crypto functions
+ 	* @param string $username The username
+ 	* @param string $aes_data The encrypted password, hex-encoded
+ 	* @param string $aes_key The MD5 hash of the encryption key, hex-encoded
+ 	* @param string $challenge The 256-bit MD5 challenge string - first 128 bits should be the hash, the last 128 should be the challenge salt
+ 	* @param int $level The privilege level we're authenticating for, defaults to 0
+ 	* @param string $captcha_hash Optional. If we're locked out and the lockout policy is captcha, this should be the identifier for the code.
+ 	* @param string $captcha_code Optional. If we're locked out and the lockout policy is captcha, this should be the code the user entered.
+ 	* @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
+ 	* @param bool $lookup_key Optional. If true (default) this queries the database for the "real" encryption key. Else, uses what is given.
+ 	* @return string 'success' on success, or error string on failure
+ 	*/
+ 	
+	function login_with_crypto($username, $aes_data, $aes_key_id, $challenge, $level = USER_LEVEL_MEMBER, $captcha_hash = false, $captcha_code = false, $remember = false, $lookup_key = true)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Instanciate the Rijndael encryption object
+		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+		
+		// Fetch our decryption key
+		
+		if ( $lookup_key )
+		{
+			$aes_key = $this->fetch_public_key($aes_key_id);
+			if ( !$aes_key )
+			{
+				// It could be that our key cache is full. If it seems larger than 65KB, clear it
+				if ( strlen(getConfig('login_key_cache')) > 65000 )
+				{
+					setConfig('login_key_cache', '');
+					return array(
+						'success' => false,
+						'error' => 'key_not_found_cleared',
+						);
+				}
+				return array(
+					'success' => false,
+					'error' => 'key_not_found'
+					);
+			}
+		}
+		else
+		{
+			$aes_key =& $aes_key_id;
+		}
+		
+		// Convert the key to a binary string
+		$bin_key = hexdecode($aes_key);
+		
+		if(strlen($bin_key) != AES_BITS / 8)
+			return array(
+				'success' => false,
+				'error' => 'key_wrong_length'
+				);
+		
+		// Decrypt our password
+		$password = $aes->decrypt($aes_data, $bin_key, ENC_HEX);
+		
+		// Let the LoginAPI do the rest.
+		return $this->login_without_crypto($username, $password, false, $level, $captcha_hash, $captcha_code, $remember);
+	}
+	
+	/**
+ 	* Attempts to login without using crypto stuff, mainly for use when the other side doesn't like Javascript
+ 	* This method of authentication is inherently insecure, there's really nothing we can do about it except hope and pray that everyone moves to Firefox
+ 	* Technically it still uses crypto, but it only decrypts the password already stored, which is (obviously) required for authentication
+ 	* @param string $username The username
+ 	* @param string $password The password -OR- the MD5 hash of the password if $already_md5ed is true
+ 	* @param bool $already_md5ed This should be set to true if $password is an MD5 hash, and should be false if it's plaintext. Defaults to false.
+ 	* @param int $level The privilege level we're authenticating for, defaults to 0
+ 	* @param bool $remember Optional. If true, remembers the session for X days. Otherwise, assigns a short session. Defaults to false.
+ 	*/
+	
+	function login_without_crypto($username, $password, $already_md5ed = false, $level = USER_LEVEL_MEMBER, $remember = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( $already_md5ed )
+		{
+			// No longer supported
+			return array(
+					'mode' => 'error',
+					'error' => '$already_md5ed is no longer supported (set this parameter to false and make sure the password you send to $session->login_without_crypto() is not hashed)'
+				);
+		}
+		
+		// Replace underscores with spaces in username
+		// (Added in 1.0.2)
+		$username = str_replace('_', ' ', $username);
+		
+		// Perhaps we're upgrading Enano?
+		if($this->compat)
+		{
+			return $this->login_compat($username, md5($password), $level);
+		}
+		
+		// Instanciate the Rijndael encryption object
+		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+		
+		// Initialize our success switch
+		$success = false;
+		
+		// Retrieve the real password from the database
+		$username_db = $db->escape(strtolower($username));
+		$username_db_upper = $db->escape($username);
+		if ( !$db->sql_query('SELECT password,password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n"
+ 											. "  WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db' OR username = '$username_db_upper' );") )
+		{
+			$this->sql('SELECT password,\'\' AS password_salt,old_encryption,user_id,user_level,temp_password,temp_password_time FROM '.table_prefix."users\n"
+ 							. "  WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username_db' OR username = '$username_db_upper' );");
+		}
+		if ( $db->numrows() < 1 )
+		{
+			// This wasn't logged in <1.0.2, dunno how it slipped through
+			if ( $level > USER_LEVEL_MEMBER )
+				$this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES\n"
+ 									. '  (\'security\', \'admin_auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
+											. '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
+			else
+				$this->sql('INSERT INTO ' . table_prefix . "logs(log_type,action,time_id,date_string,author,edit_summary) VALUES\n"
+ 									. '  (\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', '
+											. '\''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
+			
+			// Do we also need to increment the lockout countdown?
+			if ( !defined('IN_ENANO_INSTALL') )
+				$lockout_data = $this->get_lockout_info();
+			else
+				$lockout_data = array(
+					'lockout_policy' => 'disable'
+					);
+			
+			if ( $lockout_data['policy'] != 'disable' && !defined('IN_ENANO_INSTALL') )
+			{
+				$ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
+				// increment fail count
+				$this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
+				$lockout_data['fails']++;
+				return array(
+						'success' => false,
+						'error' => ( $lockout_data['fails'] >= $lockout_data['threshold'] ) ? 'locked_out' : 'invalid_credentials',
+						'lockout_threshold' => $lockout_data['threshold'],
+						'lockout_duration' => ( $lockout_data['duration'] ),
+						'lockout_fails' => $lockout_data['fails'],
+						'lockout_policy' => $lockout_data['policy']
+					);
+			}
+			
+			return array(
+				'success' => false,
+				'error' => 'invalid_credentials'
+			);
+		}
+		$row = $db->fetchrow();
+		
+		// Check to see if we're logging in using a temporary password
+		
+		if((intval($row['temp_password_time']) + 3600*24) > time() )
+		{
+			$temp_pass = hmac_sha1($password, $row['password_salt']);
+			if( $temp_pass === $row['temp_password'] )
+			{
+				$code = $plugins->setHook('login_password_reset');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				
+				return array(
+						'success' => false,
+						'error' => 'valid_reset',
+						'redirect_url' => makeUrlComplete('Special', 'PasswordReset/stage2/' . $row['user_id'] . '/' . $this->pk_encrypt($password))
+					);
+			}
+		}
+		
+		if ( $row['old_encryption'] == 1 )
+		{
+			// The user's password is stored using the obsolete and insecure MD5 algorithm - we'll update the field with the new password
+			if(md5($password) === $row['password'])
+			{
+				if ( !defined('IN_ENANO_UPGRADE') )
+				{
+					$hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
+					$password_hmac = hmac_sha1($password, $hmac_secret);
+					$this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};");
+				}
+				$success = true;
+			}
+		}
+		else if ( $row['old_encryption'] == 2 || ( defined('ENANO_UPGRADE_USE_AES_PASSWORDS') ) && strlen($row['password']) != 40 )
+		{
+			// Our password field uses the 1.0RC1-1.1.5 encryption format
+			$real_pass = $aes->decrypt($row['password'], $this->private_key);
+			if($password === $real_pass)
+			{
+				if ( !defined('IN_ENANO_UPGRADE') )
+				{
+					$hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
+					$password_hmac = hmac_sha1($password, $hmac_secret);
+					$this->sql('UPDATE '.table_prefix."users SET password = '$password_hmac', password_salt = '$hmac_secret', old_encryption = 0 WHERE user_id={$row['user_id']};");
+				}
+				$success = true;
+			}
+		}
+		else
+		{
+			// Password uses HMAC-SHA1
+			$user_challenge = hmac_sha1($password, $row['password_salt']);
+			$password_hmac =& $row['password'];
+			if ( $user_challenge === $password_hmac )
+			{
+				$success = true;
+			}
+		}
+		if($success)
+		{
+			if((int)$level > (int)$row['user_level'])
+				return array(
+					'success' => false,
+					'error' => 'too_big_for_britches'
+				);
+			
+			// grant session
+			$sess = $this->register_session($row['user_id'], $username, ( isset($password_hmac) ? $password_hmac : $password ), $level, $remember);
+			
+			if($sess)
+			{
+				if($level > USER_LEVEL_MEMBER)
+					$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'admin_auth_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', ' . $row['user_id'] . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
+				else
+					$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary) VALUES(\'security\', \'auth_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', ' . $row['user_id'] . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
+				
+				$code = $plugins->setHook('login_success');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				
+				return array(
+					'success' => true
+					);
+			}
+			else
+				return array(
+					'success' => false,
+					'error' => 'backend_fail'
+				);
+		}
+		else
+		{
+			if($level > USER_LEVEL_MEMBER)
+				$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary,page_text) VALUES(\'security\', \'admin_auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', ' . intval($level) . ')');
+			else
+				$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'auth_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($username).'\', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\')');
+				
+			// Do we also need to increment the lockout countdown?
+			if ( !defined('IN_ENANO_INSTALL') && getConfig('lockout_policy', 'lockout') !== 'disable' )
+			{
+				$ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
+				// increment fail count
+				$this->sql('INSERT INTO '.table_prefix.'lockout(ipaddr, timestamp, action, username) VALUES(\'' . $ipaddr . '\', ' . time() . ', \'credential\', \'' . $db->escape($username) . '\');');
+			}
+				
+			return array(
+				'success' => false,
+				'error' => 'invalid_credentials'
+			);
+		}
+	}
+	
+	/**
+ 	* Attempts to log in using the old table structure and algorithm. This is for upgrades from old 1.0.x releases.
+ 	* @param string $username
+ 	* @param string $password This should be an MD5 hash
+ 	* @return string 'success' if successful, or error message on failure
+ 	*/
+	
+	function login_compat($username, $password, $level = 0)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$pass_hashed =& $password;
+		$this->sql('SELECT password,user_id,user_level FROM '.table_prefix.'users WHERE username=\''.$this->prepare_text($username).'\';');
+		if($db->numrows() < 1)
+			return 'The username and/or password is incorrect.';
+		$row = $db->fetchrow();
+		if($row['password'] == $password)
+		{
+			if((int)$level > (int)$row['user_level'])
+				return 'You are not authorized for this level of access.';
+			$sess = $this->register_session_compat(intval($row['user_id']), $username, $password, $level);
+			if($sess)
+				return 'success';
+			else
+				return 'Your login credentials were correct, but an internal error occured while registering the session key in the database.';
+		}
+		else
+		{
+			return 'The username and/or password is incorrect.';
+		}
+	}
+	
+	/**
+ 	* Registers a session key in the database. This function *ASSUMES* that the username and password have already been validated!
+ 	* Basically the session key is a hex-encoded cookie (encrypted with the site's private key) that says "u=[username];p=[sha1 of password];s=[unique key id]"
+ 	* @param int $user_id
+ 	* @param string $username
+ 	* @param string $password_hmac The HMAC of the user's password, right from the database
+ 	* @param int $level The level of access to grant, defaults to USER_LEVEL_MEMBER
+ 	* @param bool $remember Whether the session should be long-term (true) or not (false). Defaults to short-term.
+ 	* @return bool
+ 	*/
+ 	
+	function register_session($user_id, $username, $password_hmac, $level = USER_LEVEL_MEMBER, $remember = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Random key identifier
+		$salt = '';
+		for ( $i = 0; $i < 32; $i++ )
+		{
+			$salt .= chr(mt_rand(32, 126));
+		}
+		
+		// Session key
+		if ( defined('ENANO_UPGRADE_USE_AES_PASSWORDS') )
+		{
+			$session_key = $this->pk_encrypt("u=$username;p=" . sha1($password_hmac) . ";s=$salt");
+		}
+		else
+		{
+			$key_pieces = array($password_hmac);
+			$sk_mode = 'generate';
+			$code = $plugins->setHook('session_key_calc');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			$key_pieces = implode("\xFF", $key_pieces);
+			
+			$session_key = hmac_sha1($key_pieces, $salt);
+		}
+		
+		// Minimum level
+		$level = max(array($level, USER_LEVEL_MEMBER));
+		
+		// Type of key
+		$key_type = ( $level > USER_LEVEL_MEMBER ) ? SK_ELEV : ( $remember ? SK_LONG : SK_SHORT );
+		
+		// If we're registering an elevated-privilege key, it needs to be on GET
+		if($level > USER_LEVEL_MEMBER)
+		{
+			$this->sid_super = $session_key;
+			$_GET['auth'] = $session_key;
+		}
+		else
+		{
+			// Stash it in a cookie
+			// For now, make the cookie last forever, we can change this in 1.1.x
+			setcookie( 'sid', $session_key, time()+15552000, scriptPath.'/', null, $GLOBALS['is_https']);
+			$_COOKIE['sid'] = $session_key;
+			$this->sid = $session_key;
+		}
+		// $keyhash is stored in the database, this is for compatibility with the older DB structure
+		$keyhash = md5($session_key);
+		// Record the user's IP
+		$ip = $_SERVER['REMOTE_ADDR'];
+		if(!is_valid_ip($ip))
+			die('$session->register_session: Remote-Addr was spoofed');
+		// The time needs to be stashed to enforce the 15-minute limit on elevated session keys
+		$time = time();
+		
+		// Sanity check
+		if(!is_int($user_id))
+			die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
+		if(!is_int($level))
+			die(var_dump($level) . '<br />Somehow an SQL injection attempt crawled into our session registrar! (2)');
+		
+		// Update RAM
+		$this->user_id = $user_id;
+		$this->user_level = max(array($this->user_level, $level));
+		
+		// All done!
+		$query = $db->sql_query('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time, key_type) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.', ' . $key_type . ');');
+		if ( !$query && defined('IN_ENANO_UPGRADE') )
+			// we're trying to upgrade so the key_type column is probably missing - try it again without specifying the key type
+			$this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$keyhash.'\', \''.$db->escape($salt).'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
+			
+		return true;
+	}
+	
+	/**
+ 	* Identical to register_session in nature, but uses the old login/table structure. DO NOT use this except in the upgrade script under very controlled circumstances.
+ 	* @see sessionManager::register_session()
+ 	* @access private
+ 	*/
+	
+	function register_session_compat($user_id, $username, $password, $level = 0)
+	{
+		$salt = md5(microtime() . mt_rand());
+		$thekey = md5($password . $salt);
+		if($level > 0)
+		{
+			$this->sid_super = $thekey;
+		}
+		else
+		{
+			setcookie( 'sid', $thekey, time()+315360000, scriptPath.'/' );
+			$_COOKIE['sid'] = $thekey;
+		}
+		$ip = ip2hex($_SERVER['REMOTE_ADDR']);
+		if(!$ip)
+			die('$session->register_session: Remote-Addr was spoofed');
+		$time = time();
+		if(!is_int($user_id))
+			die('Somehow an SQL injection attempt crawled into our session registrar! (1)');
+		if(!is_int($level))
+			die('Somehow an SQL injection attempt crawled into our session registrar! (2)');
+		$query = $this->sql('INSERT INTO '.table_prefix.'session_keys(session_key, salt, user_id, auth_level, source_ip, time) VALUES(\''.$thekey.'\', \''.$salt.'\', '.$user_id.', '.$level.', \''.$ip.'\', '.$time.');');
+		return true;
+	}
+	
+	/**
+ 	* Tells us if we're locked out from logging in or not.
+ 	* @param reference will be filled with information regarding in-progress lockout
+ 	* @return bool True if locked out, false otherwise
+ 	*/
+	
+	function get_lockout_info()
+	{
+		global $db;
+		
+		// this has to be initialized to hide warnings
+		$lockdata = null;
+		
+		// Query database for lockout info
+		$locked_out = false;
+		$threshold = ( $_ = getConfig('lockout_threshold') ) ? intval($_) : 5;
+		$duration  = ( $_ = getConfig('lockout_duration') ) ? intval($_) : 15;
+		// convert to seconds
+		$duration  = $duration * 60;
+		// decide on policy
+		$policy = ( $x = getConfig('lockout_policy') && in_array(getConfig('lockout_policy'), array('lockout', 'disable', 'captcha')) ) ? getConfig('lockout_policy') : 'lockout';
+		if ( $policy != 'disable' )
+		{
+			// enabled; make decision
+			$ipaddr = $db->escape($_SERVER['REMOTE_ADDR']);
+			$timestamp_cutoff = time() - $duration;
+			$q = $this->sql('SELECT timestamp FROM ' . table_prefix . 'lockout WHERE timestamp > ' . $timestamp_cutoff . ' AND ipaddr = \'' . $ipaddr . '\' ORDER BY timestamp DESC;');
+			$fails = $db->numrows($q);
+			$row = $db->fetchrow($q);
+			$locked_out = ( $fails >= $threshold );
+			$lockdata = array(
+					'active' => $locked_out,
+					'threshold' => $threshold,
+					'duration' => ( $duration / 60 ),
+					'fails' => $fails,
+					'policy' => $policy,
+					'last_time' => $row['timestamp'],
+					'time_rem' => $locked_out ? ( $duration / 60 ) - round( ( time() - $row['timestamp'] ) / 60 ) : 0,
+					'captcha' => $policy == 'captcha' ? $this->make_captcha() : ''
+				);
+			$db->free_result();
+		}
+		else
+		{
+			// disabled; send back default dataset
+			$lockdata = array(
+				'active' => false,
+				'threshold' => $threshold,
+				'duration' => ( $duration / 60 ),
+				'fails' => 0,
+				'policy' => $policy,
+				'last_time' => 0,
+				'time_rem' => 0,
+				'captcha' => ''
+			);
+		}
+		return $lockdata;
+	}
+	
+	/**
+ 	* Creates/restores a guest session
+ 	* @todo implement real session management for guests
+ 	*/
+ 	
+	function register_guest_session()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		$this->username = $_SERVER['REMOTE_ADDR'];
+		$this->user_level = USER_LEVEL_GUEST;
+		if($this->compat || defined('IN_ENANO_INSTALL'))
+		{
+			$this->theme = 'oxygen';
+			$this->style = 'bleu';
+		}
+		else
+		{
+			$this->theme = ( isset($_GET['theme']) && isset($template->named_theme_list[$_GET['theme']])) ? $_GET['theme'] : $template->default_theme;
+			$this->style = ( isset($_GET['style']) && file_exists(ENANO_ROOT.'/themes/'.$this->theme . '/css/'.$_GET['style'].'.css' )) ? $_GET['style'] : preg_replace('/\.css$/', '', $template->named_theme_list[$this->theme]['default_style']);
+		}
+		$this->user_id = 1;
+		// This is a VERY special case we are allowing. It lets the installer create languages using the Enano API.
+		if ( !defined('ENANO_ALLOW_LOAD_NOLANG') )
+		{
+			$language = ( isset($_GET['lang']) && preg_match('/^[a-z0-9-_]+$/', @$_GET['lang']) ) ? $_GET['lang'] : intval(getConfig('default_language'));
+			$lang = new Language($language);
+			@setlocale(LC_ALL, $lang->lang_code);
+		}
+		// make a CSRF token
+		$this->csrf_token = hmac_sha1($_SERVER['REMOTE_ADDR'], sha1($this->private_key));
+	}
+	
+	/**
+ 	* Validates a session key, and returns the userdata associated with the key or false
+ 	* @param string $key The session key to validate
+ 	* @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides.
+ 	*/
+ 	
+	function validate_session($key)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		profiler_log("SessionManager: checking session: " . sha1($key));
+		
+		if ( strlen($key) > 48 )
+		{
+			return $this->validate_aes_session($key);
+		}
+		
+		profiler_log("SessionManager: checking session: " . $key);
+		
+		return $this->validate_session_shared($key, '');
+	}
+	
+	/**
+ 	* Validates an old-format AES session key. DO NOT USE THIS. Will return false if called outside of an upgrade.
+ 	* @param string Session key
+ 	* @return array
+ 	*/
+	
+	protected function validate_aes_session($key)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// No valid use except during upgrades
+		if ( !preg_match('/^upg-/', enano_version()) && !defined('IN_ENANO_UPGRADE') )
+			return false;
+		
+		$decrypted_key = $this->pk_decrypt($key);
+		if ( !$decrypted_key )
+		{
+			// die_semicritical('AES encryption error', '<p>Something went wrong during the AES decryption process.</p><pre>'.print_r($decrypted_key, true).'</pre>');
+			return false;
+		}
+		
+		$n = preg_match('/^u='.$this->valid_username.';p=([A-Fa-f0-9]+?);s=(.{32})$/', $decrypted_key, $keydata);
+		if($n < 1)
+		{
+			echo '(debug) $session->validate_session: Key does not match regex<br />Decrypted key: '.$decrypted_key;
+			return false;
+		}
+		$keyhash = md5($key);
+		$salt = $db->escape($keydata[3]);
+		
+		return $this->validate_session_shared($keyhash, $salt, true);
+	}
+	
+	/**
+ 	* Shared portion of session validation. Do not try to call this.
+ 	* @return array
+ 	* @access private
+ 	*/
+	
+	protected function validate_session_shared($key, $salt, $loose_call = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// using a normal call to $db->sql_query to avoid failing on errors here
+		$columns_select  = "u.user_id AS uid, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme,\n"
+											. "  u.style,u.signature, u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_title, k.salt, k.source_ip,\n"
+											. "  k.time, k.auth_level, k.key_type, COUNT(p.message_id) AS num_pms, u.user_timezone, u.user_dst, x.*";
+		
+		$columns_groupby = "u.user_id, u.username, u.password, u.password_salt, u.email, u.real_name, u.user_level, u.theme, u.style, u.signature,\n"
+											. "           u.reg_time, u.account_active, u.activation_key, u.user_lang, u.user_timezone, u.user_title, u.user_dst,\n"
+											. "           k.salt, k.source_ip, k.time, k.auth_level, k.key_type, x.user_id, x.user_aim, x.user_yahoo, x.user_msn,\n"
+											. "           x.user_xmpp, x.user_homepage, x.user_location, x.user_job, x.user_hobbies, x.email_public,\n"
+											. "           x.disable_js_fx, x.date_format, x.time_format";
+		
+		$joins = "  LEFT JOIN " . table_prefix . "users AS u\n"
+						. "    ON ( u.user_id=k.user_id )\n"
+						. "  LEFT JOIN " . table_prefix . "users_extra AS x\n"
+						. "    ON ( u.user_id=x.user_id OR x.user_id IS NULL )\n"
+						. "  LEFT JOIN " . table_prefix . "privmsgs AS p\n"
+						. "    ON ( p.message_to=u.username AND p.message_read=0 )\n";
+		if ( !$loose_call )
+		{
+			$key_md5 = md5($key);
+			$query = $db->sql_query("SELECT $columns_select\n"
+														. "FROM " . table_prefix . "session_keys AS k\n"
+														. $joins
+														. "  WHERE k.session_key='$key_md5'\n"
+														. "  GROUP BY $columns_groupby;");
+		}
+		else
+		{
+			$query = $db->sql_query("SELECT $columns_select\n"
+														. "FROM " . table_prefix . "session_keys AS k\n"
+														. $joins
+														. "  WHERE k.session_key='$key'\n"
+														. "    AND k.salt='$salt'\n"
+														. "  GROUP BY $columns_groupby;");
+		}
+		
+		if ( !$query && ( defined('IN_ENANO_INSTALL') or defined('IN_ENANO_UPGRADE') ) )
+		{
+			$key_md5 = $loose_call ? $key : md5($key);
+			$query = $this->sql('SELECT u.user_id AS uid,u.username,u.password,\'\' AS password_salt,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,COUNT(p.message_id) AS num_pms, 1440 AS user_timezone, \'0;0;0;0;60\' AS user_dst, ' . SK_SHORT . ' AS key_type, k.salt FROM '.table_prefix.'session_keys AS k
+ 														LEFT JOIN '.table_prefix.'users AS u
+ 															ON ( u.user_id=k.user_id )
+ 														LEFT JOIN '.table_prefix.'privmsgs AS p
+ 															ON ( p.message_to=u.username AND p.message_read=0 )
+ 														WHERE k.session_key=\''.$key_md5.'\'
+ 														GROUP BY u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,u.theme,u.style,u.signature,u.reg_time,u.account_active,u.activation_key,k.source_ip,k.time,k.auth_level,k.salt;');
+		}
+		else if ( !$query )
+		{
+			$db->_die();
+		}
+		if($db->numrows() < 1)
+		{
+			// echo '(debug) $session->validate_session: Key was not found in database: ' . $key_md5 . '<br />';
+			return false;
+		}
+		$row = $db->fetchrow();
+		profiler_log("SessionManager: session check: selected and fetched results");
+		
+		$row['user_id'] =& $row['uid'];
+		$ip = $_SERVER['REMOTE_ADDR'];
+		if($row['auth_level'] > $row['user_level'])
+		{
+			// Failed authorization check
+			// echo '(debug) $session->validate_session: access to this auth level denied<br />';
+			return false;
+		}
+		if($ip != $row['source_ip'])
+		{
+			// Special exception for 1.1.x upgrade - the 1.1.3 upgrade changes the size of the column and this is what validate_session
+			// expects, but if the column size hasn't changed yet just check the first 10 digits of the IP.
+			$fail = true;
+			if ( defined('IN_ENANO_UPGRADE') )
+			{
+				if ( substr($ip, 0, 10) == substr($row['source_ip'], 0, 10) )
+					$fail = false;
+			}
+			// Failed IP address check
+			// echo '(debug) $session->validate_session: IP address mismatch<br />';
+			if ( $fail )
+				return false;
+		}
+		
+		// $loose_call is turned on only from validate_aes_session
+		if ( !$loose_call )
+		{
+			$key_pieces = array($row['password']);
+			$user_id =& $row['uid'];
+			$sk_mode = 'validate';
+			$code = $plugins->setHook('session_key_calc');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			$key_pieces = implode("\xFF", $key_pieces);
+			$correct_key = hexdecode(hmac_sha1($key_pieces, $row['salt']));
+			$user_key = hexdecode($key);
+			if ( $correct_key !== $user_key || !is_string($user_key) )
+			{
+				return false;
+			}
+		}
+		else
+		{
+			// if this is a "loose call", this only works once (during the final upgrade stage). Destroy the contents of session_keys.
+			if ( $row['auth_level'] == USER_LEVEL_ADMIN && preg_match('/^upg-/', enano_version()) )
+				$this->sql('DELETE FROM ' . table_prefix . "session_keys;");
+		}
+		
+		// timestamp check
+		switch ( $row['key_type'] )
+		{
+			case SK_SHORT:
+				$time_now = time();
+				$time_key = $row['time'] + ( 60 * intval(getConfig('session_short', '720')) );
+				if ( $time_now > $time_key )
+				{
+					// Session timed out
+					return false;
+				}
+				break;
+			case SK_LONG:
+				if ( intval(getConfig('session_remember_time', '0')) === 0 )
+				{
+					// sessions last infinitely, timestamp validation is therefore successful
+					break;
+				}
+				$time_now = time();
+				$time_key = $row['time'] + ( 86400 * intval(getConfig('session_remember_time', '30')) );
+				if ( $time_now > $time_key )
+				{
+					// Session timed out
+					return false;
+				}
+				break;
+			case SK_ELEV:
+				$time_now = time();
+				$time_key = $row['time'] + 900;
+				if($time_now > $time_key && $row['auth_level'] > USER_LEVEL_MEMBER)
+				{
+					// Session timed out
+					// echo '(debug) $session->validate_session: super session timed out<br />';
+					$this->sw_timed_out = true;
+					return false;
+				}
+				break;
+		}
+				
+		// If this is an elevated-access or short-term session key, update the time
+		if( $row['key_type'] == SK_ELEV || $row['key_type'] == SK_SHORT )
+		{
+			$this->sql('UPDATE '.table_prefix.'session_keys SET time='.time().' WHERE session_key=\''.md5($key).'\';');
+		}
+		
+		$user_extra = array();
+		foreach ( array('user_aim', 'user_yahoo', 'user_msn', 'user_xmpp', 'user_homepage', 'user_location', 'user_job', 'user_hobbies', 'email_public', 'disable_js_fx') as $column )
+		{
+			if ( isset($row[$column]) )
+				$user_extra[$column] = $row[$column];
+			else
+				$user_extra[$column] = '';
+		}
+		
+		if ( isset($row['date_format']) )
+			$this->date_format = $row['date_format'];
+		if ( isset($row['time_format']) )
+			$this->time_format = $row['time_format'];
+		
+		$this->user_extra = $user_extra;
+		// Leave the rest to PHP's automatic garbage collector ;-)
+		
+		$row['password'] = '';
+		$row['user_timezone'] = intval($row['user_timezone']) - 1440;
+		
+		profiler_log("SessionManager: finished session check");
+		
+		return $row;
+	}
+	
+	/**
+ 	* Validates a session key, and returns the userdata associated with the key or false. Optimized for compatibility with the old MD5-based auth system.
+ 	* @param string $key The session key to validate
+ 	* @return array Keys are 'user_id', 'username', 'email', 'real_name', 'user_level', 'theme', 'style', 'signature', 'reg_time', 'account_active', 'activation_key', and 'auth_level' or bool false if validation failed. The key 'auth_level' is the maximum authorization level that this key provides.
+ 	*/
+ 	
+	function compat_validate_session($key)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$key = $db->escape($key);
+		
+		$query = $this->sql('SELECT u.user_id,u.username,u.password,u.email,u.real_name,u.user_level,k.source_ip,k.salt,k.time,k.auth_level,1440 AS user_timezone FROM '.table_prefix.'session_keys AS k
+ 													LEFT JOIN '.table_prefix.'users AS u
+ 														ON u.user_id=k.user_id
+ 													WHERE k.session_key=\''.$key.'\';');
+		if($db->numrows() < 1)
+		{
+			// echo '(debug) $session->validate_session: Key '.$key.' was not found in database<br />';
+			return false;
+		}
+		$row = $db->fetchrow();
+		$ip = ip2hex($_SERVER['REMOTE_ADDR']);
+		if($row['auth_level'] > $row['user_level'])
+		{
+			// Failed authorization check
+			// echo '(debug) $session->validate_session: user not authorized for this access level';
+			return false;
+		}
+		if($ip != $row['source_ip'])
+		{
+			// Failed IP address check
+			// echo '(debug) $session->validate_session: IP address mismatch; IP in table: '.$row['source_ip'].'; reported IP: '.$ip.'';
+			return false;
+		}
+		
+		// Do the password validation
+		$real_key = md5($row['password'] . $row['salt']);
+		
+		//die('<pre>'.print_r($keydata, true).'</pre>');
+		if($real_key != $key)
+		{
+			// Failed password check
+			// echo '(debug) $session->validate_session: supplied password is wrong<br />Real key: '.$real_key.'<br />User key: '.$key;
+			return false;
+		}
+		
+		$time_now = time();
+		$time_key = $row['time'] + 900;
+		if($time_now > $time_key && $row['auth_level'] >= 1)
+		{
+			$this->sw_timed_out = true;
+			// Session timed out
+			// echo '(debug) $session->validate_session: super session timed out<br />';
+			return false;
+		}
+		
+		$row['user_timezone'] = intval($row['user_timezone']) - 1440;
+		
+		return $row;
+	}
+ 	
+	/**
+ 	* Demotes us to one less than the specified auth level. AKA destroys elevated authentication and/or logs out the user, depending on $level
+ 	* @param int $level How low we should go - USER_LEVEL_MEMBER means demote to USER_LEVEL_GUEST, and anything more powerful than USER_LEVEL_MEMBER means demote to USER_LEVEL_MEMBER
+ 	* @return string 'success' if successful, or error on failure
+ 	*/
+ 	
+	function logout($level = USER_LEVEL_MEMBER)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$ou = $this->username;
+		$oid = $this->user_id;
+		if($level > USER_LEVEL_MEMBER)
+		{
+			$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+			if(!$this->user_logged_in || $this->auth_level < ( USER_LEVEL_MEMBER + 1))
+			{
+				return 'success';
+			}
+			// Destroy elevated privileges
+			$keyhash = md5($this->sid_super);
+			$this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.$keyhash.'\' AND user_id=\'' . $this->user_id . '\';');
+			$this->sid_super = false;
+			$this->auth_level = USER_LEVEL_MEMBER;
+		}
+		else
+		{
+			if($this->user_logged_in)
+			{
+				$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+				// Completely destroy our session
+				if($this->auth_level > USER_LEVEL_MEMBER)
+				{
+					$this->logout(USER_LEVEL_ADMIN);
+				}
+				$this->sql('DELETE FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($this->sid).'\';');
+				setcookie( 'sid', '', time()-(3600*24), scriptPath.'/' );
+			}
+		}
+		$code = $plugins->setHook('logout_success'); // , Array('level'=>$level,'old_username'=>$ou,'old_user_id'=>$oid));
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		return 'success';
+	}
+	
+	# Miscellaneous stuff
+	
+	/**
+ 	* Alerts the user that their account is inactive, and tells them appropriate steps to remedy the situation. Halts execution.
+ 	* @param array Return from validate_session()
+ 	*/
+	
+	function show_inactive_error($userdata)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		global $title;
+		$paths->init($title);
+		
+		$language = intval(getConfig('default_language'));
+		$lang = new Language($language);
+		@setlocale(LC_ALL, $lang->lang_code);
+		
+		$a = getConfig('account_activation');
+		switch($a)
+		{
+			case 'none':
+			default:
+				$solution = $lang->get('user_login_noact_solution_none');
+				break;
+			case 'user':
+				$solution = $lang->get('user_login_noact_solution_user');
+				break;
+			case 'admin':
+				$solution = $lang->get('user_login_noact_solution_admin');
+				break;
+		}
+		
+		// admin activation request opportunity
+		$q = $db->sql_query('SELECT 1 FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\' AND edit_summary=\'' . $db->escape($userdata['username']) . '\';');
+		if ( !$q )
+			$db->_die();
+		
+		$can_request = ( $db->numrows() < 1 );
+		$db->free_result();
+		
+		if ( isset($_POST['logout']) )
+		{
+			$this->sid = $_COOKIE['sid'];
+			$this->user_logged_in = true;
+			$this->user_id =       intval($userdata['user_id']);
+			$this->username =      $userdata['username'];
+			$this->auth_level =    USER_LEVEL_MEMBER;
+			$this->user_level =    USER_LEVEL_MEMBER;
+			$this->logout();
+			redirect(scriptPath . '/', $lang->get('user_login_noact_msg_logout_success_title'), $lang->get('user_login_noact_msg_logout_success_body'), 5);
+		}
+		
+		if ( $can_request && !isset($_POST['activation_request']) )
+		{
+			$form = '<p>' . $lang->get('user_login_noact_msg_ask_admins') . '</p>
+ 							<form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
+ 								<p><input type="submit" name="activation_request" value="' . $lang->get('user_login_noact_btn_request_activation') . '" /> <input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
+ 							</form>';
+		}
+		else
+		{
+			if ( $can_request && isset($_POST['activation_request']) )
+			{
+				$this->admin_activation_request($userdata['username']);
+				$form = '<p>' . $lang->get('user_login_noact_msg_admins_just_asked') . '</p>
+ 								<form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
+ 									<p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
+ 								</form>';
+			}
+			else
+			{
+				$form = '<p>' . $lang->get('user_login_noact_msg_admins_asked') . '</p>
+ 								<form action="' . makeUrlNS('System', 'ActivateStub') . '" method="post">
+ 									<p><input type="submit" name="logout" value="' . $lang->get('user_login_noact_btn_log_out') . '" /></p>
+ 								</form>';
+			}
+		}
+		
+		global $output;
+		$output = new Output_HTML();
+		$output->set_title($lang->get('user_login_noact_title'));
+		die_friendly($lang->get('user_login_noact_title'), '<p>' . $lang->get('user_login_noact_msg_intro') . ' '.$solution.'</p>' . $form);
+	}
+	
+	/**
+ 	* Appends the high-privilege session key to the URL if we are authorized to do high-privilege stuff
+ 	* @param string $url The URL to add session data to
+ 	* @return string
+ 	*/
+	
+	function append_sid($url)
+	{
+		$sep = ( strstr($url, '?') ) ? '&' : '?';
+		if ( $this->sid_super )
+		{
+			$url = $url . $sep . 'auth=' . urlencode($this->sid_super);
+			// echo($this->sid_super.'<br/>');
+		}
+		return $url;
+	}
+	
+	/**
+ 	* Prevent the user from changing their password. Authentication plugins may call this to enforce single sign-on.
+ 	* @param string URL to page where the user may change their password
+ 	* @param string Title of the page where the user may change their password
+ 	* @return null
+ 	*/
+	
+	function disable_password_change($change_url = false, $change_title = false)
+	{
+		if ( $this->password_change_disabled )
+		{
+			// don't allow calling twice. if we have two plugins doing this, somebody is bad at configuring websites.
+			return false;
+		}
+		
+		if ( is_string($change_url) && is_string($change_title) )
+		{
+			$this->password_change_dest = array(
+					'url' => $change_url,
+					'title' => $change_title
+				);
+		}
+		else
+		{
+			$this->password_change_dest = array(
+					'url' => false,
+					'title' => false
+				);
+		}
+		
+		$this->password_change_disabled = true;
+	}
+	
+	/**
+ 	* Grabs the user's password MD5 - NOW DEPRECATED AND DISABLED.
+ 	* @return bool false
+ 	*/
+ 	
+	function grab_password_hash()
+	{
+		return false;
+	}
+	
+	/**
+ 	* Destroys the user's password MD5 in memory
+ 	*/
+	
+	function disallow_password_grab()
+	{
+		$this->password_hash = false;
+		return false;
+	}
+	
+	/**
+ 	* Generates an AES key and stashes it in the database
+ 	* @return string Hex-encoded AES key
+ 	*/
+ 	
+	function rijndael_genkey()
+	{
+		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+		$key = $aes->gen_readymade_key();
+		$keys = getConfig('login_key_cache');
+		if(is_string($keys))
+			$keys .= $key;
+		else
+			$keys = $key;
+		setConfig('login_key_cache', $keys);
+		return $key;
+	}
+	
+	/**
+ 	* Generate a totally random 128-bit value for MD5 challenges
+ 	* @return string
+ 	*/
+ 	
+	function dss_rand()
+	{
+		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+		$random = $aes->randkey(128);
+		unset($aes);
+		return md5(microtime() . $random);
+	}
+	
+	/**
+ 	* Fetch a cached login public key using the MD5sum as an identifier. Each key can only be fetched once before it is destroyed.
+ 	* @param string $md5 The MD5 sum of the key
+ 	* @return string, or bool false on failure
+ 	*/
+ 	
+	function fetch_public_key($md5)
+	{
+		$keys = getConfig('login_key_cache');
+		$keys = enano_str_split($keys, AES_BITS / 4);
+		
+		foreach($keys as $i => $k)
+		{
+			if(md5($k) == $md5)
+			{
+				unset($keys[$i]);
+				if(count($keys) > 0)
+				{
+					if ( strlen(getConfig('login_key_cache') ) > 64000 )
+					{
+						// This should only need to be done once every month or so for an average-size site
+						setConfig('login_key_cache', '');
+					}
+					else
+					{
+						$keys = implode('', array_values($keys));
+						setConfig('login_key_cache', $keys);
+					}
+				}
+				else
+				{
+					setConfig('login_key_cache', '');
+				}
+				return $k;
+			}
+		}
+		// Couldn't find the key...
+		return false;
+	}
+	
+	/**
+ 	* Adds a user to a group.
+ 	* @param int User ID
+ 	* @param int Group ID
+ 	* @param bool Group moderator - defaults to false
+ 	* @return bool True on success, false on failure
+ 	*/
+	
+	function add_user_to_group($user_id, $group_id, $is_mod = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Validation
+		if ( !is_int($user_id) || !is_int($group_id) || !is_bool($is_mod) )
+			return false;
+		if ( $user_id < 1 || $group_id < 1 )
+			return false;
+		
+		$mod_switch = ( $is_mod ) ? '1' : '0';
+		$q = $this->sql('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $user_id . ' AND group_id=' . $group_id . ';');
+		if ( !$q )
+			$db->_die();
+		if ( $db->numrows() < 1 )
+		{
+			// User is not in group
+			$this->sql('INSERT INTO '.table_prefix.'group_members(user_id,group_id,is_mod) VALUES(' . $user_id . ', ' . $group_id . ', ' . $mod_switch . ');');
+			return true;
+		}
+		else
+		{
+			$row = $db->fetchrow();
+			// Update modship status
+			if ( strval($row['is_mod']) == $mod_switch )
+			{
+				// Modship unchanged
+				return true;
+			}
+			else
+			{
+				// Modship changed
+				$this->sql('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod_switch . ' WHERE member_id=' . $row['member_id'] . ';');
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/**
+ 	* Removes a user from a group.
+ 	* @param int User ID
+ 	* @param int Group ID
+ 	* @return bool True on success, false on failure
+ 	* @todo put a little more error checking in...
+ 	*/
+	
+	function remove_user_from_group($user_id, $group_id)
+	{
+		if ( !is_int($user_id) || !is_int($group_id) )
+			return false;
+		$this->sql('DELETE FROM '.table_prefix."group_members WHERE user_id=$user_id AND group_id=$group_id;");
+		return true;
+	}
+	
+	/**
+ 	* Checks the banlist to ensure that we're an allowed user. Doesn't return anything because it dies if the user is banned.
+ 	*/
+ 	
+	function check_banlist()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$col_reason = ( $this->compat ) ? '\'No reason available (session manager is in compatibility mode)\' AS reason' : 'reason';
+		$remote_addr = ( strstr($_SERVER['REMOTE_ADDR'], ':') ) ? expand_ipv6_address($_SERVER['REMOTE_ADDR']) : $_SERVER['REMOTE_ADDR'];
+		
+		$banned = false;
+		if ( $this->user_logged_in )
+		{
+			// check by IP, email, and username
+			if ( ENANO_DBLAYER == 'MYSQL' )
+			{
+				$sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
+							. "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
+							. "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value ) OR \n"
+							. "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
+							. "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' REGEXP ban_value ) OR \n"
+							. "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
+							. "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' REGEXP ban_value ) \n"
+							. "  ORDER BY ban_type ASC;";
+			}
+			else if ( ENANO_DBLAYER == 'PGSQL' )
+			{
+				$sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE \n"
+							. "    ( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR \n"
+							. "    ( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value ) OR \n"
+							. "    ( ban_type = " . BAN_USER  . " AND is_regex = 0 AND ban_value = '{$this->username}' ) OR \n"
+							. "    ( ban_type = " . BAN_USER  . " AND is_regex = 1 AND '{$this->username}' ~ ban_value ) OR \n"
+							. "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 0 AND ban_value = '{$this->email}' ) OR \n"
+							. "    ( ban_type = " . BAN_EMAIL . " AND is_regex = 1 AND '{$this->email}' ~ ban_value ) \n"
+							. "  ORDER BY ban_type ASC;";
+			}
+			$q = $this->sql($sql);
+			if ( $db->numrows() > 0 )
+			{
+				while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
+				{
+					if ( $ban_type == BAN_IP && $is_regex != 1 )
+					{
+						// check range
+						$regexp = parse_ip_range_regex($ban_value);
+						if ( !$regexp )
+						{
+							continue;
+						}
+						if ( preg_match("/$regexp/", $remote_addr) )
+						{
+							$reason = $reason_temp;
+							$banned = true;
+						}
+					}
+					else
+					{
+						// User is banned
+						$banned = true;
+						$reason = $reason_temp;
+					}
+				}
+			}
+			$db->free_result();
+		}
+		else
+		{
+			// check by IP only
+			if ( ENANO_DBLAYER == 'MYSQL' )
+			{
+				$sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
+									( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
+									( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' REGEXP ban_value )
+								ORDER BY ban_type ASC;";
+			}
+			else if ( ENANO_DBLAYER == 'PGSQL' )
+			{
+				$sql = "SELECT $col_reason, ban_value, ban_type, is_regex FROM " . table_prefix . "banlist WHERE
+									( ban_type = " . BAN_IP    . " AND is_regex = 0 ) OR
+									( ban_type = " . BAN_IP    . " AND is_regex = 1 AND '{$_SERVER['REMOTE_ADDR']}' ~ ban_value )
+								ORDER BY ban_type ASC;";
+			}
+			$q = $this->sql($sql);
+			if ( $db->numrows() > 0 )
+			{
+				while ( list($reason_temp, $ban_value, $ban_type, $is_regex) = $db->fetchrow_num() )
+				{
+					if ( $ban_type == BAN_IP && $is_regex != 1 )
+					{
+						// check range
+						$regexp = parse_ip_range_regex($ban_value);
+						if ( !$regexp )
+						{
+							die("bad regexp for $ban_value");
+							continue;
+						}
+						if ( preg_match("/$regexp/", $remote_addr) )
+						{
+							$reason = $reason_temp;
+							$banned = true;
+						}
+					}
+					else
+					{
+						// User is banned
+						$reason = $reason_temp;
+						$banned = true;
+					}
+				}
+			}
+			$db->free_result();
+		}
+		if ( $banned && !$this->on_critical_page(true) )
+		{
+			// This guy is banned - kill the session, kill the database connection, bail out, and be pretty about it
+			die_semicritical($lang->get('user_ban_msg_title'), '<p>' . $lang->get('user_ban_msg_body') . '</p><div class="error-box"><b>' . $lang->get('user_ban_lbl_reason') . '</b><br />' . $reason . '</div>');
+			exit;
+		}
+	}
+	
+	# Registration
+	
+	/**
+ 	* Registers a user. This does not perform any type of login.
+ 	* @param string New user's username
+ 	* @param string This should be unencrypted.
+ 	* @param string E-mail address.
+ 	* @param string Optional, defaults to ''.
+ 	* @param bool Optional. If true, the account is not activated initially and an admin activation request is sent. The caller is responsible for sending the address info and notice.
+ 	*/
+ 	
+	function create_user($username, $password, $email, $real_name = '', $coppa = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		// Initialize AES
+		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+		
+		// Since we're recording IP addresses, make sure the user's IP is safe.
+		$ip =& $_SERVER['REMOTE_ADDR'];
+		if ( !is_valid_ip($ip) )
+			return 'Invalid IP';
+		
+		if ( !preg_match('#^'.$this->valid_username.'$#', $username) )
+			return $lang->get('user_reg_err_username_banned_chars');
+		
+		$username = str_replace('_', ' ', $username);
+		$user_orig = $username;
+		$username = $this->prepare_text($username);
+		$email = $this->prepare_text($email);
+		$real_name = $this->prepare_text($real_name);
+		
+		$nameclause = ( $real_name != '' ) ? ' OR real_name=\''.$real_name.'\'' : '';
+		$q = $this->sql('SELECT * FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=\''.strtolower($username).'\' OR email=\''.$email.'\''.$nameclause.';');
+		if($db->numrows() > 0)
+		{
+			$row = $db->fetchrow();
+			$str = 'user_reg_err_dupe';
+			
+			if ( $row['username'] == $username )
+			{
+				$str .= '_username';
+			}
+			if ( $row['email'] == $email )
+			{
+				$str .= '_email';
+			}
+			if ( $row['real_name'] == $real_name && $real_name != '' )
+			{
+				$str .= '_realname';
+			}
+			
+			return $lang->get($str);
+		}
+		
+		// Is the password strong enough?
+		if ( getConfig('pw_strength_enable') )
+		{
+			$min_score = intval( getConfig('pw_strength_minimum') );
+			$pass_score = password_score($password);
+			if ( $pass_score < $min_score )
+			{
+				return $lang->get('user_reg_err_password_too_weak');
+			}
+		}
+		
+		// Require the account to be activated?
+		switch(getConfig('account_activation'))
+		{
+			case 'none':
+			default:
+				$active = '1';
+				break;
+			case 'user':
+				$active = '0';
+				break;
+			case 'admin':
+				$active = '0';
+				break;
+		}
+		if ( $coppa )
+			$active = '0';
+		
+		$coppa_col = ( $coppa ) ? '1' : '0';
+		
+		// Generate a totally random activation key
+		$actkey = sha1 ( microtime() . mt_rand() );
 
-    // We good, create the user
-    $this->sql('INSERT INTO ' . table_prefix . "users ( username, email, real_name, theme, style, reg_time, account_active, activation_key, user_level, user_coppa,\n"
-             . "                                        user_registration_ip, user_lang, user_has_avatar, avatar_type ) VALUES\n"
-             . "  ( '$username', '$email', '$real_name', '$template->default_theme', '$template->default_style', " . time() . ", $active, '$actkey', \n"
-             . "    " . USER_LEVEL_CHPREF . ", $coppa_col, '$ip', $lang->lang_id, 0, 'png' );");
-    
-    // Get user ID and create users_extra entry
-    $q = $this->sql('SELECT user_id FROM '.table_prefix."users WHERE username='$username';");
-    if ( $db->numrows() > 0 )
-    {
-      list($user_id) = $db->fetchrow_num();
-      $db->free_result();
-      
-      $this->sql('INSERT INTO '.table_prefix.'users_extra(user_id) VALUES(' . $user_id . ');');
-    }
-    
-    // Set the password
-    $this->set_password($user_id, $password);
-    
-    // Config option added, 1.1.5
-    if ( getConfig('userpage_grant_acl', '1') == '1' )             
-    {
-      // Grant edit and very limited mod access to the userpage
-      $acl_data = array(
-          'read' => AUTH_ALLOW,
-          'view_source' => AUTH_ALLOW,
-          'edit_page' => AUTH_ALLOW,
-          'post_comments' => AUTH_ALLOW,
-          'edit_comments' => AUTH_ALLOW, // only allows editing own comments
-          'history_view' => AUTH_ALLOW,
-          'history_rollback' => AUTH_ALLOW,
-          'rename' => AUTH_ALLOW,
-          'delete_page' => AUTH_ALLOW,
-          'tag_create' => AUTH_ALLOW,
-          'tag_delete_own' => AUTH_ALLOW,
-          'tag_delete_other' => AUTH_ALLOW,
-          'edit_cat' => AUTH_ALLOW,
-          'create_page' => AUTH_ALLOW
-        );
-      $acl_data = $db->escape($this->perm_to_string($acl_data));
-      $userpage = $db->escape(sanitize_page_id($user_orig));
-      $cols = "target_type, target_id, page_id, namespace, rules";
-      $vals = ACL_TYPE_USER . ", $user_id, '$userpage', 'User', '$acl_data'";
-      $q = "INSERT INTO ".table_prefix."acl($cols) VALUES($vals);";
-      $this->sql($q);
-    }
-    
-    // Require the account to be activated?
-    if ( $coppa )
-    {
-      $this->admin_activation_request($user_orig);
-      $this->send_coppa_mail($user_orig, $email);
-    }
-    else
-    {
-      switch(getConfig('account_activation'))
-      {
-        case 'none':
-        default:
-          break;
-        case 'user':
-          $a = $this->send_activation_mail($user_orig);
-          if(!$a)
-          {
-            $this->admin_activation_request($user_orig);
-            return $lang->get('user_reg_err_actmail_failed') . ' ' . $a;
-          }
-          break;
-        case 'admin':
-          $this->admin_activation_request($user_orig);
-          break;
-      }
-    }
-    
-    // Leave some data behind for the hook
-    $code = $plugins->setHook('user_registered');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    return 'success';
-  }
-  
-  /**
-   * Attempts to send an e-mail to the specified user with activation instructions.
-   * @param string $u The usernamd of the user requesting activation
-   * @return bool true on success, false on failure
-   */
-   
-  function send_activation_mail($u, $actkey = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    $q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
-    $r = $db->fetchrow();
-    if ( empty($r['email']) )
-      $db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
-    
-    $aklink = makeUrlComplete('Special', 'ActivateAccount/'.str_replace(' ', '_', $u).'/'. ( ( is_string($actkey) ) ? $actkey : $r['activation_key'] ) );
-    $message = $lang->get('user_reg_activation_email', array(
-        'activation_link' => $aklink,
-        'username' => $u
-      ));
-      
-    if ( getConfig('smtp_enabled') == '1' )
-    {
-      $result = smtp_send_email($r['email'], $lang->get('user_reg_activation_email_subject'), preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
-      if ( $result == 'success' )
-      {
-        $result = true;
-      }
-      else
-      {
-        echo $result;
-        $result = false;
-      }
-    }
-    else
-    {
-      $result = mail($r['email'], $lang->get('user_reg_activation_email_subject'), preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
-    }
-    return $result;
-  }
-  
-  /**
-   * Attempts to send an e-mail to the specified user's e-mail address on file intended for the parents
-   * @param string $u The usernamd of the user requesting activation
-   * @return bool true on success, false on failure
-   */
-   
-  function send_coppa_mail($u, $actkey = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $q = $this->sql('SELECT username,email FROM '.table_prefix.'users WHERE user_id=2 OR user_level=' . USER_LEVEL_ADMIN . ' ORDER BY user_id ASC;');
-    $un = $db->fetchrow();
-    $admin_user = $un['username'];
-    
-    $q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
-    $r = $db->fetchrow();
-    if ( empty($r['email']) )
-      $db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
-      
-    if(isset($_SERVER['HTTPS'])) $prot = 'https';
-    else $prot = 'http';                                                                           
-    if($_SERVER['SERVER_PORT'] == '80') $p = '';
-    else $p = ':'.$_SERVER['SERVER_PORT'];
-    $sidbak = false;
-    if($this->sid_super)
-      $sidbak = $this->sid_super;
-    $this->sid_super = false;
-    if($sidbak)
-      $this->sid_super = $sidbak;
-    unset($sidbak);
-    $link = "$prot://".$_SERVER['HTTP_HOST'].scriptPath;
-    
-    $message = $lang->get(
-        'user_reg_activation_email_coppa',
-        array(
-          'username' => $u,
-          'admin_user' => $admin_user,
-          'site_link' => $link
-        )
-      );
-    
-    
-    if(getConfig('smtp_enabled') == '1')
-    {
-      $result = smtp_send_email($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
-      if($result == 'success') 
-      {
-        $result = true;
-      }
-      else
-      {
-        echo $result;
-        $result = false;
-      }
-    } 
-    else
-    {
-      $result = mail($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
-    }
-    return $result;
-  }
-  
-  /**
-   * Sends an e-mail to a user so they can reset their password.
-   * @param int $user The user ID, or username if it's a string
-   * @return bool true on success, false on failure
-   */
-   
-  function mail_password_reset($user)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if(is_int($user))
-    {
-      $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE user_id='.$user.';'); // This is SAFE! This is only called if $user is an integer
-    }
-    elseif(is_string($user))
-    {
-      $q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=' . ENANO_SQLFUNC_LOWERCASE . '(\''.$db->escape($user).'\');');
-    }
-    else
-    {
-      return false;
-    }
-    
-    $row = $db->fetchrow();
-    $temp_pass = $this->random_pass();
-    
-    $this->register_temp_password($row['user_id'], $temp_pass);
-    
-    $site_name = getConfig('site_name');
+		// We good, create the user
+		$this->sql('INSERT INTO ' . table_prefix . "users ( username, email, real_name, theme, style, reg_time, account_active, activation_key, user_level, user_coppa,\n"
+ 						. "                                        user_registration_ip, user_lang, user_has_avatar, avatar_type ) VALUES\n"
+ 						. "  ( '$username', '$email', '$real_name', '$template->default_theme', '$template->default_style', " . time() . ", $active, '$actkey', \n"
+ 						. "    " . USER_LEVEL_CHPREF . ", $coppa_col, '$ip', $lang->lang_id, 0, 'png' );");
+		
+		// Get user ID and create users_extra entry
+		$q = $this->sql('SELECT user_id FROM '.table_prefix."users WHERE username='$username';");
+		if ( $db->numrows() > 0 )
+		{
+			list($user_id) = $db->fetchrow_num();
+			$db->free_result();
+			
+			$this->sql('INSERT INTO '.table_prefix.'users_extra(user_id) VALUES(' . $user_id . ');');
+		}
+		
+		// Set the password
+		$this->set_password($user_id, $password);
+		
+		// Config option added, 1.1.5
+		if ( getConfig('userpage_grant_acl', '1') == '1' )             
+		{
+			// Grant edit and very limited mod access to the userpage
+			$acl_data = array(
+					'read' => AUTH_ALLOW,
+					'view_source' => AUTH_ALLOW,
+					'edit_page' => AUTH_ALLOW,
+					'post_comments' => AUTH_ALLOW,
+					'edit_comments' => AUTH_ALLOW, // only allows editing own comments
+					'history_view' => AUTH_ALLOW,
+					'history_rollback' => AUTH_ALLOW,
+					'rename' => AUTH_ALLOW,
+					'delete_page' => AUTH_ALLOW,
+					'tag_create' => AUTH_ALLOW,
+					'tag_delete_own' => AUTH_ALLOW,
+					'tag_delete_other' => AUTH_ALLOW,
+					'edit_cat' => AUTH_ALLOW,
+					'create_page' => AUTH_ALLOW
+				);
+			$acl_data = $db->escape($this->perm_to_string($acl_data));
+			$userpage = $db->escape(sanitize_page_id($user_orig));
+			$cols = "target_type, target_id, page_id, namespace, rules";
+			$vals = ACL_TYPE_USER . ", $user_id, '$userpage', 'User', '$acl_data'";
+			$q = "INSERT INTO ".table_prefix."acl($cols) VALUES($vals);";
+			$this->sql($q);
+		}
+		
+		// Require the account to be activated?
+		if ( $coppa )
+		{
+			$this->admin_activation_request($user_orig);
+			$this->send_coppa_mail($user_orig, $email);
+		}
+		else
+		{
+			switch(getConfig('account_activation'))
+			{
+				case 'none':
+				default:
+					break;
+				case 'user':
+					$a = $this->send_activation_mail($user_orig);
+					if(!$a)
+					{
+						$this->admin_activation_request($user_orig);
+						return $lang->get('user_reg_err_actmail_failed') . ' ' . $a;
+					}
+					break;
+				case 'admin':
+					$this->admin_activation_request($user_orig);
+					break;
+			}
+		}
+		
+		// Leave some data behind for the hook
+		$code = $plugins->setHook('user_registered');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		return 'success';
+	}
+	
+	/**
+ 	* Attempts to send an e-mail to the specified user with activation instructions.
+ 	* @param string $u The usernamd of the user requesting activation
+ 	* @return bool true on success, false on failure
+ 	*/
+ 	
+	function send_activation_mail($u, $actkey = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		$q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
+		$r = $db->fetchrow();
+		if ( empty($r['email']) )
+			$db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
+		
+		$aklink = makeUrlComplete('Special', 'ActivateAccount/'.str_replace(' ', '_', $u).'/'. ( ( is_string($actkey) ) ? $actkey : $r['activation_key'] ) );
+		$message = $lang->get('user_reg_activation_email', array(
+				'activation_link' => $aklink,
+				'username' => $u
+			));
+			
+		if ( getConfig('smtp_enabled') == '1' )
+		{
+			$result = smtp_send_email($r['email'], $lang->get('user_reg_activation_email_subject'), preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
+			if ( $result == 'success' )
+			{
+				$result = true;
+			}
+			else
+			{
+				echo $result;
+				$result = false;
+			}
+		}
+		else
+		{
+			$result = mail($r['email'], $lang->get('user_reg_activation_email_subject'), preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
+		}
+		return $result;
+	}
+	
+	/**
+ 	* Attempts to send an e-mail to the specified user's e-mail address on file intended for the parents
+ 	* @param string $u The usernamd of the user requesting activation
+ 	* @return bool true on success, false on failure
+ 	*/
+ 	
+	function send_coppa_mail($u, $actkey = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$q = $this->sql('SELECT username,email FROM '.table_prefix.'users WHERE user_id=2 OR user_level=' . USER_LEVEL_ADMIN . ' ORDER BY user_id ASC;');
+		$un = $db->fetchrow();
+		$admin_user = $un['username'];
+		
+		$q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
+		$r = $db->fetchrow();
+		if ( empty($r['email']) )
+			$db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
+			
+		if(isset($_SERVER['HTTPS'])) $prot = 'https';
+		else $prot = 'http';                                                                           
+		if($_SERVER['SERVER_PORT'] == '80') $p = '';
+		else $p = ':'.$_SERVER['SERVER_PORT'];
+		$sidbak = false;
+		if($this->sid_super)
+			$sidbak = $this->sid_super;
+		$this->sid_super = false;
+		if($sidbak)
+			$this->sid_super = $sidbak;
+		unset($sidbak);
+		$link = "$prot://".$_SERVER['HTTP_HOST'].scriptPath;
+		
+		$message = $lang->get(
+				'user_reg_activation_email_coppa',
+				array(
+					'username' => $u,
+					'admin_user' => $admin_user,
+					'site_link' => $link
+				)
+			);
+		
+		
+		if(getConfig('smtp_enabled') == '1')
+		{
+			$result = smtp_send_email($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
+			if($result == 'success') 
+			{
+				$result = true;
+			}
+			else
+			{
+				echo $result;
+				$result = false;
+			}
+		} 
+		else
+		{
+			$result = mail($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
+		}
+		return $result;
+	}
+	
+	/**
+ 	* Sends an e-mail to a user so they can reset their password.
+ 	* @param int $user The user ID, or username if it's a string
+ 	* @return bool true on success, false on failure
+ 	*/
+ 	
+	function mail_password_reset($user)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if(is_int($user))
+		{
+			$q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE user_id='.$user.';'); // This is SAFE! This is only called if $user is an integer
+		}
+		elseif(is_string($user))
+		{
+			$q = $this->sql('SELECT user_id,username,email FROM '.table_prefix.'users WHERE ' . ENANO_SQLFUNC_LOWERCASE . '(username)=' . ENANO_SQLFUNC_LOWERCASE . '(\''.$db->escape($user).'\');');
+		}
+		else
+		{
+			return false;
+		}
+		
+		$row = $db->fetchrow();
+		$temp_pass = $this->random_pass();
+		
+		$this->register_temp_password($row['user_id'], $temp_pass);
+		
+		$site_name = getConfig('site_name');
  
-    $message = $lang->get('userfuncs_passreset_email', array(
-        'username' => $row['username'],
-        'site_name' => $site_name,
-        'remote_addr' => $_SERVER['REMOTE_ADDR'],
-        'temp_pass' => $temp_pass
-      ));
-    
-    if(getConfig('smtp_enabled') == '1')
-    {
-      $result = smtp_send_email($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
-      if($result == 'success')
-      {
-        $result = true;
-      }
-      else
-      {
-        echo '<p>'.$result.'</p>';
-        $result = false;
-      }
-    } else {
-      $result = mail($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
-    }
-    return $result;
-  }
-  
-  /**
-   * Sets the temporary password for the specified user to whatever is specified.
-   * @param int $user_id
-   * @param string $password
-   * @return bool
-   */
-   
-  function register_temp_password($user_id, $password)
-  {
-    global $db;
-    if ( !is_int($user_id) )
-      return false;
-    
-    $this->sql('SELECT password_salt FROM ' . table_prefix . "users WHERE user_id = $user_id;");
-    if ( $db->numrows() < 1 )
-      return false;
-    
-    list($salt) = $db->fetchrow_num();
-    $db->free_result();
-    
-    $temp_pass = hmac_sha1($password, $salt);
-    $this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';');
-  }
-  
-  /**
-   * Sends a request to the admin panel to have the username $u activated.
-   * @param string $u The username of the user requesting activation
-   */
-  
-  function admin_activation_request($u)
-  {
-    global $db;
-    $this->sql('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, author, edit_summary) VALUES(\'admin\', \'activ_req\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$this->username.'\', \''.$db->escape($u).'\');');
-  }
-  
-  /**
-   * Activates a user account. If the action fails, a report is sent to the admin.
-   * @param string $user The username of the user requesting activation
-   * @param string $key The activation key
-   */
-  
-  function activate_account($user, $key)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $q = $this->sql('SELECT 1 FROM ' . table_prefix . 'users WHERE username = \''.$db->escape($user).'\' AND activation_key = \''.$db->escape($key).'\'');
-    if ( $db->numrows() > 0 )
-    {
-      $new_key = md5(AESCrypt::randkey());
-      $this->sql('UPDATE ' . table_prefix . 'users SET account_active = 1, activation_key = \'' . $new_key . '\' WHERE username=\''.$db->escape($user).'\' AND activation_key=\''.$db->escape($key).'\';');
-      $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')');
-      return true;
-    }
-    else
-    {
-      $this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')');
-      return false;
-    }
-  }
-  
-  /**
-   * For a given user level identifier (USER_LEVEL_*), returns a string describing that user level.
-   * @param int User level
-   * @param bool If true, returns a shorter string. Optional.
-   * @return string
-   */
-  
-  function userlevel_to_string($user_level, $short = false)
-  {
-    global $lang;
-    
-    static $levels = array(
-        'short' => array(
-            USER_LEVEL_GUEST => 'Guest',
-            USER_LEVEL_MEMBER => 'Member',
-            USER_LEVEL_CHPREF => 'Sensitive preferences changeable',
-            USER_LEVEL_MOD => 'Moderator',
-            USER_LEVEL_ADMIN => 'Administrative'
-          ),
-        'long' => array(
-            USER_LEVEL_GUEST => 'Low - guest privileges',
-            USER_LEVEL_MEMBER => 'Standard - normal member level',
-            USER_LEVEL_CHPREF => 'Medium - user can change his/her own e-mail address and password',
-            USER_LEVEL_MOD => 'High - moderator privileges',
-            USER_LEVEL_ADMIN => 'Highest - administrative privileges'
-          ),
-        'l10n' => false
-      );
-    
-    if ( is_object($lang) && !$levels['l10n'] )
-    {
-      $levels = array(
-          'short' => array(
-              USER_LEVEL_GUEST => $lang->get('user_level_short_guest'),
-              USER_LEVEL_MEMBER => $lang->get('user_level_short_member'),
-              USER_LEVEL_CHPREF => $lang->get('user_level_short_chpref'),
-              USER_LEVEL_MOD => $lang->get('user_level_short_mod'),
-              USER_LEVEL_ADMIN => $lang->get('user_level_short_admin')
-            ),
-          'long' => array(
-              USER_LEVEL_GUEST => $lang->get('user_level_long_guest'),
-              USER_LEVEL_MEMBER => $lang->get('user_level_long_member'),
-              USER_LEVEL_CHPREF => $lang->get('user_level_long_chpref'),
-              USER_LEVEL_MOD => $lang->get('user_level_long_mod'),
-              USER_LEVEL_ADMIN => $lang->get('user_level_long_admin')
-            ),
-          'l10n' => true
-        );
-    }
-    
-    $key = ( $short ) ? 'short' : 'long';
-    if ( isset($levels[$key][$user_level]) )
-    {
-      return $levels[$key][$user_level];
-    }
-    else
-    {
-      if ( $short )
-      {
-        return ( is_object($lang) ) ? $lang->get('user_level_short_unknown', array('user_level' => $user_level)) : "Unknown - $user_level";
-      }
-      else
-      {
-        return ( is_object($lang) ) ? $lang->get('user_level_long_unknown', array('user_level' => $user_level)) : "Unknown level ($user_level)";
-      }
-    }
-    
-    return 'Linux rocks!';
-    
-  }
-  
-  /**
-   * Change a user's e-mail address.
-   * @param int $user_id The user ID of the user to update - this cannot be changed
-   * @param string $email The new e-mail address
-   * @return string 'success' if successful, or array of error strings on failure
-   */
-   
-  function change_email($user_id, $email)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Create some arrays
-    
-    $errors = array(); // Used to hold error strings
-    
-    // Scan the user ID for problems
-    if ( intval($user_id) < 1 )
-      $errors[] = 'SQL injection attempt';
-    
-    $user_id = intval($user_id);
-    
-    // Verify e-mail address
-    if ( !check_email_address($email) )
-      $errors[] = 'user_err_email_not_valid';
-    
-    if ( count($errors) > 0 )
-      return $errors;
-    
-    // Make query
-    $email = $db->escape($email);
-    $q = $db->sql_query('UPDATE ' . table_prefix . "users SET email = '$email' WHERE user_id = $user_id;");
-    
-    // We also need to trigger re-activation.
-    switch(getConfig('account_activation', 'none'))
-    {
-      case 'user':
-      case 'admin':
-        
-        // Note: even with admin activation, activation e-mails are sent when an e-mail is changed.
-        
-        if ( $session->user_level >= USER_LEVEL_MOD && getConfig('account_activation') == 'admin' )
-          // Trust admins and moderators
-          break;
-        
-        // retrieve username
-        if ( !$username )
-        {
-          $q = $this->sql('SELECT username FROM ' . table_prefix . "users WHERE user_id = $user_id;");
-          if($db->numrows() < 1)
-          {
-            $errors[] = 'The username could not be selected.';
-          }
-          else
-          {
-            $row = $db->fetchrow();
-            $username = $row['username'];
-          }
-        }
-        if ( !$username )
-          return $errors;
-        
-        // Generate an activation key
-        $actkey = sha1 ( microtime() . mt_rand() );
-        $a = $this->send_activation_mail($username, $actkey);
-        if(!$a)
-        {
-          $this->admin_activation_request($username);
-        }
-        // Deactivate the account until e-mail is confirmed
-        $q = $db->sql_query('UPDATE ' . table_prefix . "users SET account_active = 0, activation_key = '$actkey' WHERE user_id = $user_id;");
-        break;
-    }
-    
-    // Yay! We're done
-    return 'success';
-  }
-  
-  /**
-   * Sets a user's password.
-   * @param int|string User ID or username
-   * @param string New password
-   */
-  
-  function set_password($user, $password)
-  {
-    // Generate new password and salt
-    $hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
-    $password_hmac = hmac_sha1($password, $hmac_secret);
-    
-    // Figure out how we want to specify the user
-    $uidcol = is_int($user) ? "user_id = $user" : ENANO_SQLFUNC_LOWERCASE . "(username) = '" . strtolower($this->prepare_text($user)) . "'";
-    
-    // Perform update
-    $this->sql('UPDATE ' . table_prefix . "users SET old_encryption = 0, password = '$password_hmac', password_salt = '$hmac_secret' WHERE $uidcol;");
-    
-    return true;
-  }
-  
-  /**
-   * Encrypts a string using the site's private key.
-   * @param string
-   * @param int Return type - one of ENC_BINARY, ENC_HEX, ENC_BASE64
-   * @return string
-   */
-  
-  function pk_encrypt($string, $return_type = ENC_HEX)
-  {
-    $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-    return $aes->encrypt($string, $this->private_key, $return_type);
-  }
-  
-  /**
-   * Encrypts a string using the site's private key.
-   * @param string
-   * @param int Input type - one of ENC_BINARY, ENC_HEX, ENC_BASE64
-   * @return string
-   */
-  
-  function pk_decrypt($string, $input_type = ENC_HEX)
-  {
-    $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-    return $aes->decrypt($string, $this->private_key, $input_type);
-  }
-  
-  #
-  # USER RANKS
-  #
-  
-  /**
-   * SYNOPSIS OF THE RANK SYSTEM
-   * Enano's rank logic calculates a user's rank based on a precedence scale. The way things are checked is:
-   *   1. Check to see if the user has a specific rank assigned. Use that if possible.
-   *   2. Check the user's primary group to see if it specifies a rank. Use that if possible.
-   *   3. Check the other groups a user is in. If one that has a custom rank is encountered, use that rank.
-   *   4. See if the user's user level has a specific rank hard-coded to be associated with it. (Always overrideable as can be seen above)
-   *   5. Use the "member" rank
-   */
-  
-  /**
-   * Generates a textual SQL query for fetching rank data to be sent to calculate_user_rank().
-   * @param string Text to append, possibly a WHERE clause or so
-   * @return string
-   */
-  
-  function generate_rank_sql($append = '')
-  {
-    // Generate level-to-rank associations
-    $assoc = array();
-    foreach ( $this->level_rank_table as $level => $rank )
-    {
-      $assoc[] = "        ( u.user_level = $level AND rl.rank_id = $rank )";
-    }
-    $assoc = implode(" OR\n", $assoc) . "\n";
-    
-    $gid_col = ( ENANO_DBLAYER == 'PGSQL' ) ?
-      'array_to_string(' . table_prefix . 'array_accum(m.group_id), \',\') AS group_list' :
-      'GROUP_CONCAT(m.group_id) AS group_list';
-    
-    // The actual query
-    $sql = "SELECT u.user_id, u.username, u.user_level, u.user_group, u.user_rank, u.user_title, g.group_rank,\n"
-         . "       COALESCE(ru.rank_id,    rg.rank_id,    rl.rank_id,    rd.rank_id   ) AS rank_id,\n"
-         . "       COALESCE(ru.rank_title, rg.rank_title, rl.rank_title, rd.rank_title) AS rank_title,\n"
-         . "       COALESCE(ru.rank_style, rg.rank_style, rl.rank_style, rd.rank_style) AS rank_style,\n"
-         . "       rg.rank_id AS group_rank_id,\n"
-         . "       ( ru.rank_id IS NULL AND rg.rank_id IS NULL ) AS using_default,\n"
-         . "       ( ru.rank_id IS NULL AND rg.rank_id IS NOT NULL ) AS using_group,\n"
-         . "       ( ru.rank_id IS NOT NULL ) AS using_user,\n"
-         . "       u.user_rank_userset,\n"
-         . "       $gid_col\n"
-         . "  FROM " . table_prefix . "users AS u\n"
-         . "  LEFT JOIN " . table_prefix . "groups AS g\n"
-         . "    ON ( g.group_id = u.user_group )\n"
-         . "  LEFT JOIN " . table_prefix . "group_members AS m\n"
-         . "    ON ( u.user_id = m.user_id )\n"
-         . "  LEFT JOIN " . table_prefix . "ranks AS ru\n"
-         . "    ON ( u.user_rank = ru.rank_id )\n"
-         . "  LEFT JOIN " . table_prefix . "ranks AS rg\n"
-         . "    ON ( g.group_rank = rg.rank_id )\n"
-         . "  LEFT JOIN " . table_prefix . "ranks AS rl\n"
-         . "    ON (\n"
-         . $assoc
-         . "      )\n"
-         . "  LEFT JOIN " . table_prefix . "ranks AS rd\n"
-         . "    ON ( rd.rank_id = 1 )$append\n"
-         . "  GROUP BY u.user_id, u.username, u.user_level, u.user_group, u.user_rank, u.user_title, u.user_rank_userset, g.group_rank,\n"
-         . "       ru.rank_id, ru.rank_title, ru.rank_style,rg.rank_id, rg.rank_title, rg.rank_style,\n"
-         . "       rl.rank_id, rl.rank_title, rl.rank_style,rd.rank_id, rd.rank_title, rd.rank_style;";
-    
-    return $sql;
-  }
-  
-  /**
-   * Returns an associative array with a user's rank information.
-   * The array will contain the following values:
-   *   username: string  The user's username
-   *   user_id:  integer Numerical user ID
-   *   rank_id:  integer Numerical rank ID
-   *   rank:     string  The user's current rank
-   *   title:    string  The user's custom user title if applicable; should be displayed one line below the rank
-   *   style:    string  CSS for the username
-   * @param int|string Username *or* user ID
-   * @return array or false on failure
-   */
-  
-  function get_user_rank($id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $user_ranks;
-    // cache info in RAM if possible
-    static $_cache = array();
-    
-    if ( is_int($id) && $id == 0 )
-      $id = 1;
-    
-    if ( is_int($id) )
-      $col = "u.user_id = $id";
-    else if ( is_string($id) )
-      $col = ENANO_SQLFUNC_LOWERCASE . "(username) = " . ENANO_SQLFUNC_LOWERCASE . "('" . $db->escape($id) . "')";
-    else
-      // invalid parameter
-      return false;
-      
-    // check the RAM cache
-    if ( isset($_cache[$id]) )
-      return $_cache[$id];
-    
-    // check the disk cache
-    if ( is_int($id) )
-    {
-      if ( isset($user_ranks[$id]) )
-      {
-        $_cache[$id] =& $user_ranks[$id];
-        return $user_ranks[$id];
-      }
-    }
-    else if ( is_string($id) )
-    {
-      foreach ( $user_ranks as $key => $valarray )
-      {
-        if ( is_string($key) && strtolower($key) == strtolower($id) )
-        {
-          $_cache[$id] = $valarray;
-          return $valarray;
-        }
-      }
-    }
-    
-    $sql = $this->generate_rank_sql("\n  WHERE $col");
-    
-    $q = $this->sql($sql);
-    // any results?
-    if ( $db->numrows() < 1 )
-    {
-      // nuttin'.
-      $db->free_result();
-      $_cache[$id] = false;
-      return false;
-    }
-    
-    // Found something.
-    $row = $db->fetchrow();
-    $db->free_result();
-    
-    $row = $this->calculate_user_rank($row);
-    
-    $_cache[$id] = $row;
-    return $row;
-  }
-  
-  /**
-   * Performs the actual rank calculation based on the contents of a row.
-   * @param array
-   * @return array
-   */
-  
-  function calculate_user_rank($row)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    static $rank_cache = array();
-    static $group_ranks = array();
-    
-    // try to cache that rank info
-    if ( !isset($rank_cache[ intval($row['rank_id']) ]) && $row['rank_id'] )
-    {
-      $rank_cache[ intval($row['rank_id']) ] = array(
-          'rank_id' => intval($row['rank_id']),
-          'rank_title' => intval($row['rank_title']),
-          'rank_style' => intval($row['rank_style'])
-        );
-    }
-    // cache group info (if appropriate)
-    if ( $row['using_group'] && !isset($group_ranks[ intval($row['user_group']) ]) )
-    {
-      $group_ranks[ intval($row['user_group']) ] = intval($row['group_rank_id']);
-    }
-    
-    // sanitize and process the as-of-yet rank data
-    $row['rank_id'] = intval($row["rank_id"]);
-    $row['rank_title'] = $row["rank_title"];
-    
-    // if we're falling back to some default, then see if we can use one of the user's other groups
-    if ( $row['using_default'] && !empty($row['group_list']) )
-    {
-      $group_list = explode(',', $row['group_list']);
-      if ( array_walk($group_list, 'intval') )
-      {
-        // go through the group list and see if any of them has a rank assigned
-        foreach ( $group_list as $group_id )
-        {
-          // cached in RAM? Preferably use that.
-          if ( !isset($group_ranks[$group_id]) )
-          {
-            // Not cached - grab it
-            $q = $this->sql('SELECT group_rank FROM ' . table_prefix . "groups WHERE group_id = $group_id;");
-            if ( $db->numrows() < 1 )
-            {
-              $db->free_result();
-              continue;
-            }
-            list($result) = $db->fetchrow_num();
-            $db->free_result();
-            
-            if ( $result === null || $result < 1 )
-            {
-              $group_ranks[$group_id] = false;
-            }
-            else
-            {
-              $group_ranks[$group_id] = intval($result);
-            }
-          }
-          // we've got it now
-          if ( $group_ranks[$group_id] )
-          {
-            // found a group with a rank assigned
-            // so get the rank info
-            $rank_id =& $group_ranks[$group_id];
-            if ( !isset($rank_cache[$rank_id]) )
-            {
-              $q = $this->sql('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = $rank_id;");
-              if ( $db->numrows() < 1 )
-              {
-                $db->free_result();
-                continue;
-              }
-              $rank_cache[$rank_id] = $db->fetchrow();
-              $db->free_result();
-            }
-            // set the final rank parameters
-            // die("found member-of-group exception with uid {$row['user_id']} gid $group_id rid $rank_id rt {$rank_cache[$rank_id]['rank_title']}");
-            $row['rank_id'] = $rank_id;
-            $row['rank_title'] = $rank_cache[$rank_id]['rank_title'];
-            $row['rank_style'] = $rank_cache[$rank_id]['rank_style'];
-            break;
-          }
-        }
-      }
-    }
-    
-    if ( $row['user_title'] === NULL )
-      $row['user_title'] = false;
-    
-    $row['user_id'] = intval($row['user_id']);
-    $row['user_level'] = intval($row['user_level']);
-    $row['user_group'] = intval($row['user_group']);
-    
-    unset($row['user_rank'], $row['group_rank'], $row['group_list'], $row['using_default'], $row['using_group'], $row['user_level'], $row['user_group'], $row['username']);
-    return $row;
-  }
-  
-  /**
-   * Get the list of ranks that a user is allowed to use. Returns false if they cannot change it.
-   * @param string|int User ID or username
-   * @return array Associative by rank ID
-   */
-  
-  function get_user_possible_ranks($id)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // cache info in RAM if possible
-    static $_cache = array();
-    
-    if ( is_int($id) && $id == 0 )
-      $id = 1;
-    
-    if ( is_int($id) )
-      $col = "u.user_id = $id";
-    else if ( is_string($id) )
-      $col = ENANO_SQLFUNC_LOWERCASE . "(username) = " . ENANO_SQLFUNC_LOWERCASE . "('" . $db->escape($id) . "')";
-    else
-      // invalid parameter
-      return false;
-      
-    // check the RAM cache
-    if ( isset($_cache[$id]) )
-      return $_cache[$id];
-    
-    $sql = $this->generate_rank_sql("\n  WHERE $col");
-    
-    $q = $this->sql($sql);
-    // any results?
-    if ( $db->numrows() < 1 )
-    {
-      // nuttin'.
-      $db->free_result();
-      $_cache[$id] = false;
-      return false;
-    }
-    
-    // Found something.
-    $row = $db->fetchrow();
-    $db->free_result();
-    
-    if ( $row['using_user'] && !$row['user_rank_userset'] )
-    {
-      // The user's rank was set manually by an admin.
-      $result = array(
-        array(
-          'rank_id' => $row['rank_id'],
-          'rank_title' => $row['rank_title'],
-          'rank_style' => $row['rank_style'],
-          'rank_type' => 'user'
-          )
-        );
-      $_cache[$id] = $result;
-      return $result;
-    }
-    
-    // copy the result to a more permanent array so we can reference this later
-    $current_settings = $row;
-    unset($row);
-    
-    $result = array();
-    
-    // first rank available to us will be the one set by the user's user level
-    if ( isset($this->level_rank_table[$current_settings['user_level']]) )
-    {
-      $q = $this->sql('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = {$this->level_rank_table[$this->user_level]};");
-      if ( $db->numrows() > 0 )
-      {
-        $row = $db->fetchrow();
-        $row['rank_type'] = 'ulevel';
-        
-        $result[] = $row;
-      }
-      $db->free_result();
-    }
-    
-    // for each group the user is in, figure out if it has a rank associated with it
-    $group_list = explode(',', $current_settings['group_list']);
-    foreach ( $group_list as $group_id )
-    {
-      $group_id = intval($group_id);
-      $q = $this->sql('SELECT r.rank_id, r.rank_title, r.rank_style FROM ' . table_prefix . "groups AS g\n"
-                    . "  LEFT JOIN " . table_prefix . "ranks AS r\n"
-                    . "    ON ( g.group_rank = r.rank_id )\n"
-                    . "  WHERE g.group_id = $group_id\n"
-                    . "    AND r.rank_id IS NOT NULL;");
-      if ( $db->numrows() > 0 )
-      {
-        $row = $db->fetchrow();
-        $row['rank_type'] = 'group';
-        
-        $result[] = $row;
-      }
-      $db->free_result();
-    }
-    
-    $_cache[$id] = $result;
-    return $result;
-  }
-  
-  #
-  # Access Control Lists
-  #
-  
-  /**
-   * Creates a new permission field in memory. If the permissions are set in the database, they are used. Otherwise, $default_perm is used.
-   * @param string $acl_type An identifier for this field
-   * @param int $default_perm Whether permission should be granted or not if it's not specified in the ACLs.
-   * @param string $desc A human readable name for the permission type
-   * @param array $deps The list of dependencies - this should be an array of ACL types
-   * @param string $scope Which namespaces this field should apply to. This should be either a pipe-delimited list of namespace IDs or just "All".
-   */
-   
-  function register_acl_type($acl_type, $default_perm = AUTH_DISALLOW, $desc = false, $deps = Array(), $scope = 'All')
-  {
-    if(isset($this->acl_types[$acl_type]))
-      return false;
-    else
-    {
-      if(!$desc)
-      {
-        $desc = capitalize_first_letter(str_replace('_', ' ', $acl_type));
-      }
-      $this->acl_types[$acl_type] = $default_perm;
-      $this->acl_descs[$acl_type] = $desc;
-      $this->acl_deps[$acl_type] = $deps;
-      $this->acl_scope[$acl_type] = explode('|', $scope);
-    }
-    return true;
-  }
-  
-  /**
-   * Tells us whether permission $type is allowed or not based on the current rules.
-   * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
-   * @param bool $no_deps If true, disables dependency checking
-   * @return bool True if allowed, false if denied or if an error occured
-   */
-   
-  function get_permissions($type, $no_deps = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( isset( $this->perms[$type] ) )
-    {
-      if ( $this->perms[$type] == AUTH_DENY )
-        $ret = false;
-      else if ( $this->perms[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
-        $ret = true;
-      else if ( $this->perms[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
-        $ret = false;
-      else if ( $this->perms[$type] == AUTH_ALLOW )
-        $ret = true;
-      else if ( $this->perms[$type] == AUTH_DISALLOW )
-        $ret = false;
-    }
-    else if(isset($this->acl_types[$type]))
-    {
-      if ( $this->acl_types[$type] == AUTH_DENY )
-        $ret = false;
-      else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
-        $ret = true;
-      else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
-        $ret = false;
-      else if ( $this->acl_types[$type] == AUTH_ALLOW )
-        $ret = true;
-      else if ( $this->acl_types[$type] == AUTH_DISALLOW )
-        $ret = false;
-    }
-    else
-    {
-      // ACL type is undefined
-      trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING);
-      return false; // Be on the safe side and deny access
-    }
-    if ( !$no_deps )
-    {
-      if ( !$this->acl_check_deps($type) )
-        return false;
-    }
-    return $ret;
-  }
-  
-  /**
-   * Fetch the permissions that apply to the current user for the page specified. The object you get will have the get_permissions method
-   * and several other abilities.
-   * @param string $page_id
-   * @param string $namespace
-   * @return object
-   */
-   
-  function fetch_page_acl($page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( count ( $this->acl_base_cache ) < 1 )
-    {
-      // Permissions table not yet initialized
-      return false;
-    }
-    
-    // cache of permission objects (to save RAM and SQL queries)
-    static $objcache = array();
-    
-    if ( count($objcache) == 0 )
-    {
-      foreach ( $paths->nslist as $key => $_ )
-      {
-        $objcache[$key] = array();
-      }
-    }
-    
-    if ( isset($objcache[$namespace][$page_id]) )
-    {
-      return $objcache[$namespace][$page_id];
-    }
-    
-    $objcache[$namespace][$page_id] = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache );
-    $object =& $objcache[$namespace][$page_id];
-    
-    profiler_log("session: fetched ACLs for page {$namespace}:{$page_id}");
-    
-    return $object;
-  }
-  
-  /**
-   * Fetch the permissions that apply to an arbitrary user for the page specified. The object you get will have the get_permissions method
-   * and several other abilities.
-   * @param int|string $user_id_or_name; user ID *or* username of the user
-   * @param string $page_id; if null, will be default effective permissions. 
-   * @param string $namespace; if null, will be default effective permissions.
-   * @return object
-   */
-  
-  function fetch_page_acl_user($user_id_or_name, $page_id, $namespace)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // cache user info
-    static $user_info_cache = null;
-    
-    if ( isset($user_info_cache[$user_id_or_name]) )
-    {
-      $user_id =& $user_info_cache[$user_id_or_name]['user_id'];
-      $groups =& $user_info_cache[$user_id_or_name]['groups'];
-    }
-    else
-    {
-      $uid_column = ( is_int($user_id_or_name) ) ? "user_id = $user_id_or_name" : "username = '" . $db->escape($user_id_or_name) . "'";
-      $q = $db->sql_query('SELECT u.user_id, m.group_id, g.group_name FROM ' . table_prefix . "users AS u\n"
-                        . "  LEFT JOIN " . table_prefix . "group_members AS m\n"
-                        . "    ON ( ( u.user_id = m.user_id AND m.pending = 0 ) OR m.member_id IS NULL )\n"
-                        . "  LEFT JOIN " . table_prefix . "groups AS g\n"
-                        . "    ON ( g.group_id = m.group_id )\n"
-                        . "  WHERE $uid_column;");
-      if ( !$q )
-        $db->_die();
-      
-      // The l10n engine takes care of this later.
-      $groups = array(1 => 'Everyone');
-      
-      if ( $row = $db->fetchrow() )
-      {
-        $user_id = intval($row['user_id']);
-        if ( $row['group_id'] )
-        {
-          do
-          {
-            $groups[ intval($row['group_id'] ) ] = $row['group_name'];
-          }
-          while ( $row = $db->fetchrow() );
-        }
-        $db->free_result();
-      }
-      else
-      {
-        $db->free_result();
-        throw new Exception('Unknown user ID or username');
-      }
-      
-      $user_info_cache[$user_id_or_name] = array(
-          'user_id' => $user_id,
-          'groups' => $groups
-        );
-    }
-    
-    // cache base permissions
-    static $base_cache = array();
-    if ( !isset($base_cache[$user_id_or_name]) )
-    {
-      $base_cache[$user_id_or_name] = $this->acl_types;
-      $current_perms =& $base_cache[$user_id_or_name];
-      $current_perms['__resolve_table'] = array();
-      
-      $bs = 'SELECT rules, target_type, target_id, rule_id, page_id, namespace, g.group_name FROM '.table_prefix."acl AS a\n"
-          . "  LEFT JOIN " . table_prefix . "groups AS g\n"
-          . "    ON ( ( a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id ) OR ( a.target_type != " . ACL_TYPE_GROUP . " ) )\n"
-          . '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
-          . '  ( ';
-    
-      $q = Array();
-      $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id= ' . $user_id . ' )';
-      if(count($groups) > 0)
-      {
-        foreach($groups as $g_id => $g_name)
-        {
-          $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
-        }
-      }
-      $bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
-      $q = $this->sql($bs);
-      foreach ( $this->acl_types as $perm_type => $_ )
-      {
-        // init the resolver table with blanks
-        $current_perms['__resolve_table'][$perm_type] = array(
-            'src' => ACL_INHERIT_ENANO_DEFAULT,
-            'rule_id' => -1
-          );
-      }
-      if ( $row = $db->fetchrow() )
-      {
-        do {
-          $rules = $this->string_to_perm($row['rules']);
-          $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
-          // track where these rulings are coming from
-          $src = ( $is_everyone ) ? ACL_INHERIT_GLOBAL_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_GLOBAL_GROUP : ACL_INHERIT_GLOBAL_USER );
-          foreach ( $rules as $perm_type => $_ )
-          {
-            $current_perms['__resolve_table'][$perm_type] = array(
-                'src' => $src,
-                'rule_id' => $row['rule_id']
-              );
-            if ( $row['group_name'] )
-            {
-              $current_perms['__resolve_table'][$perm_type]['group_name'] = $row['group_name'];
-            }
-          }
-          // merge it in
-          $current_perms = $this->acl_merge($current_perms, $rules, $is_everyone, $_defaults_used);
-        } while ( $row = $db->fetchrow() );
-      }
-    }
-    
-    // cache of permission objects (to save RAM and SQL queries)
-    static $objcache = array();
-    
-    if ( count($objcache) == 0 )
-    {
-      foreach ( $paths->nslist as $key => $_ )
-      {
-        $objcache[$key] = array();
-      }
-    }
-    
-    if ( !isset($objcache[$namespace][$page_id]) )
-    {
-      $objcache[$namespace][$page_id] = array();
-    }
-    
-    if ( isset($objcache[$namespace][$page_id][$user_id_or_name]) )
-    {
-      return $objcache[$namespace][$page_id][$user_id_or_name];
-    }
-    
-    //if ( !isset( $paths->pages[$paths->nslist[$namespace] . $page_id] ) )
-    //{
-    //  // Page does not exist
-    //  return false;
-    //}
-    
-    $objcache[$namespace][$page_id][$user_id_or_name] = new Session_ACLPageInfo(
-      $page_id,                                        // $page_id, 
-      $namespace,                                      // $namespace,
-      $this->acl_types,                                // $acl_types,
-      $this->acl_descs,                                // $acl_descs,
-      $this->acl_deps,                                 // $acl_deps,
-      $base_cache[$user_id_or_name],                   // $base,
-      $user_info_cache[$user_id_or_name]['user_id'],   // $user_id = null,
-      $user_info_cache[$user_id_or_name]['groups'],    // $groups = null,
-      $base_cache[$user_id_or_name]['__resolve_table'] // $resolve_table = array()
-    );
-    $object =& $objcache[$namespace][$page_id][$user_id_or_name];
-    
-    return $object;
-  }
-  
-  /**
-   * Checks if the given ACL rule type applies to a namespace.
-   * @param string ACL rule type
-   * @param string Namespace
-   * @return bool
-   */
-  
-  function check_acl_scope($acl_rule, $namespace)
-  {
-    if ( !isset($this->acl_scope[$acl_rule]) )
-      return false;
-    if ( $this->acl_scope[$acl_rule] === array('All') )
-      return true;
-    return ( in_array($namespace, $this->acl_scope[$acl_rule]) ) ? true : false;
-  }
-  
-  /**
-   * Read all of our permissions from the database and process/apply them. This should be called after the page is determined.
-   * @access private
-   */
-  
-  function init_permissions()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    // Initialize the permissions list with some defaults
-    $this->perms = $this->acl_types;
-    $this->acl_defaults_used = $this->perms;
-    
-    // Fetch sitewide defaults from the permissions table
-    $bs = 'SELECT rules, target_type, target_id FROM '.table_prefix.'acl' . "\n"
-             . '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
-             . '  ( ';
-    
-    $q = Array();
-    $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
-    if(count($this->groups) > 0)
-    {
-      foreach($this->groups as $g_id => $g_name)
-      {
-        $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
-      }
-    }
-    $bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
-    $q = $this->sql($bs);
-    if ( $row = $db->fetchrow() )
-    {
-      do {
-        $rules = $this->string_to_perm($row['rules']);
-        $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
-        $this->acl_merge_with_current($rules, $is_everyone);
-      } while ( $row = $db->fetchrow() );
-    }
-    
-    // Cache the sitewide permissions for later use
-    $this->acl_base_cache = $this->perms;
-    
-    profiler_log('session: base ACL set calculated');
-    
-    // Load and calculate permissions for the current page
-    $page_acl = $this->fetch_page_acl($paths->page_id, $paths->namespace);
-    $this->perms = $page_acl->perms;
-    $this->acl_defaults_used = $page_acl->acl_defaults_used;
-  }
-  
-  /**
-   * Extends the scope of a permission type.
-   * @param string The name of the permission type
-   * @param string The namespace(s) that should be covered. This can be either one namespace ID or a pipe-delimited list.
-   * @param object Optional - the current $paths object, in case we're doing this from the acl_rule_init hook
-   */
-   
-  function acl_extend_scope($perm_type, $namespaces, &$p_in)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $p_obj = ( is_object($p_in) ) ? $p_in : $paths;
-    $nslist = explode('|', $namespaces);
-    foreach ( $nslist as $i => $ns )
-    {
-      if ( !isset($p_obj->nslist[$ns]) )
-      {
-        unset($nslist[$i]);
-      }
-      else
-      {
-        if ( $this->acl_scope[$perm_type] !== array('All') )
-          $this->acl_scope[$perm_type][] = $ns;
-        if ( isset($this->acl_types[$perm_type]) && !isset($this->perms[$perm_type]) )
-        {
-          $this->perms[$perm_type] = $this->acl_types[$perm_type];
-        }
-      }
-    }
-  }
-  
-  /**
-   * Converts a permissions field into a string for database insertion. Similar in spirit to serialize().
-   * @param array $perms An associative array with only integers as values
-   * @return string
-   */
-   
-  function perm_to_string($perms)
-  {
-    $s = '';
-    foreach($perms as $perm => $ac)
-    {
-      if ( $ac == 'i' )
-        continue;
-      $s .= "$perm=$ac;";
-    }
-    return $s;
-  }
-  
-  /**
-   * Converts a permissions string back to an array.
-   * @param string $perms The result from sessionManager::perm_to_string()
-   * @return array
-   */
-   
-  function string_to_perm($perms)
-  {
-    $ret = Array();
-    preg_match_all('#([a-z0-9_-]+)=([0-9]+);#i', $perms, $matches);
-    foreach($matches[1] as $i => $t)
-    {
-      $ret[$t] = intval($matches[2][$i]);
-    }
-    return $ret;
-  }
-  
-  /**
-   * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence over the first, but AUTH_DENY always prevails.
-   * @param array $perm1 The first set of permissions
-   * @param array $perm2 The second set of permissions
-   * @param bool $is_everyone If true, applies exceptions for "Everyone" group
-   * @param array|reference $defaults_used Array that will be filled with default usage data
-   * @return array
-   */
-   
-  function acl_merge($perm1, $perm2, $is_everyone = false, &$defaults_used = array())
-  {
-    $ret = $perm1;
-    if ( !is_array(@$defaults_used) )
-    {
-      $defaults_used = array();
-    }
-    foreach ( $perm1 as $i => $p )
-    {
-      if ( isset($perm2[$i]) )
-      {
-        if ( $is_everyone && isset($defaults_used[$i]) && $defaults_used[$i] === false )
-          continue;
-        // Decide precedence
-        if ( isset($defaults_used[$i]) )
-        {
-          // echo "$i: default in use, overriding to: {$perm2[$i]}<br />";
-          // Defaults are in use, override
-          
-          // CHANGED - 1.1.4:
-          // For some time this has been intentionally relaxed so that the following
-          // exception is available to Deny permissions:
-          //   If the rule applies to the group "Everyone" on the entire site,
-          //   Deny settings could be overriden.
-          // This is documented at: http://docs.enanocms.org/Help:4.2
-          if ( $perm1[$i] != AUTH_DENY )
-          {
-            $perm1[$i] = $perm2[$i];
-            $defaults_used[$i] = ( $is_everyone );
-          }
-        }
-        else
-        {
-          // echo "$i: default NOT in use";
-          // Defaults are not in use, merge as normal
-          if ( $perm1[$i] != AUTH_DENY )
-          {
-            // echo ", but overriding";
-            $perm1[$i] = $perm2[$i];
-          }
-          // echo "<br />";
-        }
-      }
-    }
-    return $perm1;
-  }
-  
-  /**
-   * Merges two ACL arrays, but instead of calculating inheritance for missing permission types, just returns 'i' for that type. Useful
-   * for explicitly requiring inheritance in ACL editing interfaces
-   * @param array $perm1 The first set of permissions
-   * @param array $perm2 The second, authoritative set of permissions
-   */
-  
-  function acl_merge_inherit($perm1, $perm2)
-  {
-    foreach ( $perm1 as $type => $level )
-    {
-      $perm1[$type][$level] = 'i';
-    }
-    $ret = $perm1;
-    foreach ( $perm2 as $type => $level )
-    {
-      if ( isset( $ret[$type] ) )
-      {
-        if ( $ret[$type] != AUTH_DENY )
-          $ret[$type] = $level;
-      }
-    }
-    return $ret;
-  }
-  
-  /**
-   * Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
-   * @param array The array to merge into the master ACL list
-   * @param bool If true, $perm is treated as the "new default"
-   * @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
-   */
-  
-  function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
-  {
-    $this->perms = $this->acl_merge($this->perms, $perm, $is_everyone, $this->acl_defaults_used);
-  }
-  
-  /**
-   * Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence
-   * over the first, without exceptions. This is used to merge the hardcoded defaults with admin-specified
-   * defaults, which take precedence.
-   * @param array $perm1 The first set of permissions
-   * @param array $perm2 The second set of permissions
-   * @return array
-   */
-   
-  function acl_merge_complete($perm1, $perm2)
-  {
-    $ret = $perm1;
-    foreach ( $perm2 as $type => $level )
-    {
-      $ret[$type] = $level;
-    }
-    return $ret;
-  }
-  
-  /**
-   * Tell us if the dependencies for a given permission are met.
-   * @param string The ACL permission ID
-   * @return bool
-   */
-   
-  function acl_check_deps($type, $debug = false)
-  {
-    global $paths;
-    
-    // This will only happen if the permissions table is hacked or improperly accessed
-    if(!isset($this->acl_deps[$type]))
-      return true;
-    // Permission has no dependencies?
-    if(sizeof($this->acl_deps[$type]) < 1)
-      return true;
-    // go through them all and build a flat list of dependencies
-    $deps = $this->acl_deps[$type];
-    while(true)
-    {
-      $j = sizeof($deps);
-      for ( $i = 0; $i < $j; $i++ )
-      {
-        $b = $deps;
-        if ( !$this->check_acl_scope($deps[$i], $paths->namespace) )
-        {
-          // Action $type depends on action $deps[$i] which cannot be satisfied because $deps[$i] is out of scope.
-          // echo '<pre>' . enano_debug_print_backtrace(true) . '</pre>';
-          trigger_error("acl_check_deps: $type depends on {$deps[$i]} which is not within scope of $paths->namespace; this indicats a bug in ACL rule specification", E_USER_WARNING);
-          return false;
-        }
-        $deps = array_merge($deps, $this->acl_deps[$deps[$i]]);
-        if( $b == $deps )
-        {
-          break 2;
-        }
-        $j = sizeof($deps);
-      }
-    }
-    $debugdata = array();
-    foreach($deps as $d)
-    {
-      // Our dependencies are fully resolved, so tell get_permissions() to not recursively call this function
-      if ( !$this->get_permissions($d, true) )
-      {
-        if ( $debug )
-        {
-          $debugdata[] = $d;
-        }
-        else
-        {
-          return false;
-        }
-      }
-    }
-    return $debug ? $debugdata : true;
-  }
-  
-  /**
-   * Makes a CAPTCHA code and caches the code in the database
-   * @param int $len The length of the code, in bytes
-   * @param string Optional, the hash to reuse
-   * @return string A unique identifier assigned to the code. This hash should be passed to sessionManager::getCaptcha() to retrieve the code.
-   */
-  
-  function make_captcha($len = 7, $hash = '')
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $code = $this->generate_captcha_code($len);
-    if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $hash) )
-      $hash = md5(microtime() . mt_rand());
-    $session_data = $db->escape(serialize(array()));
-    
-    // sanity check
-    if ( !is_valid_ip(@$_SERVER['REMOTE_ADDR']) || !is_int($this->user_id) )
-      return false;
-    
-    $this->sql('DELETE FROM ' . table_prefix . "captcha WHERE session_id = '$hash';");
-    $this->sql('INSERT INTO ' . table_prefix . 'captcha(session_id, code, session_data, source_ip, user_id)' . " VALUES('$hash', '$code', '$session_data', '{$_SERVER['REMOTE_ADDR']}', {$this->user_id});");
-    return $hash;
-  }
-  
-  /**
-   * Generates a "pronouncable" or "human-friendly" word using various phonics rules
-   * @param int Optional. The length of the word.
-   * @return string
-   */
-  
-  function generate_captcha_code($len = 7)
-  {
-    // don't use k and x, they get mixed up a lot
-    $consonants = 'bcdfghmnpqrsvwyz';
-    $vowels = 'aeiou';
-    $prev = 'vowel';
-    $prev_l = '';
-    $word = '';
-    $allow_next_vowel = true;
-    for ( $i = 0; $i < $len; $i++ )
-    {
-      if ( $prev == 'vowel' )
-      {
-        $allow_next_vowel = false;
-        if ( $prev_l == 'o' && mt_rand(0, 3) == 3 && $allow_next_vowel )
-          $word .= 'i';
-        else if ( $prev_l == 'q' && mt_rand(0, 3) != 1 && $allow_next_vowel )
-          $word .= 'u';
-        else if ( $prev_l == 'o' && mt_rand(0, 3) == 2 && $allow_next_vowel )
-          $word .= 'u';
-        else if ( $prev_l == 'a' && mt_rand(0, 3) == 3 && $allow_next_vowel )
-          $word .= 'i';
-        else if ( $prev_l == 'a' && mt_rand(0, 10) == 7 && $allow_next_vowel )
-          $word .= 'o';
-        else if ( $prev_l == 'a' && mt_rand(0, 7) == 2 && $allow_next_vowel )
-          $word .= 'u';
-        else
-        {
-          $allow_next_vowel = true;
-          $word .= $consonants{mt_rand(0, (strlen($consonants)-1))};
-        }
-      }
-      else if ( $prev == 'consonant' )
-      {
-        if ( $prev_l == 'p' && mt_rand(0, 7) == 4 )
-          $word .= 't';
-        else if ( $prev_l == 'p' && mt_rand(0, 5) == 1 )
-          $word .= 'h';
-        else
-          $word .= $vowels{mt_rand(0, (strlen($vowels)-1))};
-      }
-      $prev_l = substr($word, -1);
-      $l = ( mt_rand(0, 1) == 1 ) ? strtoupper($prev_l) : strtolower($prev_l);
-      $word = substr($word, 0, -1) . $l;
-      if ( strstr('aeiou', $prev_l) )
-        $prev = 'vowel';
-      else
-        $prev = 'consonant';
-    }
-    return $word;
-  }
-  
-  /**
-   * For the given code ID, returns the correct CAPTCHA code, or false on failure
-   * @param string $hash The unique ID assigned to the code
-   * @param bool If true, the code is NOT deleted from the database. Use with caution!
-   * @return string The correct confirmation code
-   */
-  
-  function get_captcha($hash, $nodelete = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $hash) )
-    {
-      die("session manager: bad captcha_hash $hash");
-      return false;
-    }
-    
-    // sanity check
-    if ( !is_valid_ip(@$_SERVER['REMOTE_ADDR']) )
-    {
-      die("session manager insanity: bad REMOTE_ADDR or invalid UID");
-      return false;
-    }
-    
-    $q = $this->sql('SELECT code_id, code FROM ' . table_prefix . "captcha WHERE session_id = '$hash' AND source_ip = '{$_SERVER['REMOTE_ADDR']}';");
-    if ( $db->numrows() < 1 )
-    {
-      return false;
-    }
-    
-    list($code_id, $code) = $db->fetchrow_num();
-    
-    $db->free_result();
-    
-    // delete it
-    if ( !$nodelete )
-      $this->sql('DELETE FROM ' . table_prefix . "captcha WHERE code_id = $code_id;");
-    
-    return $code;
-  }
-  
-  /**
-   * (AS OF 1.0.2: Deprecated. Captcha codes are now killed on first fetch for security.) Deletes all CAPTCHA codes cached in the DB for this user.
-   */
-  
-  function kill_captcha()
-  {
-    return true;
-  }
-  
-  /**
-   * Generates a random password.
-   * @param int $length Optional - length of password
-   * @return string
-   */
-   
-  function random_pass($length = 10)
-  {
-    $valid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+@#%&<>';
-    $valid_chars = enano_str_split($valid_chars);
-    $ret = '';
-    for ( $i = 0; $i < $length; $i++ )
-    {
-      $ret .= $valid_chars[mt_rand(0, count($valid_chars)-1)];
-    }
-    return $ret;
-  }
-  
-  /**
-   * Generates some Javascript that calls the AES encryption library. Put this after your </form>.
-   * @param string The name of the form
-   * @param string The name of the password field
-   * @param string The name of the field that switches encryption on or off
-   * @param string The name of the field that contains the encryption key
-   * @param string The name of the field that will contain the encrypted password
-   * @param string The name of the field that handles MD5 challenge data
-   * @param string The name of the field that tells if the server supports DiffieHellman
-   * @param string The name of the field with the DiffieHellman public key
-   * @param string The name of the field that the client should populate with its public key
-   * @return string
-   */
-   
-  function aes_javascript($form_name, $pw_field, $use_crypt = 'use_crypt', $crypt_key = 'crypt_key', $crypt_data = 'crypt_data', $challenge = 'challenge_data', $dh_supported = 'dh_supported', $dh_pubkey = 'dh_public_key', $dh_client_pubkey = 'dh_client_public_key')
-  {
-    $code = '
-      <script type="text/javascript">
-          
-          function runEncryption(nowhiteout)
-          {
-            var frm = document.forms.'.$form_name.';
-            if ( !nowhiteout )
-              whiteOutForm(frm);
-            
-            load_component(\'crypto\');
-            var testpassed = ' . ( ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0') ? 'false; // CRYPTO-AUTH DISABLED ON USER REQUEST // ' : '' ) . '( aes_self_test() && md5_vm_test() );
-            var use_diffiehellman = false;' . "\n";
-    if ( $dh_supported && $dh_pubkey )
-    {
-      $code .= <<<EOF
-            if ( frm.$dh_supported.value == 'true' && !is_iPhone )
-              use_diffiehellman = true;
+		$message = $lang->get('userfuncs_passreset_email', array(
+				'username' => $row['username'],
+				'site_name' => $site_name,
+				'remote_addr' => $_SERVER['REMOTE_ADDR'],
+				'temp_pass' => $temp_pass
+			));
+		
+		if(getConfig('smtp_enabled') == '1')
+		{
+			$result = smtp_send_email($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
+			if($result == 'success')
+			{
+				$result = true;
+			}
+			else
+			{
+				echo '<p>'.$result.'</p>';
+				$result = false;
+			}
+		} else {
+			$result = mail($row['email'], getConfig('site_name').' password reset', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
+		}
+		return $result;
+	}
+	
+	/**
+ 	* Sets the temporary password for the specified user to whatever is specified.
+ 	* @param int $user_id
+ 	* @param string $password
+ 	* @return bool
+ 	*/
+ 	
+	function register_temp_password($user_id, $password)
+	{
+		global $db;
+		if ( !is_int($user_id) )
+			return false;
+		
+		$this->sql('SELECT password_salt FROM ' . table_prefix . "users WHERE user_id = $user_id;");
+		if ( $db->numrows() < 1 )
+			return false;
+		
+		list($salt) = $db->fetchrow_num();
+		$db->free_result();
+		
+		$temp_pass = hmac_sha1($password, $salt);
+		$this->sql('UPDATE '.table_prefix.'users SET temp_password=\'' . $temp_pass . '\',temp_password_time='.time().' WHERE user_id='.intval($user_id).';');
+	}
+	
+	/**
+ 	* Sends a request to the admin panel to have the username $u activated.
+ 	* @param string $u The username of the user requesting activation
+ 	*/
+	
+	function admin_activation_request($u)
+	{
+		global $db;
+		$this->sql('INSERT INTO '.table_prefix.'logs(log_type, action, time_id, date_string, author, edit_summary) VALUES(\'admin\', \'activ_req\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$this->username.'\', \''.$db->escape($u).'\');');
+	}
+	
+	/**
+ 	* Activates a user account. If the action fails, a report is sent to the admin.
+ 	* @param string $user The username of the user requesting activation
+ 	* @param string $key The activation key
+ 	*/
+	
+	function activate_account($user, $key)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$q = $this->sql('SELECT 1 FROM ' . table_prefix . 'users WHERE username = \''.$db->escape($user).'\' AND activation_key = \''.$db->escape($key).'\'');
+		if ( $db->numrows() > 0 )
+		{
+			$new_key = md5(AESCrypt::randkey());
+			$this->sql('UPDATE ' . table_prefix . 'users SET account_active = 1, activation_key = \'' . $new_key . '\' WHERE username=\''.$db->escape($user).'\' AND activation_key=\''.$db->escape($key).'\';');
+			$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_good\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')');
+			return true;
+		}
+		else
+		{
+			$this->sql('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,edit_summary) VALUES(\'security\', \'activ_bad\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($user).'\', \''.$_SERVER['REMOTE_ADDR'].'\')');
+			return false;
+		}
+	}
+	
+	/**
+ 	* For a given user level identifier (USER_LEVEL_*), returns a string describing that user level.
+ 	* @param int User level
+ 	* @param bool If true, returns a shorter string. Optional.
+ 	* @return string
+ 	*/
+	
+	function userlevel_to_string($user_level, $short = false)
+	{
+		global $lang;
+		
+		static $levels = array(
+				'short' => array(
+						USER_LEVEL_GUEST => 'Guest',
+						USER_LEVEL_MEMBER => 'Member',
+						USER_LEVEL_CHPREF => 'Sensitive preferences changeable',
+						USER_LEVEL_MOD => 'Moderator',
+						USER_LEVEL_ADMIN => 'Administrative'
+					),
+				'long' => array(
+						USER_LEVEL_GUEST => 'Low - guest privileges',
+						USER_LEVEL_MEMBER => 'Standard - normal member level',
+						USER_LEVEL_CHPREF => 'Medium - user can change his/her own e-mail address and password',
+						USER_LEVEL_MOD => 'High - moderator privileges',
+						USER_LEVEL_ADMIN => 'Highest - administrative privileges'
+					),
+				'l10n' => false
+			);
+		
+		if ( is_object($lang) && !$levels['l10n'] )
+		{
+			$levels = array(
+					'short' => array(
+							USER_LEVEL_GUEST => $lang->get('user_level_short_guest'),
+							USER_LEVEL_MEMBER => $lang->get('user_level_short_member'),
+							USER_LEVEL_CHPREF => $lang->get('user_level_short_chpref'),
+							USER_LEVEL_MOD => $lang->get('user_level_short_mod'),
+							USER_LEVEL_ADMIN => $lang->get('user_level_short_admin')
+						),
+					'long' => array(
+							USER_LEVEL_GUEST => $lang->get('user_level_long_guest'),
+							USER_LEVEL_MEMBER => $lang->get('user_level_long_member'),
+							USER_LEVEL_CHPREF => $lang->get('user_level_long_chpref'),
+							USER_LEVEL_MOD => $lang->get('user_level_long_mod'),
+							USER_LEVEL_ADMIN => $lang->get('user_level_long_admin')
+						),
+					'l10n' => true
+				);
+		}
+		
+		$key = ( $short ) ? 'short' : 'long';
+		if ( isset($levels[$key][$user_level]) )
+		{
+			return $levels[$key][$user_level];
+		}
+		else
+		{
+			if ( $short )
+			{
+				return ( is_object($lang) ) ? $lang->get('user_level_short_unknown', array('user_level' => $user_level)) : "Unknown - $user_level";
+			}
+			else
+			{
+				return ( is_object($lang) ) ? $lang->get('user_level_long_unknown', array('user_level' => $user_level)) : "Unknown level ($user_level)";
+			}
+		}
+		
+		return 'Linux rocks!';
+		
+	}
+	
+	/**
+ 	* Change a user's e-mail address.
+ 	* @param int $user_id The user ID of the user to update - this cannot be changed
+ 	* @param string $email The new e-mail address
+ 	* @return string 'success' if successful, or array of error strings on failure
+ 	*/
+ 	
+	function change_email($user_id, $email)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Create some arrays
+		
+		$errors = array(); // Used to hold error strings
+		
+		// Scan the user ID for problems
+		if ( intval($user_id) < 1 )
+			$errors[] = 'SQL injection attempt';
+		
+		$user_id = intval($user_id);
+		
+		// Verify e-mail address
+		if ( !check_email_address($email) )
+			$errors[] = 'user_err_email_not_valid';
+		
+		if ( count($errors) > 0 )
+			return $errors;
+		
+		// Make query
+		$email = $db->escape($email);
+		$q = $db->sql_query('UPDATE ' . table_prefix . "users SET email = '$email' WHERE user_id = $user_id;");
+		
+		// We also need to trigger re-activation.
+		switch(getConfig('account_activation', 'none'))
+		{
+			case 'user':
+			case 'admin':
+				
+				// Note: even with admin activation, activation e-mails are sent when an e-mail is changed.
+				
+				if ( $session->user_level >= USER_LEVEL_MOD && getConfig('account_activation') == 'admin' )
+					// Trust admins and moderators
+					break;
+				
+				// retrieve username
+				if ( !$username )
+				{
+					$q = $this->sql('SELECT username FROM ' . table_prefix . "users WHERE user_id = $user_id;");
+					if($db->numrows() < 1)
+					{
+						$errors[] = 'The username could not be selected.';
+					}
+					else
+					{
+						$row = $db->fetchrow();
+						$username = $row['username'];
+					}
+				}
+				if ( !$username )
+					return $errors;
+				
+				// Generate an activation key
+				$actkey = sha1 ( microtime() . mt_rand() );
+				$a = $this->send_activation_mail($username, $actkey);
+				if(!$a)
+				{
+					$this->admin_activation_request($username);
+				}
+				// Deactivate the account until e-mail is confirmed
+				$q = $db->sql_query('UPDATE ' . table_prefix . "users SET account_active = 0, activation_key = '$actkey' WHERE user_id = $user_id;");
+				break;
+		}
+		
+		// Yay! We're done
+		return 'success';
+	}
+	
+	/**
+ 	* Sets a user's password.
+ 	* @param int|string User ID or username
+ 	* @param string New password
+ 	*/
+	
+	function set_password($user, $password)
+	{
+		// Generate new password and salt
+		$hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
+		$password_hmac = hmac_sha1($password, $hmac_secret);
+		
+		// Figure out how we want to specify the user
+		$uidcol = is_int($user) ? "user_id = $user" : ENANO_SQLFUNC_LOWERCASE . "(username) = '" . strtolower($this->prepare_text($user)) . "'";
+		
+		// Perform update
+		$this->sql('UPDATE ' . table_prefix . "users SET old_encryption = 0, password = '$password_hmac', password_salt = '$hmac_secret' WHERE $uidcol;");
+		
+		return true;
+	}
+	
+	/**
+ 	* Encrypts a string using the site's private key.
+ 	* @param string
+ 	* @param int Return type - one of ENC_BINARY, ENC_HEX, ENC_BASE64
+ 	* @return string
+ 	*/
+	
+	function pk_encrypt($string, $return_type = ENC_HEX)
+	{
+		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+		return $aes->encrypt($string, $this->private_key, $return_type);
+	}
+	
+	/**
+ 	* Encrypts a string using the site's private key.
+ 	* @param string
+ 	* @param int Input type - one of ENC_BINARY, ENC_HEX, ENC_BASE64
+ 	* @return string
+ 	*/
+	
+	function pk_decrypt($string, $input_type = ENC_HEX)
+	{
+		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+		return $aes->decrypt($string, $this->private_key, $input_type);
+	}
+	
+	#
+	# USER RANKS
+	#
+	
+	/**
+ 	* SYNOPSIS OF THE RANK SYSTEM
+ 	* Enano's rank logic calculates a user's rank based on a precedence scale. The way things are checked is:
+ 	*   1. Check to see if the user has a specific rank assigned. Use that if possible.
+ 	*   2. Check the user's primary group to see if it specifies a rank. Use that if possible.
+ 	*   3. Check the other groups a user is in. If one that has a custom rank is encountered, use that rank.
+ 	*   4. See if the user's user level has a specific rank hard-coded to be associated with it. (Always overrideable as can be seen above)
+ 	*   5. Use the "member" rank
+ 	*/
+	
+	/**
+ 	* Generates a textual SQL query for fetching rank data to be sent to calculate_user_rank().
+ 	* @param string Text to append, possibly a WHERE clause or so
+ 	* @return string
+ 	*/
+	
+	function generate_rank_sql($append = '')
+	{
+		// Generate level-to-rank associations
+		$assoc = array();
+		foreach ( $this->level_rank_table as $level => $rank )
+		{
+			$assoc[] = "        ( u.user_level = $level AND rl.rank_id = $rank )";
+		}
+		$assoc = implode(" OR\n", $assoc) . "\n";
+		
+		$gid_col = ( ENANO_DBLAYER == 'PGSQL' ) ?
+			'array_to_string(' . table_prefix . 'array_accum(m.group_id), \',\') AS group_list' :
+			'GROUP_CONCAT(m.group_id) AS group_list';
+		
+		// The actual query
+		$sql = "SELECT u.user_id, u.username, u.user_level, u.user_group, u.user_rank, u.user_title, g.group_rank,\n"
+ 				. "       COALESCE(ru.rank_id,    rg.rank_id,    rl.rank_id,    rd.rank_id   ) AS rank_id,\n"
+ 				. "       COALESCE(ru.rank_title, rg.rank_title, rl.rank_title, rd.rank_title) AS rank_title,\n"
+ 				. "       COALESCE(ru.rank_style, rg.rank_style, rl.rank_style, rd.rank_style) AS rank_style,\n"
+ 				. "       rg.rank_id AS group_rank_id,\n"
+ 				. "       ( ru.rank_id IS NULL AND rg.rank_id IS NULL ) AS using_default,\n"
+ 				. "       ( ru.rank_id IS NULL AND rg.rank_id IS NOT NULL ) AS using_group,\n"
+ 				. "       ( ru.rank_id IS NOT NULL ) AS using_user,\n"
+ 				. "       u.user_rank_userset,\n"
+ 				. "       $gid_col\n"
+ 				. "  FROM " . table_prefix . "users AS u\n"
+ 				. "  LEFT JOIN " . table_prefix . "groups AS g\n"
+ 				. "    ON ( g.group_id = u.user_group )\n"
+ 				. "  LEFT JOIN " . table_prefix . "group_members AS m\n"
+ 				. "    ON ( u.user_id = m.user_id )\n"
+ 				. "  LEFT JOIN " . table_prefix . "ranks AS ru\n"
+ 				. "    ON ( u.user_rank = ru.rank_id )\n"
+ 				. "  LEFT JOIN " . table_prefix . "ranks AS rg\n"
+ 				. "    ON ( g.group_rank = rg.rank_id )\n"
+ 				. "  LEFT JOIN " . table_prefix . "ranks AS rl\n"
+ 				. "    ON (\n"
+ 				. $assoc
+ 				. "      )\n"
+ 				. "  LEFT JOIN " . table_prefix . "ranks AS rd\n"
+ 				. "    ON ( rd.rank_id = 1 )$append\n"
+ 				. "  GROUP BY u.user_id, u.username, u.user_level, u.user_group, u.user_rank, u.user_title, u.user_rank_userset, g.group_rank,\n"
+ 				. "       ru.rank_id, ru.rank_title, ru.rank_style,rg.rank_id, rg.rank_title, rg.rank_style,\n"
+ 				. "       rl.rank_id, rl.rank_title, rl.rank_style,rd.rank_id, rd.rank_title, rd.rank_style;";
+		
+		return $sql;
+	}
+	
+	/**
+ 	* Returns an associative array with a user's rank information.
+ 	* The array will contain the following values:
+ 	*   username: string  The user's username
+ 	*   user_id:  integer Numerical user ID
+ 	*   rank_id:  integer Numerical rank ID
+ 	*   rank:     string  The user's current rank
+ 	*   title:    string  The user's custom user title if applicable; should be displayed one line below the rank
+ 	*   style:    string  CSS for the username
+ 	* @param int|string Username *or* user ID
+ 	* @return array or false on failure
+ 	*/
+	
+	function get_user_rank($id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $user_ranks;
+		// cache info in RAM if possible
+		static $_cache = array();
+		
+		if ( is_int($id) && $id == 0 )
+			$id = 1;
+		
+		if ( is_int($id) )
+			$col = "u.user_id = $id";
+		else if ( is_string($id) )
+			$col = ENANO_SQLFUNC_LOWERCASE . "(username) = " . ENANO_SQLFUNC_LOWERCASE . "('" . $db->escape($id) . "')";
+		else
+			// invalid parameter
+			return false;
+			
+		// check the RAM cache
+		if ( isset($_cache[$id]) )
+			return $_cache[$id];
+		
+		// check the disk cache
+		if ( is_int($id) )
+		{
+			if ( isset($user_ranks[$id]) )
+			{
+				$_cache[$id] =& $user_ranks[$id];
+				return $user_ranks[$id];
+			}
+		}
+		else if ( is_string($id) )
+		{
+			foreach ( $user_ranks as $key => $valarray )
+			{
+				if ( is_string($key) && strtolower($key) == strtolower($id) )
+				{
+					$_cache[$id] = $valarray;
+					return $valarray;
+				}
+			}
+		}
+		
+		$sql = $this->generate_rank_sql("\n  WHERE $col");
+		
+		$q = $this->sql($sql);
+		// any results?
+		if ( $db->numrows() < 1 )
+		{
+			// nuttin'.
+			$db->free_result();
+			$_cache[$id] = false;
+			return false;
+		}
+		
+		// Found something.
+		$row = $db->fetchrow();
+		$db->free_result();
+		
+		$row = $this->calculate_user_rank($row);
+		
+		$_cache[$id] = $row;
+		return $row;
+	}
+	
+	/**
+ 	* Performs the actual rank calculation based on the contents of a row.
+ 	* @param array
+ 	* @return array
+ 	*/
+	
+	function calculate_user_rank($row)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		static $rank_cache = array();
+		static $group_ranks = array();
+		
+		// try to cache that rank info
+		if ( !isset($rank_cache[ intval($row['rank_id']) ]) && $row['rank_id'] )
+		{
+			$rank_cache[ intval($row['rank_id']) ] = array(
+					'rank_id' => intval($row['rank_id']),
+					'rank_title' => intval($row['rank_title']),
+					'rank_style' => intval($row['rank_style'])
+				);
+		}
+		// cache group info (if appropriate)
+		if ( $row['using_group'] && !isset($group_ranks[ intval($row['user_group']) ]) )
+		{
+			$group_ranks[ intval($row['user_group']) ] = intval($row['group_rank_id']);
+		}
+		
+		// sanitize and process the as-of-yet rank data
+		$row['rank_id'] = intval($row["rank_id"]);
+		$row['rank_title'] = $row["rank_title"];
+		
+		// if we're falling back to some default, then see if we can use one of the user's other groups
+		if ( $row['using_default'] && !empty($row['group_list']) )
+		{
+			$group_list = explode(',', $row['group_list']);
+			if ( array_walk($group_list, 'intval') )
+			{
+				// go through the group list and see if any of them has a rank assigned
+				foreach ( $group_list as $group_id )
+				{
+					// cached in RAM? Preferably use that.
+					if ( !isset($group_ranks[$group_id]) )
+					{
+						// Not cached - grab it
+						$q = $this->sql('SELECT group_rank FROM ' . table_prefix . "groups WHERE group_id = $group_id;");
+						if ( $db->numrows() < 1 )
+						{
+							$db->free_result();
+							continue;
+						}
+						list($result) = $db->fetchrow_num();
+						$db->free_result();
+						
+						if ( $result === null || $result < 1 )
+						{
+							$group_ranks[$group_id] = false;
+						}
+						else
+						{
+							$group_ranks[$group_id] = intval($result);
+						}
+					}
+					// we've got it now
+					if ( $group_ranks[$group_id] )
+					{
+						// found a group with a rank assigned
+						// so get the rank info
+						$rank_id =& $group_ranks[$group_id];
+						if ( !isset($rank_cache[$rank_id]) )
+						{
+							$q = $this->sql('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = $rank_id;");
+							if ( $db->numrows() < 1 )
+							{
+								$db->free_result();
+								continue;
+							}
+							$rank_cache[$rank_id] = $db->fetchrow();
+							$db->free_result();
+						}
+						// set the final rank parameters
+						// die("found member-of-group exception with uid {$row['user_id']} gid $group_id rid $rank_id rt {$rank_cache[$rank_id]['rank_title']}");
+						$row['rank_id'] = $rank_id;
+						$row['rank_title'] = $rank_cache[$rank_id]['rank_title'];
+						$row['rank_style'] = $rank_cache[$rank_id]['rank_style'];
+						break;
+					}
+				}
+			}
+		}
+		
+		if ( $row['user_title'] === NULL )
+			$row['user_title'] = false;
+		
+		$row['user_id'] = intval($row['user_id']);
+		$row['user_level'] = intval($row['user_level']);
+		$row['user_group'] = intval($row['user_group']);
+		
+		unset($row['user_rank'], $row['group_rank'], $row['group_list'], $row['using_default'], $row['using_group'], $row['user_level'], $row['user_group'], $row['username']);
+		return $row;
+	}
+	
+	/**
+ 	* Get the list of ranks that a user is allowed to use. Returns false if they cannot change it.
+ 	* @param string|int User ID or username
+ 	* @return array Associative by rank ID
+ 	*/
+	
+	function get_user_possible_ranks($id)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// cache info in RAM if possible
+		static $_cache = array();
+		
+		if ( is_int($id) && $id == 0 )
+			$id = 1;
+		
+		if ( is_int($id) )
+			$col = "u.user_id = $id";
+		else if ( is_string($id) )
+			$col = ENANO_SQLFUNC_LOWERCASE . "(username) = " . ENANO_SQLFUNC_LOWERCASE . "('" . $db->escape($id) . "')";
+		else
+			// invalid parameter
+			return false;
+			
+		// check the RAM cache
+		if ( isset($_cache[$id]) )
+			return $_cache[$id];
+		
+		$sql = $this->generate_rank_sql("\n  WHERE $col");
+		
+		$q = $this->sql($sql);
+		// any results?
+		if ( $db->numrows() < 1 )
+		{
+			// nuttin'.
+			$db->free_result();
+			$_cache[$id] = false;
+			return false;
+		}
+		
+		// Found something.
+		$row = $db->fetchrow();
+		$db->free_result();
+		
+		if ( $row['using_user'] && !$row['user_rank_userset'] )
+		{
+			// The user's rank was set manually by an admin.
+			$result = array(
+				array(
+					'rank_id' => $row['rank_id'],
+					'rank_title' => $row['rank_title'],
+					'rank_style' => $row['rank_style'],
+					'rank_type' => 'user'
+					)
+				);
+			$_cache[$id] = $result;
+			return $result;
+		}
+		
+		// copy the result to a more permanent array so we can reference this later
+		$current_settings = $row;
+		unset($row);
+		
+		$result = array();
+		
+		// first rank available to us will be the one set by the user's user level
+		if ( isset($this->level_rank_table[$current_settings['user_level']]) )
+		{
+			$q = $this->sql('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = {$this->level_rank_table[$this->user_level]};");
+			if ( $db->numrows() > 0 )
+			{
+				$row = $db->fetchrow();
+				$row['rank_type'] = 'ulevel';
+				
+				$result[] = $row;
+			}
+			$db->free_result();
+		}
+		
+		// for each group the user is in, figure out if it has a rank associated with it
+		$group_list = explode(',', $current_settings['group_list']);
+		foreach ( $group_list as $group_id )
+		{
+			$group_id = intval($group_id);
+			$q = $this->sql('SELECT r.rank_id, r.rank_title, r.rank_style FROM ' . table_prefix . "groups AS g\n"
+										. "  LEFT JOIN " . table_prefix . "ranks AS r\n"
+										. "    ON ( g.group_rank = r.rank_id )\n"
+										. "  WHERE g.group_id = $group_id\n"
+										. "    AND r.rank_id IS NOT NULL;");
+			if ( $db->numrows() > 0 )
+			{
+				$row = $db->fetchrow();
+				$row['rank_type'] = 'group';
+				
+				$result[] = $row;
+			}
+			$db->free_result();
+		}
+		
+		$_cache[$id] = $result;
+		return $result;
+	}
+	
+	#
+	# Access Control Lists
+	#
+	
+	/**
+ 	* Creates a new permission field in memory. If the permissions are set in the database, they are used. Otherwise, $default_perm is used.
+ 	* @param string $acl_type An identifier for this field
+ 	* @param int $default_perm Whether permission should be granted or not if it's not specified in the ACLs.
+ 	* @param string $desc A human readable name for the permission type
+ 	* @param array $deps The list of dependencies - this should be an array of ACL types
+ 	* @param string $scope Which namespaces this field should apply to. This should be either a pipe-delimited list of namespace IDs or just "All".
+ 	*/
+ 	
+	function register_acl_type($acl_type, $default_perm = AUTH_DISALLOW, $desc = false, $deps = Array(), $scope = 'All')
+	{
+		if(isset($this->acl_types[$acl_type]))
+			return false;
+		else
+		{
+			if(!$desc)
+			{
+				$desc = capitalize_first_letter(str_replace('_', ' ', $acl_type));
+			}
+			$this->acl_types[$acl_type] = $default_perm;
+			$this->acl_descs[$acl_type] = $desc;
+			$this->acl_deps[$acl_type] = $deps;
+			$this->acl_scope[$acl_type] = explode('|', $scope);
+		}
+		return true;
+	}
+	
+	/**
+ 	* Tells us whether permission $type is allowed or not based on the current rules.
+ 	* @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
+ 	* @param bool $no_deps If true, disables dependency checking
+ 	* @return bool True if allowed, false if denied or if an error occured
+ 	*/
+ 	
+	function get_permissions($type, $no_deps = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( isset( $this->perms[$type] ) )
+		{
+			if ( $this->perms[$type] == AUTH_DENY )
+				$ret = false;
+			else if ( $this->perms[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
+				$ret = true;
+			else if ( $this->perms[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
+				$ret = false;
+			else if ( $this->perms[$type] == AUTH_ALLOW )
+				$ret = true;
+			else if ( $this->perms[$type] == AUTH_DISALLOW )
+				$ret = false;
+		}
+		else if(isset($this->acl_types[$type]))
+		{
+			if ( $this->acl_types[$type] == AUTH_DENY )
+				$ret = false;
+			else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
+				$ret = true;
+			else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
+				$ret = false;
+			else if ( $this->acl_types[$type] == AUTH_ALLOW )
+				$ret = true;
+			else if ( $this->acl_types[$type] == AUTH_DISALLOW )
+				$ret = false;
+		}
+		else
+		{
+			// ACL type is undefined
+			trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING);
+			return false; // Be on the safe side and deny access
+		}
+		if ( !$no_deps )
+		{
+			if ( !$this->acl_check_deps($type) )
+				return false;
+		}
+		return $ret;
+	}
+	
+	/**
+ 	* Fetch the permissions that apply to the current user for the page specified. The object you get will have the get_permissions method
+ 	* and several other abilities.
+ 	* @param string $page_id
+ 	* @param string $namespace
+ 	* @return object
+ 	*/
+ 	
+	function fetch_page_acl($page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( count ( $this->acl_base_cache ) < 1 )
+		{
+			// Permissions table not yet initialized
+			return false;
+		}
+		
+		// cache of permission objects (to save RAM and SQL queries)
+		static $objcache = array();
+		
+		if ( count($objcache) == 0 )
+		{
+			foreach ( $paths->nslist as $key => $_ )
+			{
+				$objcache[$key] = array();
+			}
+		}
+		
+		if ( isset($objcache[$namespace][$page_id]) )
+		{
+			return $objcache[$namespace][$page_id];
+		}
+		
+		$objcache[$namespace][$page_id] = new Session_ACLPageInfo( $page_id, $namespace, $this->acl_types, $this->acl_descs, $this->acl_deps, $this->acl_base_cache );
+		$object =& $objcache[$namespace][$page_id];
+		
+		profiler_log("session: fetched ACLs for page {$namespace}:{$page_id}");
+		
+		return $object;
+	}
+	
+	/**
+ 	* Fetch the permissions that apply to an arbitrary user for the page specified. The object you get will have the get_permissions method
+ 	* and several other abilities.
+ 	* @param int|string $user_id_or_name; user ID *or* username of the user
+ 	* @param string $page_id; if null, will be default effective permissions. 
+ 	* @param string $namespace; if null, will be default effective permissions.
+ 	* @return object
+ 	*/
+	
+	function fetch_page_acl_user($user_id_or_name, $page_id, $namespace)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// cache user info
+		static $user_info_cache = null;
+		
+		if ( isset($user_info_cache[$user_id_or_name]) )
+		{
+			$user_id =& $user_info_cache[$user_id_or_name]['user_id'];
+			$groups =& $user_info_cache[$user_id_or_name]['groups'];
+		}
+		else
+		{
+			$uid_column = ( is_int($user_id_or_name) ) ? "user_id = $user_id_or_name" : "username = '" . $db->escape($user_id_or_name) . "'";
+			$q = $db->sql_query('SELECT u.user_id, m.group_id, g.group_name FROM ' . table_prefix . "users AS u\n"
+												. "  LEFT JOIN " . table_prefix . "group_members AS m\n"
+												. "    ON ( ( u.user_id = m.user_id AND m.pending = 0 ) OR m.member_id IS NULL )\n"
+												. "  LEFT JOIN " . table_prefix . "groups AS g\n"
+												. "    ON ( g.group_id = m.group_id )\n"
+												. "  WHERE $uid_column;");
+			if ( !$q )
+				$db->_die();
+			
+			// The l10n engine takes care of this later.
+			$groups = array(1 => 'Everyone');
+			
+			if ( $row = $db->fetchrow() )
+			{
+				$user_id = intval($row['user_id']);
+				if ( $row['group_id'] )
+				{
+					do
+					{
+						$groups[ intval($row['group_id'] ) ] = $row['group_name'];
+					}
+					while ( $row = $db->fetchrow() );
+				}
+				$db->free_result();
+			}
+			else
+			{
+				$db->free_result();
+				throw new Exception('Unknown user ID or username');
+			}
+			
+			$user_info_cache[$user_id_or_name] = array(
+					'user_id' => $user_id,
+					'groups' => $groups
+				);
+		}
+		
+		// cache base permissions
+		static $base_cache = array();
+		if ( !isset($base_cache[$user_id_or_name]) )
+		{
+			$base_cache[$user_id_or_name] = $this->acl_types;
+			$current_perms =& $base_cache[$user_id_or_name];
+			$current_perms['__resolve_table'] = array();
+			
+			$bs = 'SELECT rules, target_type, target_id, rule_id, page_id, namespace, g.group_name FROM '.table_prefix."acl AS a\n"
+					. "  LEFT JOIN " . table_prefix . "groups AS g\n"
+					. "    ON ( ( a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id ) OR ( a.target_type != " . ACL_TYPE_GROUP . " ) )\n"
+					. '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
+					. '  ( ';
+		
+			$q = Array();
+			$q[] = '( target_type='.ACL_TYPE_USER.' AND target_id= ' . $user_id . ' )';
+			if(count($groups) > 0)
+			{
+				foreach($groups as $g_id => $g_name)
+				{
+					$q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
+				}
+			}
+			$bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
+			$q = $this->sql($bs);
+			foreach ( $this->acl_types as $perm_type => $_ )
+			{
+				// init the resolver table with blanks
+				$current_perms['__resolve_table'][$perm_type] = array(
+						'src' => ACL_INHERIT_ENANO_DEFAULT,
+						'rule_id' => -1
+					);
+			}
+			if ( $row = $db->fetchrow() )
+			{
+				do {
+					$rules = $this->string_to_perm($row['rules']);
+					$is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
+					// track where these rulings are coming from
+					$src = ( $is_everyone ) ? ACL_INHERIT_GLOBAL_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_GLOBAL_GROUP : ACL_INHERIT_GLOBAL_USER );
+					foreach ( $rules as $perm_type => $_ )
+					{
+						$current_perms['__resolve_table'][$perm_type] = array(
+								'src' => $src,
+								'rule_id' => $row['rule_id']
+							);
+						if ( $row['group_name'] )
+						{
+							$current_perms['__resolve_table'][$perm_type]['group_name'] = $row['group_name'];
+						}
+					}
+					// merge it in
+					$current_perms = $this->acl_merge($current_perms, $rules, $is_everyone, $_defaults_used);
+				} while ( $row = $db->fetchrow() );
+			}
+		}
+		
+		// cache of permission objects (to save RAM and SQL queries)
+		static $objcache = array();
+		
+		if ( count($objcache) == 0 )
+		{
+			foreach ( $paths->nslist as $key => $_ )
+			{
+				$objcache[$key] = array();
+			}
+		}
+		
+		if ( !isset($objcache[$namespace][$page_id]) )
+		{
+			$objcache[$namespace][$page_id] = array();
+		}
+		
+		if ( isset($objcache[$namespace][$page_id][$user_id_or_name]) )
+		{
+			return $objcache[$namespace][$page_id][$user_id_or_name];
+		}
+		
+		//if ( !isset( $paths->pages[$paths->nslist[$namespace] . $page_id] ) )
+		//{
+		//  // Page does not exist
+		//  return false;
+		//}
+		
+		$objcache[$namespace][$page_id][$user_id_or_name] = new Session_ACLPageInfo(
+			$page_id,                                        // $page_id, 
+			$namespace,                                      // $namespace,
+			$this->acl_types,                                // $acl_types,
+			$this->acl_descs,                                // $acl_descs,
+			$this->acl_deps,                                 // $acl_deps,
+			$base_cache[$user_id_or_name],                   // $base,
+			$user_info_cache[$user_id_or_name]['user_id'],   // $user_id = null,
+			$user_info_cache[$user_id_or_name]['groups'],    // $groups = null,
+			$base_cache[$user_id_or_name]['__resolve_table'] // $resolve_table = array()
+		);
+		$object =& $objcache[$namespace][$page_id][$user_id_or_name];
+		
+		return $object;
+	}
+	
+	/**
+ 	* Checks if the given ACL rule type applies to a namespace.
+ 	* @param string ACL rule type
+ 	* @param string Namespace
+ 	* @return bool
+ 	*/
+	
+	function check_acl_scope($acl_rule, $namespace)
+	{
+		if ( !isset($this->acl_scope[$acl_rule]) )
+			return false;
+		if ( $this->acl_scope[$acl_rule] === array('All') )
+			return true;
+		return ( in_array($namespace, $this->acl_scope[$acl_rule]) ) ? true : false;
+	}
+	
+	/**
+ 	* Read all of our permissions from the database and process/apply them. This should be called after the page is determined.
+ 	* @access private
+ 	*/
+	
+	function init_permissions()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		// Initialize the permissions list with some defaults
+		$this->perms = $this->acl_types;
+		$this->acl_defaults_used = $this->perms;
+		
+		// Fetch sitewide defaults from the permissions table
+		$bs = 'SELECT rules, target_type, target_id FROM '.table_prefix.'acl' . "\n"
+ 						. '  WHERE page_id IS NULL AND namespace IS NULL AND' . "\n"
+ 						. '  ( ';
+		
+		$q = Array();
+		$q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
+		if(count($this->groups) > 0)
+		{
+			foreach($this->groups as $g_id => $g_name)
+			{
+				$q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
+			}
+		}
+		$bs .= implode(" OR \n    ", $q) . " ) \n  ORDER BY target_type ASC, target_id ASC;";
+		$q = $this->sql($bs);
+		if ( $row = $db->fetchrow() )
+		{
+			do {
+				$rules = $this->string_to_perm($row['rules']);
+				$is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
+				$this->acl_merge_with_current($rules, $is_everyone);
+			} while ( $row = $db->fetchrow() );
+		}
+		
+		// Cache the sitewide permissions for later use
+		$this->acl_base_cache = $this->perms;
+		
+		profiler_log('session: base ACL set calculated');
+		
+		// Load and calculate permissions for the current page
+		$page_acl = $this->fetch_page_acl($paths->page_id, $paths->namespace);
+		$this->perms = $page_acl->perms;
+		$this->acl_defaults_used = $page_acl->acl_defaults_used;
+	}
+	
+	/**
+ 	* Extends the scope of a permission type.
+ 	* @param string The name of the permission type
+ 	* @param string The namespace(s) that should be covered. This can be either one namespace ID or a pipe-delimited list.
+ 	* @param object Optional - the current $paths object, in case we're doing this from the acl_rule_init hook
+ 	*/
+ 	
+	function acl_extend_scope($perm_type, $namespaces, &$p_in)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$p_obj = ( is_object($p_in) ) ? $p_in : $paths;
+		$nslist = explode('|', $namespaces);
+		foreach ( $nslist as $i => $ns )
+		{
+			if ( !isset($p_obj->nslist[$ns]) )
+			{
+				unset($nslist[$i]);
+			}
+			else
+			{
+				if ( $this->acl_scope[$perm_type] !== array('All') )
+					$this->acl_scope[$perm_type][] = $ns;
+				if ( isset($this->acl_types[$perm_type]) && !isset($this->perms[$perm_type]) )
+				{
+					$this->perms[$perm_type] = $this->acl_types[$perm_type];
+				}
+			}
+		}
+	}
+	
+	/**
+ 	* Converts a permissions field into a string for database insertion. Similar in spirit to serialize().
+ 	* @param array $perms An associative array with only integers as values
+ 	* @return string
+ 	*/
+ 	
+	function perm_to_string($perms)
+	{
+		$s = '';
+		foreach($perms as $perm => $ac)
+		{
+			if ( $ac == 'i' )
+				continue;
+			$s .= "$perm=$ac;";
+		}
+		return $s;
+	}
+	
+	/**
+ 	* Converts a permissions string back to an array.
+ 	* @param string $perms The result from sessionManager::perm_to_string()
+ 	* @return array
+ 	*/
+ 	
+	function string_to_perm($perms)
+	{
+		$ret = Array();
+		preg_match_all('#([a-z0-9_-]+)=([0-9]+);#i', $perms, $matches);
+		foreach($matches[1] as $i => $t)
+		{
+			$ret[$t] = intval($matches[2][$i]);
+		}
+		return $ret;
+	}
+	
+	/**
+ 	* Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence over the first, but AUTH_DENY always prevails.
+ 	* @param array $perm1 The first set of permissions
+ 	* @param array $perm2 The second set of permissions
+ 	* @param bool $is_everyone If true, applies exceptions for "Everyone" group
+ 	* @param array|reference $defaults_used Array that will be filled with default usage data
+ 	* @return array
+ 	*/
+ 	
+	function acl_merge($perm1, $perm2, $is_everyone = false, &$defaults_used = array())
+	{
+		$ret = $perm1;
+		if ( !is_array(@$defaults_used) )
+		{
+			$defaults_used = array();
+		}
+		foreach ( $perm1 as $i => $p )
+		{
+			if ( isset($perm2[$i]) )
+			{
+				if ( $is_everyone && isset($defaults_used[$i]) && $defaults_used[$i] === false )
+					continue;
+				// Decide precedence
+				if ( isset($defaults_used[$i]) )
+				{
+					// echo "$i: default in use, overriding to: {$perm2[$i]}<br />";
+					// Defaults are in use, override
+					
+					// CHANGED - 1.1.4:
+					// For some time this has been intentionally relaxed so that the following
+					// exception is available to Deny permissions:
+					//   If the rule applies to the group "Everyone" on the entire site,
+					//   Deny settings could be overriden.
+					// This is documented at: http://docs.enanocms.org/Help:4.2
+					if ( $perm1[$i] != AUTH_DENY )
+					{
+						$perm1[$i] = $perm2[$i];
+						$defaults_used[$i] = ( $is_everyone );
+					}
+				}
+				else
+				{
+					// echo "$i: default NOT in use";
+					// Defaults are not in use, merge as normal
+					if ( $perm1[$i] != AUTH_DENY )
+					{
+						// echo ", but overriding";
+						$perm1[$i] = $perm2[$i];
+					}
+					// echo "<br />";
+				}
+			}
+		}
+		return $perm1;
+	}
+	
+	/**
+ 	* Merges two ACL arrays, but instead of calculating inheritance for missing permission types, just returns 'i' for that type. Useful
+ 	* for explicitly requiring inheritance in ACL editing interfaces
+ 	* @param array $perm1 The first set of permissions
+ 	* @param array $perm2 The second, authoritative set of permissions
+ 	*/
+	
+	function acl_merge_inherit($perm1, $perm2)
+	{
+		foreach ( $perm1 as $type => $level )
+		{
+			$perm1[$type][$level] = 'i';
+		}
+		$ret = $perm1;
+		foreach ( $perm2 as $type => $level )
+		{
+			if ( isset( $ret[$type] ) )
+			{
+				if ( $ret[$type] != AUTH_DENY )
+					$ret[$type] = $level;
+			}
+		}
+		return $ret;
+	}
+	
+	/**
+ 	* Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
+ 	* @param array The array to merge into the master ACL list
+ 	* @param bool If true, $perm is treated as the "new default"
+ 	* @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
+ 	*/
+	
+	function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
+	{
+		$this->perms = $this->acl_merge($this->perms, $perm, $is_everyone, $this->acl_defaults_used);
+	}
+	
+	/**
+ 	* Merges two ACL arrays. Both parameters should be permission list arrays. The second group takes precedence
+ 	* over the first, without exceptions. This is used to merge the hardcoded defaults with admin-specified
+ 	* defaults, which take precedence.
+ 	* @param array $perm1 The first set of permissions
+ 	* @param array $perm2 The second set of permissions
+ 	* @return array
+ 	*/
+ 	
+	function acl_merge_complete($perm1, $perm2)
+	{
+		$ret = $perm1;
+		foreach ( $perm2 as $type => $level )
+		{
+			$ret[$type] = $level;
+		}
+		return $ret;
+	}
+	
+	/**
+ 	* Tell us if the dependencies for a given permission are met.
+ 	* @param string The ACL permission ID
+ 	* @return bool
+ 	*/
+ 	
+	function acl_check_deps($type, $debug = false)
+	{
+		global $paths;
+		
+		// This will only happen if the permissions table is hacked or improperly accessed
+		if(!isset($this->acl_deps[$type]))
+			return true;
+		// Permission has no dependencies?
+		if(sizeof($this->acl_deps[$type]) < 1)
+			return true;
+		// go through them all and build a flat list of dependencies
+		$deps = $this->acl_deps[$type];
+		while(true)
+		{
+			$j = sizeof($deps);
+			for ( $i = 0; $i < $j; $i++ )
+			{
+				$b = $deps;
+				if ( !$this->check_acl_scope($deps[$i], $paths->namespace) )
+				{
+					// Action $type depends on action $deps[$i] which cannot be satisfied because $deps[$i] is out of scope.
+					// echo '<pre>' . enano_debug_print_backtrace(true) . '</pre>';
+					trigger_error("acl_check_deps: $type depends on {$deps[$i]} which is not within scope of $paths->namespace; this indicats a bug in ACL rule specification", E_USER_WARNING);
+					return false;
+				}
+				$deps = array_merge($deps, $this->acl_deps[$deps[$i]]);
+				if( $b == $deps )
+				{
+					break 2;
+				}
+				$j = sizeof($deps);
+			}
+		}
+		$debugdata = array();
+		foreach($deps as $d)
+		{
+			// Our dependencies are fully resolved, so tell get_permissions() to not recursively call this function
+			if ( !$this->get_permissions($d, true) )
+			{
+				if ( $debug )
+				{
+					$debugdata[] = $d;
+				}
+				else
+				{
+					return false;
+				}
+			}
+		}
+		return $debug ? $debugdata : true;
+	}
+	
+	/**
+ 	* Makes a CAPTCHA code and caches the code in the database
+ 	* @param int $len The length of the code, in bytes
+ 	* @param string Optional, the hash to reuse
+ 	* @return string A unique identifier assigned to the code. This hash should be passed to sessionManager::getCaptcha() to retrieve the code.
+ 	*/
+	
+	function make_captcha($len = 7, $hash = '')
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$code = $this->generate_captcha_code($len);
+		if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $hash) )
+			$hash = md5(microtime() . mt_rand());
+		$session_data = $db->escape(serialize(array()));
+		
+		// sanity check
+		if ( !is_valid_ip(@$_SERVER['REMOTE_ADDR']) || !is_int($this->user_id) )
+			return false;
+		
+		$this->sql('DELETE FROM ' . table_prefix . "captcha WHERE session_id = '$hash';");
+		$this->sql('INSERT INTO ' . table_prefix . 'captcha(session_id, code, session_data, source_ip, user_id)' . " VALUES('$hash', '$code', '$session_data', '{$_SERVER['REMOTE_ADDR']}', {$this->user_id});");
+		return $hash;
+	}
+	
+	/**
+ 	* Generates a "pronouncable" or "human-friendly" word using various phonics rules
+ 	* @param int Optional. The length of the word.
+ 	* @return string
+ 	*/
+	
+	function generate_captcha_code($len = 7)
+	{
+		// don't use k and x, they get mixed up a lot
+		$consonants = 'bcdfghmnpqrsvwyz';
+		$vowels = 'aeiou';
+		$prev = 'vowel';
+		$prev_l = '';
+		$word = '';
+		$allow_next_vowel = true;
+		for ( $i = 0; $i < $len; $i++ )
+		{
+			if ( $prev == 'vowel' )
+			{
+				$allow_next_vowel = false;
+				if ( $prev_l == 'o' && mt_rand(0, 3) == 3 && $allow_next_vowel )
+					$word .= 'i';
+				else if ( $prev_l == 'q' && mt_rand(0, 3) != 1 && $allow_next_vowel )
+					$word .= 'u';
+				else if ( $prev_l == 'o' && mt_rand(0, 3) == 2 && $allow_next_vowel )
+					$word .= 'u';
+				else if ( $prev_l == 'a' && mt_rand(0, 3) == 3 && $allow_next_vowel )
+					$word .= 'i';
+				else if ( $prev_l == 'a' && mt_rand(0, 10) == 7 && $allow_next_vowel )
+					$word .= 'o';
+				else if ( $prev_l == 'a' && mt_rand(0, 7) == 2 && $allow_next_vowel )
+					$word .= 'u';
+				else
+				{
+					$allow_next_vowel = true;
+					$word .= $consonants{mt_rand(0, (strlen($consonants)-1))};
+				}
+			}
+			else if ( $prev == 'consonant' )
+			{
+				if ( $prev_l == 'p' && mt_rand(0, 7) == 4 )
+					$word .= 't';
+				else if ( $prev_l == 'p' && mt_rand(0, 5) == 1 )
+					$word .= 'h';
+				else
+					$word .= $vowels{mt_rand(0, (strlen($vowels)-1))};
+			}
+			$prev_l = substr($word, -1);
+			$l = ( mt_rand(0, 1) == 1 ) ? strtoupper($prev_l) : strtolower($prev_l);
+			$word = substr($word, 0, -1) . $l;
+			if ( strstr('aeiou', $prev_l) )
+				$prev = 'vowel';
+			else
+				$prev = 'consonant';
+		}
+		return $word;
+	}
+	
+	/**
+ 	* For the given code ID, returns the correct CAPTCHA code, or false on failure
+ 	* @param string $hash The unique ID assigned to the code
+ 	* @param bool If true, the code is NOT deleted from the database. Use with caution!
+ 	* @return string The correct confirmation code
+ 	*/
+	
+	function get_captcha($hash, $nodelete = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !preg_match('/^[a-f0-9]{32}([a-z0-9]{8})?$/', $hash) )
+		{
+			die("session manager: bad captcha_hash $hash");
+			return false;
+		}
+		
+		// sanity check
+		if ( !is_valid_ip(@$_SERVER['REMOTE_ADDR']) )
+		{
+			die("session manager insanity: bad REMOTE_ADDR or invalid UID");
+			return false;
+		}
+		
+		$q = $this->sql('SELECT code_id, code FROM ' . table_prefix . "captcha WHERE session_id = '$hash' AND source_ip = '{$_SERVER['REMOTE_ADDR']}';");
+		if ( $db->numrows() < 1 )
+		{
+			return false;
+		}
+		
+		list($code_id, $code) = $db->fetchrow_num();
+		
+		$db->free_result();
+		
+		// delete it
+		if ( !$nodelete )
+			$this->sql('DELETE FROM ' . table_prefix . "captcha WHERE code_id = $code_id;");
+		
+		return $code;
+	}
+	
+	/**
+ 	* (AS OF 1.0.2: Deprecated. Captcha codes are now killed on first fetch for security.) Deletes all CAPTCHA codes cached in the DB for this user.
+ 	*/
+	
+	function kill_captcha()
+	{
+		return true;
+	}
+	
+	/**
+ 	* Generates a random password.
+ 	* @param int $length Optional - length of password
+ 	* @return string
+ 	*/
+ 	
+	function random_pass($length = 10)
+	{
+		$valid_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_+@#%&<>';
+		$valid_chars = enano_str_split($valid_chars);
+		$ret = '';
+		for ( $i = 0; $i < $length; $i++ )
+		{
+			$ret .= $valid_chars[mt_rand(0, count($valid_chars)-1)];
+		}
+		return $ret;
+	}
+	
+	/**
+ 	* Generates some Javascript that calls the AES encryption library. Put this after your </form>.
+ 	* @param string The name of the form
+ 	* @param string The name of the password field
+ 	* @param string The name of the field that switches encryption on or off
+ 	* @param string The name of the field that contains the encryption key
+ 	* @param string The name of the field that will contain the encrypted password
+ 	* @param string The name of the field that handles MD5 challenge data
+ 	* @param string The name of the field that tells if the server supports DiffieHellman
+ 	* @param string The name of the field with the DiffieHellman public key
+ 	* @param string The name of the field that the client should populate with its public key
+ 	* @return string
+ 	*/
+ 	
+	function aes_javascript($form_name, $pw_field, $use_crypt = 'use_crypt', $crypt_key = 'crypt_key', $crypt_data = 'crypt_data', $challenge = 'challenge_data', $dh_supported = 'dh_supported', $dh_pubkey = 'dh_public_key', $dh_client_pubkey = 'dh_client_public_key')
+	{
+		$code = '
+			<script type="text/javascript">
+					
+					function runEncryption(nowhiteout)
+					{
+						var frm = document.forms.'.$form_name.';
+						if ( !nowhiteout )
+							whiteOutForm(frm);
+						
+						load_component(\'crypto\');
+						var testpassed = ' . ( ( isset($_GET['use_crypt']) && $_GET['use_crypt']=='0') ? 'false; // CRYPTO-AUTH DISABLED ON USER REQUEST // ' : '' ) . '( aes_self_test() && md5_vm_test() );
+						var use_diffiehellman = false;' . "\n";
+		if ( $dh_supported && $dh_pubkey )
+		{
+			$code .= <<<EOF
+						if ( frm.$dh_supported.value == 'true' && !is_iPhone )
+							use_diffiehellman = true;
 EOF;
-    }
-    $code .= '
-    
-            if ( frm[\'' . $dh_supported . '\'] )
-            {
-              frm[\'' . $dh_supported . '\'].value = ( use_diffiehellman ) ? "true" : "false";
-            }
-            
-            if ( frm["' . $pw_field . '_confirm"] )
-            {
-              pass1 = frm.' . $pw_field . '.value;
-              pass2 = frm.' . $pw_field . '_confirm.value;
-              if ( pass1 != pass2 )
-              {
-                load_component("l10n");
-                alert($lang.get("userfuncs_passreset_err_no_match"));
-                return false;
-              }
-              if ( pass1.length < 6 )
-              {
-                load_component("l10n");
-                alert($lang.get("userfuncs_passreset_err_too_short"));
-                return false;
-              }
-              frm.' . $pw_field . '_confirm.value = "";
-            }
-            
-            if ( testpassed && use_diffiehellman )
-            {
-              // try to blank out the table to prevent double submits and what have you
-              var el = frm.' . $pw_field . ';
-              while ( el.tagName != "BODY" && el.tagName != "TABLE" )
-              {
-                el = el.parentNode;
-              }
-              /*
-              if ( el.tagName == "TABLE" )
-              {
-                whiteOutElement(el);
-              }
-              */
-              
-              frm.'.$use_crypt.'.value = \'yes_dh\';
-              
-              // Perform Diffie Hellman stuff
-              // console.info("DiffieHellman: started keygen process");
-              var dh_priv = dh_gen_private();
-              var dh_pub = dh_gen_public(dh_priv);
-              var secret = dh_gen_shared_secret(dh_priv, frm.' . $dh_pubkey . '.value);
-              // console.info("DiffieHellman: finished keygen process");
-              
-              // secret_hash is used to verify that the server guesses the correct secret
-              var secret_hash = hex_sha1(secret);
-              
-              // give the server our values
-              frm.' . $crypt_key . '.value = secret_hash;
-              ' . ( $dh_supported ? 'frm.' . $dh_client_pubkey . '.value = dh_pub;' : '' ) . '
-              
-              // console.info("DiffieHellman: set public values");
-              
-              // crypt_key is the actual AES key
-              var crypt_key = (hex_sha256(secret)).substr(0, (keySizeInBits / 4));
-              
-              // Perform encryption
-              crypt_key = hexToByteArray(crypt_key);
-              var pass = frm.'.$pw_field.'.value;
-              pass = stringToByteArray(pass);
-              var cryptstring = rijndaelEncrypt(pass, crypt_key, \'ECB\');
-              if(!cryptstring)
-              {
-                return false;
-              }
-              cryptstring = byteArrayToHex(cryptstring);
-              // console.info("DiffieHellman: finished AES");
-              frm.'.$crypt_data.'.value = cryptstring;
-              frm.'.$pw_field.'.value = \'\';
-              // console.info("DiffieHellman: ready to submit");
-            }
-            else if ( testpassed && !use_diffiehellman )
-            {
-              frm.'.$use_crypt.'.value = \'yes\';
-              var cryptkey = frm.'.$crypt_key.'.value;
-              frm.'.$crypt_key.'.value = hex_md5(cryptkey);
-              cryptkey = hexToByteArray(cryptkey);
-              if(!cryptkey || ( ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ) && cryptkey.length != keySizeInBits / 8 )
-              {
-                if ( frm._login ) frm._login.disabled = true;
-                len = ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ? \'\\nLen: \'+cryptkey.length : \'\';
-                alert(\'The key is messed up\\nType: \'+typeof(cryptkey)+len);
-              }
-              pass = frm.'.$pw_field.'.value;
-              chal = frm.'.$challenge.'.value;
-              challenge = hex_md5(pass + chal) + chal;
-              frm.'.$challenge.'.value = challenge;
-              pass = stringToByteArray(pass);
-              cryptstring = rijndaelEncrypt(pass, cryptkey, \'ECB\');
-              if(!cryptstring)
-              {
-                return false;
-              }
-              cryptstring = byteArrayToHex(cryptstring);
-              frm.'.$crypt_data.'.value = cryptstring;
-              frm.'.$pw_field.'.value = \'\';
-            }
-          }
-        </script>
-        ';
-    return $code;
-  }
-  
-  /**
-   * Generates the HTML form elements required for an encrypted logon experience.
-   * @return string
-   */
-  
-  function generate_aes_form()
-  {
-    $return = '<input type="hidden" name="use_crypt" value="no" />';
-    $return .= '<input type="hidden" name="crypt_key" value="' . $this->rijndael_genkey() . '" />';
-    $return .= '<input type="hidden" name="crypt_data" value="" />';
-    $return .= '<input type="hidden" name="challenge_data" value="' . $this->dss_rand() . '" />';
-    
-    require_once(ENANO_ROOT . '/includes/math.php');
-    require_once(ENANO_ROOT . '/includes/diffiehellman.php');
-    
-    global $dh_supported, $_math;
-    if ( $dh_supported )
-    {
-      $dh_key_priv = dh_gen_private();
-      $dh_key_pub = dh_gen_public($dh_key_priv);
-      $dh_key_priv = $_math->str($dh_key_priv);
-      $dh_key_pub = $_math->str($dh_key_pub);
-      // store the keys in the DB
-      $this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
-      
-      $return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"true\" />
-            <input type=\"hidden\" name=\"dh_public_key\" value=\"$dh_key_pub\" />
-            <input type=\"hidden\" name=\"dh_client_public_key\" value=\"\" />";
-    }
-    else
-    {
-      $return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"false\" />";
-    }
-    return $return;
-  }
-  
-  /**
-   * If you used all the same form fields as the normal login interface, this will take care of DiffieHellman for you and return the key.
-   * @param string Password field name (defaults to "password")
-   * @return string
-   */
-  
-  function get_aes_post($fieldname = 'password')
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-    if ( $_POST['use_crypt'] == 'yes' )
-    {
-      $crypt_key = $this->fetch_public_key($_POST['crypt_key']);
-      if ( !$crypt_key )
-      {
-        throw new Exception($lang->get('user_err_key_not_found'));
-      }
-      $crypt_key = hexdecode($crypt_key);
-      $data = $aes->decrypt($_POST['crypt_data'], $crypt_key, ENC_HEX);
-    }
-    else if ( $_POST['use_crypt'] == 'yes_dh' )
-    {
-      require_once(ENANO_ROOT . '/includes/math.php');
-      require_once(ENANO_ROOT . '/includes/diffiehellman.php');
-      
-      global $dh_supported, $_math;
-        
-      if ( !$dh_supported )
-      {
-        throw new Exception('Server does not support DiffieHellman, denying request');
-      }
-      
-      // Fetch private key
-      $dh_public = $_POST['dh_public_key'];
-      if ( !ctype_digit($dh_public) )
-      {
-        throw new Exception('ERR_DH_KEY_NOT_INTEGER');
-      }
-      $q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
-      if ( !$q )
-        $db->die_json();
-      
-      if ( $db->numrows() < 1 )
-      {
-        throw new Exception('ERR_DH_KEY_NOT_FOUND');
-      }
-      
-      list($dh_private, $dh_key_id) = $db->fetchrow_num();
-      $db->free_result();
-      
-      // We have the private key, now delete the key pair, we no longer need it
-      $q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
-      if ( !$q )
-        $db->die_json();
-      
-      // Generate the shared secret
-      $dh_secret = dh_gen_shared_secret($dh_private, $_POST['dh_client_public_key']);
-      $dh_secret = $_math->str($dh_secret);
-      
-      // Did we get all our math right?
-      $dh_secret_check = sha1($dh_secret);
-      $dh_hash = $_POST['crypt_key'];
-      if ( $dh_secret_check !== $dh_hash )
-      {
-        throw new Exception('ERR_DH_HASH_NO_MATCH');
-      }
-      
-      // All good! Generate the AES key
-      $aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
-      
-      // decrypt user info
-      $aes_key = hexdecode($aes_key);
-      $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-      $data = $aes->decrypt($_POST['crypt_data'], $aes_key, ENC_HEX);
-    }
-    else
-    {
-      $data = $_POST[$fieldname];
-    }
-    return $data;
-  }
-  
-  /**
-   * Backend code for the JSON login interface. Basically a frontend to the session API that takes all parameters in one huge array.
-   * @param array LoginAPI request
-   * @return array LoginAPI response
-   */
-  
-  function process_login_request($req, $_dbgtmp = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Setup EnanoMath and Diffie-Hellman
-    global $dh_supported;
-    if ( !function_exists('dh_gen_private') )
-    {
-      require_once(ENANO_ROOT.'/includes/math.php');
-      
-      $dh_supported = true;
-      try
-      {
-        require_once(ENANO_ROOT . '/includes/diffiehellman.php');
-      }
-      catch ( Exception $e )
-      {
-        $dh_supported = false;
-      }
-    }
-    global $_math;
-    
-    // Check for the mode
-    if ( !isset($req['mode']) )
-    {
-      return $this->get_login_response('api_error', 'ERR_JSON_NO_MODE');
-    }
-    
-    // Main processing switch
-    switch ( $req['mode'] )
-    {
-      default:
-        return $this->get_login_response('api_error', 'ERR_JSON_INVALID_MODE');
-        break;
-      case 'getkey':
-        
-        $this->start();
-        
-        return $this->get_login_response('initial');
-        break;
-      case 'login_dh':
-        // User is requesting a login and has sent Diffie-Hellman data.
-        
-        //
-        // KEY RECONSTRUCTION
-        //
-        
-        $userinfo_crypt = $req['userinfo'];
-        $dh_public = $req['dh_public_key'];
-        $dh_hash = $req['dh_secret_hash'];
-        
-        // Check the key
-        if ( !ctype_digit($dh_public) || !ctype_digit($req['dh_client_key']) )
-        {
-          return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_NUMERIC');
-        }
-        
-        // Fetch private key
-        $q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
-        if ( !$q )
-          $db->die_json();
-        
-        if ( $db->numrows() < 1 )
-        {
-          return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_FOUND');
-        }
-        
-        list($dh_private, $dh_key_id) = $db->fetchrow_num();
-        $db->free_result();
-        
-        // We have the private key, now delete the key pair, we no longer need it
-        $q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
-        if ( !$q )
-          $db->die_json();
-        
-        // Generate the shared secret
-        $dh_secret = dh_gen_shared_secret($dh_private, $req['dh_client_key']);
-        $dh_secret = $_math->str($dh_secret);
-        
-        // Did we get all our math right?
-        $dh_secret_check = sha1($dh_secret);
-        if ( $dh_secret_check !== $dh_hash )
-        {
-          return $this->get_login_response('api_error', 'ERR_DH_HASH_NO_MATCH');
-        }
-        
-        // All good! Generate the AES key
-        $aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
-      case 'login_aes':
-        if ( $req['mode'] == 'login_aes' )
-        {
-          // login_aes-specific code
-          $aes_key = $this->fetch_public_key($req['key_aes']);
-          if ( !$aes_key )
-          {
-            return $this->get_login_response('api_error', 'ERR_AES_LOOKUP_FAILED');
-          }
-          $userinfo_crypt = $req['userinfo'];
-        }
-        // shared between the two systems from here on out
-        
-        // decrypt user info
-        $aes_key = hexdecode($aes_key);
-        $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-        // using "true" here disables caching of the decrypted login info (which includes the password)
-        $userinfo_json = $aes->decrypt($userinfo_crypt, $aes_key, ENC_HEX, true);
-        if ( !$userinfo_json )
-        {
-          return $this->get_login_response('api_error', 'ERR_AES_DECRYPT_FAILED');
-        }
-        // de-JSON user info
-        try
-        {
-          $userinfo = enano_json_decode($userinfo_json);
-        }
-        catch ( Exception $e )
-        {
-          return $this->get_login_response('api_error', 'ERR_USERINFO_DECODE_FAILED');
-        }
-        
-      case 'login_pt':
-        // plaintext login
-        if ( $req['mode'] == 'login_pt' )
-        {
-          $userinfo = isset($req['userinfo']) ? $req['userinfo'] : array();
-        }
-        
-        if ( !isset($userinfo['username']) || !isset($userinfo['password']) )
-        {
-          return $this->get_login_response('api_error', 'ERR_USERINFO_MISSING_VALUES');
-        }
-        
-        $username =& $userinfo['username'];
-        $password =& $userinfo['password'];
-        
-        // locked out? check captcha
-        $lockout_data = $this->get_lockout_info();
-        if ( $lockout_data['policy'] == 'captcha' && $lockout_data['active'] )
-        {
-          // policy is captcha -- check if it's correct, and if so, bypass lockout check
-          $real_code = $this->get_captcha($req['captcha_hash']);
-          if ( strtolower($real_code) !== strtolower($req['captcha_code']) )
-          {
-            // captcha is bad
-            return $this->get_login_response('login_failure', 'lockout_bad_captcha');
-          }
-        }
-        else if ( $lockout_data['policy'] == 'lockout' && $lockout_data['active'] )
-        {
-          // we're fully locked out
-          return $this->get_login_response('login_failure', 'lockout_request_denied');
-        }
-        
-        // At this point if any extra info was injected into the login data packet, we need to let plugins process it
-        /**
-         * Called upon processing an incoming login request. If you added anything to the userinfo object during the jshook
-         * login_build_userinfo, that will be in the $userinfo array here. Expected return values are: true if your plugin has
-         * not only succeeded but ALSO issued a session key (bypass the whole Enano builtin login process) and an associative array
-         * with "mode" set to "error" and an error string in "error" to send an error back to the client. Any return value other
-         * than these will be treated as a pass-through, and the user's password will be validated through Enano's standard process.
-         * @hook login_process_userdata_json
-         */
-        
-        $code = $plugins->setHook('login_process_userdata_json', true);
-        
-        foreach ( $code as $cmd )
-        {
-          $result = eval($cmd);
-          if ( $result === true )
-          {
-            return $this->get_login_response('login_success', false, array(
-                'key' => $this->sid_super,
-                'user_id' => $this->user_id,
-                'user_level' => $this->user_level,
-                'reset' => false
-              ));
-          }
-          else if ( is_array($result) )
-          {
-            if ( isset($result['mode']) && $result['mode'] === 'error' && isset($result['error']) )
-            {
-              // Pass back any additional information from the error response
-              $append = $result;
-              unset($append['mode'], $append['error']);
-              $append['from_plugin'] = true;
-              
-              return $this->get_login_response('login_failure', $result['error'], $append);
-            }
-          }
-        }
-        
-        // attempt the login
-        $login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['remember']);
-        
-        if ( $login_result['success'] )
-        {
-          return $this->get_login_response('login_success', false, array(
-              'key' => $this->sid_super,
-              'user_id' => $this->user_id,
-              'user_level' => $this->user_level,
-            ));
-        }
-        else if ( !$login_result['success'] && $login_result['error'] === 'valid_reset' )
-        {
-          return $this->get_login_response('reset_pass_used', false, array(
-              'redirect_url' => $login_result['redirect_url']
-            ));
-        }
-        else
-        {
-          return $this->get_login_response('login_failure', 'invalid_credentials');
-        }
-        
-        break;
-      case 'clean_key':
-        // Clean out a key, since it won't be used.
-        // This is called when the user clicks Cancel in the AJAX login interface.
-        if ( !empty($req['key_aes']) )
-        {
-          $this->fetch_public_key($req['key_aes']);
-        }
-        if ( !empty($req['key_dh']) )
-        {
-          $pk = $db->escape($req['key_dh']);
-          $q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE public_key = '$pk';");
-          if ( !$q )
-            $db->die_json();
-        }
-        return array(
-            'mode' => 'noop'
-          );
-        break;
-      case 'respond_password_reset':
-        die(enano_json_encode(array(
-            'mode' => 'login_success_reset',
-            'user_id' => $req['user_id'],
-            'temp_password' => $req['temp_password'],
-            'respawn_info' => $this->process_login_request(array('mode' => 'getkey'))
-          )));
-        break;
-      case 'logout':
-        if ( !$this->started )
-          $this->start();
-        if ( !isset($req['csrf_token']) )
-          return array(
-              'mode' => 'error',
-              'error' => 'Invalid CSRF token'
-            );
-        
-        if ( $req['csrf_token'] !== $this->csrf_token )
-          return array(
-              'mode' => 'error',
-              'error' => 'Invalid CSRF token'
-            );
-        $level = isset($req['level']) && is_int($req['level']) ? $req['level'] : USER_LEVEL_MEMBER;
-        if ( ($result = $this->logout($level)) === 'success' )
-        {
-          return array(
-            'mode' => 'logout_success'
-          );
-        }
-        else
-        {
-          return array(
-            'mode' => 'error',
-            'error' => $result
-          );
-        }
-        break;
-    }
-    
-  }
-  
-  /**
-   * Generate a packet to send to the client for logins.
-   * @param string mode
-   * @param array 
-   * @return array
-   */
-  
-  function get_login_response($mode, $error = false, $base = array())
-  {
-    $this->start();
-    
-    // init
-    $response = $base;
-    // modules in the packet
-    $response['mode'] = $mode;
-    $response['error'] = $error;
-    $response['crypto'] = $mode !== 'login_success' ? $this->get_login_crypto_packet() : false;
-    $response['lockout'] = $mode !== 'login_success' ? $this->get_lockout_info() : false;
-    $response['extended_time'] = intval(getConfig('session_remember_time', '30'));
-    $response['username'] = $this->user_logged_in ? $this->username : false;
-    return $response;
-  }
-  
-  /**
-   * Get a packet of crypto flags for login.
-   * @return array
-   */
-  
-  function get_login_crypto_packet()
-  {
-    global $dh_supported, $_math;
-    
-    $response = array();
-    
-    $response['dh_enable'] = $dh_supported;
-    $response['aes_key'] = $this->rijndael_genkey();
-    
-    // Can we do Diffie-Hellman? If so, generate and stash a public/private key pair.
-    if ( $dh_supported )
-    {
-      $dh_key_priv = dh_gen_private();
-      $dh_key_pub = dh_gen_public($dh_key_priv);
-      $dh_key_priv = $_math->str($dh_key_priv);
-      $dh_key_pub = $_math->str($dh_key_pub);
-      $response['dh_public_key'] = $dh_key_pub;
-      // store the keys in the DB
-      $this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
-    }
-    
-    return $response;
-  }
-  
+		}
+		$code .= '
+		
+						if ( frm[\'' . $dh_supported . '\'] )
+						{
+							frm[\'' . $dh_supported . '\'].value = ( use_diffiehellman ) ? "true" : "false";
+						}
+						
+						if ( frm["' . $pw_field . '_confirm"] )
+						{
+							pass1 = frm.' . $pw_field . '.value;
+							pass2 = frm.' . $pw_field . '_confirm.value;
+							if ( pass1 != pass2 )
+							{
+								load_component("l10n");
+								alert($lang.get("userfuncs_passreset_err_no_match"));
+								return false;
+							}
+							if ( pass1.length < 6 )
+							{
+								load_component("l10n");
+								alert($lang.get("userfuncs_passreset_err_too_short"));
+								return false;
+							}
+							frm.' . $pw_field . '_confirm.value = "";
+						}
+						
+						if ( testpassed && use_diffiehellman )
+						{
+							// try to blank out the table to prevent double submits and what have you
+							var el = frm.' . $pw_field . ';
+							while ( el.tagName != "BODY" && el.tagName != "TABLE" )
+							{
+								el = el.parentNode;
+							}
+							/*
+							if ( el.tagName == "TABLE" )
+							{
+								whiteOutElement(el);
+							}
+							*/
+							
+							frm.'.$use_crypt.'.value = \'yes_dh\';
+							
+							// Perform Diffie Hellman stuff
+							// console.info("DiffieHellman: started keygen process");
+							var dh_priv = dh_gen_private();
+							var dh_pub = dh_gen_public(dh_priv);
+							var secret = dh_gen_shared_secret(dh_priv, frm.' . $dh_pubkey . '.value);
+							// console.info("DiffieHellman: finished keygen process");
+							
+							// secret_hash is used to verify that the server guesses the correct secret
+							var secret_hash = hex_sha1(secret);
+							
+							// give the server our values
+							frm.' . $crypt_key . '.value = secret_hash;
+							' . ( $dh_supported ? 'frm.' . $dh_client_pubkey . '.value = dh_pub;' : '' ) . '
+							
+							// console.info("DiffieHellman: set public values");
+							
+							// crypt_key is the actual AES key
+							var crypt_key = (hex_sha256(secret)).substr(0, (keySizeInBits / 4));
+							
+							// Perform encryption
+							crypt_key = hexToByteArray(crypt_key);
+							var pass = frm.'.$pw_field.'.value;
+							pass = stringToByteArray(pass);
+							var cryptstring = rijndaelEncrypt(pass, crypt_key, \'ECB\');
+							if(!cryptstring)
+							{
+								return false;
+							}
+							cryptstring = byteArrayToHex(cryptstring);
+							// console.info("DiffieHellman: finished AES");
+							frm.'.$crypt_data.'.value = cryptstring;
+							frm.'.$pw_field.'.value = \'\';
+							// console.info("DiffieHellman: ready to submit");
+						}
+						else if ( testpassed && !use_diffiehellman )
+						{
+							frm.'.$use_crypt.'.value = \'yes\';
+							var cryptkey = frm.'.$crypt_key.'.value;
+							frm.'.$crypt_key.'.value = hex_md5(cryptkey);
+							cryptkey = hexToByteArray(cryptkey);
+							if(!cryptkey || ( ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ) && cryptkey.length != keySizeInBits / 8 )
+							{
+								if ( frm._login ) frm._login.disabled = true;
+								len = ( typeof cryptkey == \'string\' || typeof cryptkey == \'object\' ) ? \'\\nLen: \'+cryptkey.length : \'\';
+								alert(\'The key is messed up\\nType: \'+typeof(cryptkey)+len);
+							}
+							pass = frm.'.$pw_field.'.value;
+							chal = frm.'.$challenge.'.value;
+							challenge = hex_md5(pass + chal) + chal;
+							frm.'.$challenge.'.value = challenge;
+							pass = stringToByteArray(pass);
+							cryptstring = rijndaelEncrypt(pass, cryptkey, \'ECB\');
+							if(!cryptstring)
+							{
+								return false;
+							}
+							cryptstring = byteArrayToHex(cryptstring);
+							frm.'.$crypt_data.'.value = cryptstring;
+							frm.'.$pw_field.'.value = \'\';
+						}
+					}
+				</script>
+				';
+		return $code;
+	}
+	
+	/**
+ 	* Generates the HTML form elements required for an encrypted logon experience.
+ 	* @return string
+ 	*/
+	
+	function generate_aes_form()
+	{
+		$return = '<input type="hidden" name="use_crypt" value="no" />';
+		$return .= '<input type="hidden" name="crypt_key" value="' . $this->rijndael_genkey() . '" />';
+		$return .= '<input type="hidden" name="crypt_data" value="" />';
+		$return .= '<input type="hidden" name="challenge_data" value="' . $this->dss_rand() . '" />';
+		
+		require_once(ENANO_ROOT . '/includes/math.php');
+		require_once(ENANO_ROOT . '/includes/diffiehellman.php');
+		
+		global $dh_supported, $_math;
+		if ( $dh_supported )
+		{
+			$dh_key_priv = dh_gen_private();
+			$dh_key_pub = dh_gen_public($dh_key_priv);
+			$dh_key_priv = $_math->str($dh_key_priv);
+			$dh_key_pub = $_math->str($dh_key_pub);
+			// store the keys in the DB
+			$this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
+			
+			$return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"true\" />
+						<input type=\"hidden\" name=\"dh_public_key\" value=\"$dh_key_pub\" />
+						<input type=\"hidden\" name=\"dh_client_public_key\" value=\"\" />";
+		}
+		else
+		{
+			$return .=  "<input type=\"hidden\" name=\"dh_supported\" value=\"false\" />";
+		}
+		return $return;
+	}
+	
+	/**
+ 	* If you used all the same form fields as the normal login interface, this will take care of DiffieHellman for you and return the key.
+ 	* @param string Password field name (defaults to "password")
+ 	* @return string
+ 	*/
+	
+	function get_aes_post($fieldname = 'password')
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+		if ( $_POST['use_crypt'] == 'yes' )
+		{
+			$crypt_key = $this->fetch_public_key($_POST['crypt_key']);
+			if ( !$crypt_key )
+			{
+				throw new Exception($lang->get('user_err_key_not_found'));
+			}
+			$crypt_key = hexdecode($crypt_key);
+			$data = $aes->decrypt($_POST['crypt_data'], $crypt_key, ENC_HEX);
+		}
+		else if ( $_POST['use_crypt'] == 'yes_dh' )
+		{
+			require_once(ENANO_ROOT . '/includes/math.php');
+			require_once(ENANO_ROOT . '/includes/diffiehellman.php');
+			
+			global $dh_supported, $_math;
+				
+			if ( !$dh_supported )
+			{
+				throw new Exception('Server does not support DiffieHellman, denying request');
+			}
+			
+			// Fetch private key
+			$dh_public = $_POST['dh_public_key'];
+			if ( !ctype_digit($dh_public) )
+			{
+				throw new Exception('ERR_DH_KEY_NOT_INTEGER');
+			}
+			$q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
+			if ( !$q )
+				$db->die_json();
+			
+			if ( $db->numrows() < 1 )
+			{
+				throw new Exception('ERR_DH_KEY_NOT_FOUND');
+			}
+			
+			list($dh_private, $dh_key_id) = $db->fetchrow_num();
+			$db->free_result();
+			
+			// We have the private key, now delete the key pair, we no longer need it
+			$q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
+			if ( !$q )
+				$db->die_json();
+			
+			// Generate the shared secret
+			$dh_secret = dh_gen_shared_secret($dh_private, $_POST['dh_client_public_key']);
+			$dh_secret = $_math->str($dh_secret);
+			
+			// Did we get all our math right?
+			$dh_secret_check = sha1($dh_secret);
+			$dh_hash = $_POST['crypt_key'];
+			if ( $dh_secret_check !== $dh_hash )
+			{
+				throw new Exception('ERR_DH_HASH_NO_MATCH');
+			}
+			
+			// All good! Generate the AES key
+			$aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
+			
+			// decrypt user info
+			$aes_key = hexdecode($aes_key);
+			$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+			$data = $aes->decrypt($_POST['crypt_data'], $aes_key, ENC_HEX);
+		}
+		else
+		{
+			$data = $_POST[$fieldname];
+		}
+		return $data;
+	}
+	
+	/**
+ 	* Backend code for the JSON login interface. Basically a frontend to the session API that takes all parameters in one huge array.
+ 	* @param array LoginAPI request
+ 	* @return array LoginAPI response
+ 	*/
+	
+	function process_login_request($req, $_dbgtmp = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Setup EnanoMath and Diffie-Hellman
+		global $dh_supported;
+		if ( !function_exists('dh_gen_private') )
+		{
+			require_once(ENANO_ROOT.'/includes/math.php');
+			
+			$dh_supported = true;
+			try
+			{
+				require_once(ENANO_ROOT . '/includes/diffiehellman.php');
+			}
+			catch ( Exception $e )
+			{
+				$dh_supported = false;
+			}
+		}
+		global $_math;
+		
+		// Check for the mode
+		if ( !isset($req['mode']) )
+		{
+			return $this->get_login_response('api_error', 'ERR_JSON_NO_MODE');
+		}
+		
+		// Main processing switch
+		switch ( $req['mode'] )
+		{
+			default:
+				return $this->get_login_response('api_error', 'ERR_JSON_INVALID_MODE');
+				break;
+			case 'getkey':
+				
+				$this->start();
+				
+				return $this->get_login_response('initial');
+				break;
+			case 'login_dh':
+				// User is requesting a login and has sent Diffie-Hellman data.
+				
+				//
+				// KEY RECONSTRUCTION
+				//
+				
+				$userinfo_crypt = $req['userinfo'];
+				$dh_public = $req['dh_public_key'];
+				$dh_hash = $req['dh_secret_hash'];
+				
+				// Check the key
+				if ( !ctype_digit($dh_public) || !ctype_digit($req['dh_client_key']) )
+				{
+					return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_NUMERIC');
+				}
+				
+				// Fetch private key
+				$q = $db->sql_query('SELECT private_key, key_id FROM ' . table_prefix . "diffiehellman WHERE public_key = '$dh_public';");
+				if ( !$q )
+					$db->die_json();
+				
+				if ( $db->numrows() < 1 )
+				{
+					return $this->get_login_response('api_error', 'ERR_DH_KEY_NOT_FOUND');
+				}
+				
+				list($dh_private, $dh_key_id) = $db->fetchrow_num();
+				$db->free_result();
+				
+				// We have the private key, now delete the key pair, we no longer need it
+				$q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE key_id = $dh_key_id;");
+				if ( !$q )
+					$db->die_json();
+				
+				// Generate the shared secret
+				$dh_secret = dh_gen_shared_secret($dh_private, $req['dh_client_key']);
+				$dh_secret = $_math->str($dh_secret);
+				
+				// Did we get all our math right?
+				$dh_secret_check = sha1($dh_secret);
+				if ( $dh_secret_check !== $dh_hash )
+				{
+					return $this->get_login_response('api_error', 'ERR_DH_HASH_NO_MATCH');
+				}
+				
+				// All good! Generate the AES key
+				$aes_key = substr(sha256($dh_secret), 0, ( AES_BITS / 4 ));
+			case 'login_aes':
+				if ( $req['mode'] == 'login_aes' )
+				{
+					// login_aes-specific code
+					$aes_key = $this->fetch_public_key($req['key_aes']);
+					if ( !$aes_key )
+					{
+						return $this->get_login_response('api_error', 'ERR_AES_LOOKUP_FAILED');
+					}
+					$userinfo_crypt = $req['userinfo'];
+				}
+				// shared between the two systems from here on out
+				
+				// decrypt user info
+				$aes_key = hexdecode($aes_key);
+				$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+				// using "true" here disables caching of the decrypted login info (which includes the password)
+				$userinfo_json = $aes->decrypt($userinfo_crypt, $aes_key, ENC_HEX, true);
+				if ( !$userinfo_json )
+				{
+					return $this->get_login_response('api_error', 'ERR_AES_DECRYPT_FAILED');
+				}
+				// de-JSON user info
+				try
+				{
+					$userinfo = enano_json_decode($userinfo_json);
+				}
+				catch ( Exception $e )
+				{
+					return $this->get_login_response('api_error', 'ERR_USERINFO_DECODE_FAILED');
+				}
+				
+			case 'login_pt':
+				// plaintext login
+				if ( $req['mode'] == 'login_pt' )
+				{
+					$userinfo = isset($req['userinfo']) ? $req['userinfo'] : array();
+				}
+				
+				if ( !isset($userinfo['username']) || !isset($userinfo['password']) )
+				{
+					return $this->get_login_response('api_error', 'ERR_USERINFO_MISSING_VALUES');
+				}
+				
+				$username =& $userinfo['username'];
+				$password =& $userinfo['password'];
+				
+				// locked out? check captcha
+				$lockout_data = $this->get_lockout_info();
+				if ( $lockout_data['policy'] == 'captcha' && $lockout_data['active'] )
+				{
+					// policy is captcha -- check if it's correct, and if so, bypass lockout check
+					$real_code = $this->get_captcha($req['captcha_hash']);
+					if ( strtolower($real_code) !== strtolower($req['captcha_code']) )
+					{
+						// captcha is bad
+						return $this->get_login_response('login_failure', 'lockout_bad_captcha');
+					}
+				}
+				else if ( $lockout_data['policy'] == 'lockout' && $lockout_data['active'] )
+				{
+					// we're fully locked out
+					return $this->get_login_response('login_failure', 'lockout_request_denied');
+				}
+				
+				// At this point if any extra info was injected into the login data packet, we need to let plugins process it
+				/**
+ 				* Called upon processing an incoming login request. If you added anything to the userinfo object during the jshook
+ 				* login_build_userinfo, that will be in the $userinfo array here. Expected return values are: true if your plugin has
+ 				* not only succeeded but ALSO issued a session key (bypass the whole Enano builtin login process) and an associative array
+ 				* with "mode" set to "error" and an error string in "error" to send an error back to the client. Any return value other
+ 				* than these will be treated as a pass-through, and the user's password will be validated through Enano's standard process.
+ 				* @hook login_process_userdata_json
+ 				*/
+				
+				$code = $plugins->setHook('login_process_userdata_json', true);
+				
+				foreach ( $code as $cmd )
+				{
+					$result = eval($cmd);
+					if ( $result === true )
+					{
+						return $this->get_login_response('login_success', false, array(
+								'key' => $this->sid_super,
+								'user_id' => $this->user_id,
+								'user_level' => $this->user_level,
+								'reset' => false
+							));
+					}
+					else if ( is_array($result) )
+					{
+						if ( isset($result['mode']) && $result['mode'] === 'error' && isset($result['error']) )
+						{
+							// Pass back any additional information from the error response
+							$append = $result;
+							unset($append['mode'], $append['error']);
+							$append['from_plugin'] = true;
+							
+							return $this->get_login_response('login_failure', $result['error'], $append);
+						}
+					}
+				}
+				
+				// attempt the login
+				$login_result = $this->login_without_crypto($username, $password, false, intval($req['level']), @$req['remember']);
+				
+				if ( $login_result['success'] )
+				{
+					return $this->get_login_response('login_success', false, array(
+							'key' => $this->sid_super,
+							'user_id' => $this->user_id,
+							'user_level' => $this->user_level,
+						));
+				}
+				else if ( !$login_result['success'] && $login_result['error'] === 'valid_reset' )
+				{
+					return $this->get_login_response('reset_pass_used', false, array(
+							'redirect_url' => $login_result['redirect_url']
+						));
+				}
+				else
+				{
+					return $this->get_login_response('login_failure', 'invalid_credentials');
+				}
+				
+				break;
+			case 'clean_key':
+				// Clean out a key, since it won't be used.
+				// This is called when the user clicks Cancel in the AJAX login interface.
+				if ( !empty($req['key_aes']) )
+				{
+					$this->fetch_public_key($req['key_aes']);
+				}
+				if ( !empty($req['key_dh']) )
+				{
+					$pk = $db->escape($req['key_dh']);
+					$q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE public_key = '$pk';");
+					if ( !$q )
+						$db->die_json();
+				}
+				return array(
+						'mode' => 'noop'
+					);
+				break;
+			case 'respond_password_reset':
+				die(enano_json_encode(array(
+						'mode' => 'login_success_reset',
+						'user_id' => $req['user_id'],
+						'temp_password' => $req['temp_password'],
+						'respawn_info' => $this->process_login_request(array('mode' => 'getkey'))
+					)));
+				break;
+			case 'logout':
+				if ( !$this->started )
+					$this->start();
+				if ( !isset($req['csrf_token']) )
+					return array(
+							'mode' => 'error',
+							'error' => 'Invalid CSRF token'
+						);
+				
+				if ( $req['csrf_token'] !== $this->csrf_token )
+					return array(
+							'mode' => 'error',
+							'error' => 'Invalid CSRF token'
+						);
+				$level = isset($req['level']) && is_int($req['level']) ? $req['level'] : USER_LEVEL_MEMBER;
+				if ( ($result = $this->logout($level)) === 'success' )
+				{
+					return array(
+						'mode' => 'logout_success'
+					);
+				}
+				else
+				{
+					return array(
+						'mode' => 'error',
+						'error' => $result
+					);
+				}
+				break;
+		}
+		
+	}
+	
+	/**
+ 	* Generate a packet to send to the client for logins.
+ 	* @param string mode
+ 	* @param array 
+ 	* @return array
+ 	*/
+	
+	function get_login_response($mode, $error = false, $base = array())
+	{
+		$this->start();
+		
+		// init
+		$response = $base;
+		// modules in the packet
+		$response['mode'] = $mode;
+		$response['error'] = $error;
+		$response['crypto'] = $mode !== 'login_success' ? $this->get_login_crypto_packet() : false;
+		$response['lockout'] = $mode !== 'login_success' ? $this->get_lockout_info() : false;
+		$response['extended_time'] = intval(getConfig('session_remember_time', '30'));
+		$response['username'] = $this->user_logged_in ? $this->username : false;
+		return $response;
+	}
+	
+	/**
+ 	* Get a packet of crypto flags for login.
+ 	* @return array
+ 	*/
+	
+	function get_login_crypto_packet()
+	{
+		global $dh_supported, $_math;
+		
+		$response = array();
+		
+		$response['dh_enable'] = $dh_supported;
+		$response['aes_key'] = $this->rijndael_genkey();
+		
+		// Can we do Diffie-Hellman? If so, generate and stash a public/private key pair.
+		if ( $dh_supported )
+		{
+			$dh_key_priv = dh_gen_private();
+			$dh_key_pub = dh_gen_public($dh_key_priv);
+			$dh_key_priv = $_math->str($dh_key_priv);
+			$dh_key_pub = $_math->str($dh_key_pub);
+			$response['dh_public_key'] = $dh_key_pub;
+			// store the keys in the DB
+			$this->sql('INSERT INTO ' . table_prefix . "diffiehellman( public_key, private_key ) VALUES ( '$dh_key_pub', '$dh_key_priv' );");
+		}
+		
+		return $response;
+	}
+	
 }
 
 /**
@@ -4158,457 +4158,457 @@
  */
  
 class Session_ACLPageInfo {
-  
-  /**
-   * The page ID of this ACL info package
-   * @var string
-   */
-   
-  var $page_id;
-  
-  /**
-   * The namespace of the page being checked
-   * @var string
-   */
-   
-  var $namespace;
-  
-  /**
-   * Our list of permission types.
-   * @access private
-   * @var array
-   */
-   
-  var $acl_types = Array();
-  
-  /**
-   * The list of descriptions for the permission types
-   * @var array
-   */
-   
-  var $acl_descs = Array();
-  
-  /**
-   * A list of dependencies for ACL types.
-   * @var array
-   */
-   
-  var $acl_deps = Array();
-  
-  /**
-   * Our tell-all list of permissions. Do not even try to change this.
-   * @access private
-   * @var array
-   */
-   
-  var $perms = Array();
-  
-  /**
-   * Array to track which default permissions are being used
-   * @var array
-   * @access private
-   */
-   
-  var $acl_defaults_used = Array();
-  
-  /**
-   * Tracks whether Wiki Mode is on for the page we're operating on.
-   * @var bool
-   */
-  
-  var $wiki_mode = false;
-  
-  /**
-   * Tracks where permissions were calculated using the ACL_INHERIT_* constants. Layout:
-   * array(
-   *   [permission_name] => array(
-   *       [src] => ACL_INHERIT_*
-   *       [rule_id] => integer
-   *     ),
-   *   ...
-   * )
-   *
-   * @var array
-   */
-  
-  var $perm_resolve_table = array();
-  
-  #
-  # USER PARAMETERS
-  #
-  
-  /**
-   * User ID
-   * @var int
-   */
-  
-  var $user_id = 1;
-  
-  /**
-   * Group membership associative array (group_id => group_name)
-   * @var array
-   */
-  
-  var $groups = array();
-  
-  /**
-   * Constructor.
-   * @param string $page_id The ID of the page to check
-   * @param string $namespace The namespace of the page to check.
-   * @param array $acl_types List of ACL types
-   * @param array $acl_descs List of human-readable descriptions for permissions (associative)
-   * @param array $acl_deps List of dependencies for permissions. For example, viewing history/diffs depends on the ability to read the page.
-   * @param array $base What to start with - this is an attempt to reduce the number of SQL queries.
-   * @param int|string $user_id_or_name Username or ID to search for, defaults to current user
-   * @param array $resolve_table Debugging info for tracking where rules came from, defaults to a blank array.
-   */
-   
-  function __construct($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base, $user_id = null, $groups = null, $resolve_table = array())
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // hack
-    if ( isset($base['__resolve_table']) )
-    {
-      unset($base['__resolve_table']);
-    }
-    
-    foreach ( $acl_types as $perm_type => $_ )
-    {
-      if ( !$session->check_acl_scope($perm_type, $namespace) )
-      {
-        unset($acl_types[$perm_type]);
-        unset($acl_deps[$perm_type]);
-        unset($acl_descs[$perm_type]);
-        unset($base[$perm_type]);
-      }
-    }
-    
-    $this->acl_deps = $acl_deps;
-    $this->acl_types = $acl_types;
-    $this->acl_descs = $acl_descs;
-    
-    $this->perms = $acl_types;
-    $this->perms = $session->acl_merge_complete($this->perms, $base);
-    
-    $this->perm_resolve_table = array();
-    if ( is_array($resolve_table) )
-      $this->perm_resolve_table = $resolve_table;
-    
-    if ( is_int($user_id) && is_array($groups) )
-    {
-      $this->user_id = $user_id;
-      $this->groups = $groups;
-    }
-    else
-    {
-      $this->user_id = $session->user_id;
-      $this->groups = $session->groups;
-    }
-    
-    $this->page_id = $page_id;
-    $this->namespace = $namespace;
-    
-    $this->__calculate();
-  }
-  
-  /**
-   * Performs the actual permission calculation.
-   * @access private
-   */
-  
-  private function __calculate()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $page_id =& $this->page_id;
-    $namespace =& $this->namespace;
-    
-    // PAGE group info
-    $pg_list = $paths->get_page_groups($page_id, $namespace);
-    $pg_info = '';
-    foreach ( $pg_list as $g_id )
-    {
-      $pg_info .= ' ( page_id=\'' . $g_id . '\' AND namespace=\'__PageGroup\' ) OR';
-    }
-    
-    // Build a query to grab ACL info
-    $bs = 'SELECT rules,target_type,target_id,page_id,namespace,rule_id,pg.pg_name,g.group_name FROM '.table_prefix."acl AS a\n"
-        . "  LEFT JOIN " . table_prefix . "page_groups AS pg\n"
-        . "    ON ( ( a.page_id = CAST(pg.pg_id AS char) AND a.namespace = '__PageGroup' ) OR ( a.namespace != '__PageGroup' ) )\n"
-        . "  LEFT JOIN " . table_prefix . "groups AS g\n"
-        . "    ON ( ( a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id ) OR ( a.target_type != " . ACL_TYPE_GROUP . " ) )\n";
-    
-    $bs .= '  WHERE ' . "\n"
-          . '  ( ';
-    $q = Array();
-    $q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
-    if(count($this->groups) > 0)
-    {
-      foreach($this->groups as $g_id => $g_name)
-      {
-        $q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
-      }
-    }
-    // The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
-    // permissions to override group permissions.
-    $bs .= implode(" OR\n    ", $q) . ' ) AND (' . $pg_info . ' ( page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' ) )     
-      ORDER BY target_type ASC, page_id ASC, namespace ASC;';
-      
-    $q = $session->sql($bs);
-    if ( $row = $db->fetchrow($q, true) )
-    {
-      do
-      {
-        $rules = $session->string_to_perm($row['rules']);
-        $is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
-        // log where this comes from
-        if ( $row['namespace'] == '__PageGroup' )
-        {
-          $src = ( $is_everyone ) ? ACL_INHERIT_PG_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_PG_GROUP : ACL_INHERIT_PG_USER );
-          $pg_name = $row['pg_name'];
-        }
-        else
-        {
-          $src = ( $is_everyone ) ? ACL_INHERIT_LOCAL_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_LOCAL_GROUP : ACL_INHERIT_LOCAL_USER );
-        }
-        if ( $row['group_name'] )
-        {
-          $group_name = $row['group_name'];
-        }
-        foreach ( $rules as $perm_type => $perm_value )
-        {
-          if ( !isset($this->perms[$perm_type]) )
-            continue;
-          
-          if ( $this->perms[$perm_type] == AUTH_DENY )
-            continue;
-          
-          if ( !$session->check_acl_scope($perm_type, $this->namespace) )
-            continue;
-          
-          $this->perm_resolve_table[$perm_type] = array(
-              'src' => $src,
-              'rule_id' => $row['rule_id']
-            );
-          if ( isset($pg_name) )
-          {
-            $this->perm_resolve_table[$perm_type]['pg_name'] = $pg_name;
-          }
-          if ( isset($group_name) )
-          {
-            $this->perm_resolve_table[$perm_type]['group_name'] = $group_name;
-          }
-        }
-        $this->acl_merge_with_current($rules, $is_everyone);
-      } while ( $row = $db->fetchrow() );
-    }
-    
-    $this->page_id = $page_id;
-    $this->namespace = $namespace;
-    
-    $pathskey = $paths->nslist[$this->namespace].sanitize_page_id($this->page_id);
-    $ns = namespace_factory($this->page_id, $this->namespace);
-    $cdata = $ns->get_cdata();
-    $ppwm = $cdata['wiki_mode'];
-    unset($ns, $cdata);
-    
-    if ( $ppwm == 1 && ( $session->user_logged_in || getConfig('wiki_mode_require_login') != '1' ) )
-      $this->wiki_mode = true;
-    else if ( $ppwm == 1 && !$session->user_logged_in && getConfig('wiki_mode_require_login') == '1' )
-      $this->wiki_mode = true;
-    else if ( $ppwm == 0 )
-      $this->wiki_mode = false;
-    else if ( $ppwm == 2 )
-    {
-      if ( $this->user_id > 1 )
-      {
-        $this->wiki_mode = ( getConfig('wiki_mode') == '1' );
-      }
-      else
-      {
-        $this->wiki_mode = ( getConfig('wiki_mode') == '1' && getConfig('wiki_mode_require_login') != '1' );
-      }
-    }
-    else
-    {
-      // Ech. Internal logic failure, this should never happen.
-      return false;
-    }
-  }
-  
-  /**
-   * Tells us whether permission $type is allowed or not based on the current rules.
-   * @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
-   * @param bool $no_deps If true, disables dependency checking
-   * @return bool True if allowed, false if denied or if an error occured
-   */
-   
-  function get_permissions($type, $no_deps = false)
-  {
-    // echo '<pre>' . print_r($this->perms, true) . '</pre>';
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( isset( $this->perms[$type] ) )
-    {
-      if ( $this->perms[$type] == AUTH_DENY )
-      {
-        $ret = false;
-      }
-      else if ( $this->perms[$type] == AUTH_WIKIMODE && $this->wiki_mode )
-      {
-        $ret = true;
-      }
-      else if ( $this->perms[$type] == AUTH_WIKIMODE && !$this->wiki_mode )
-      {
-        $ret = false;
-      }
-      else if ( $this->perms[$type] == AUTH_ALLOW )
-      {
-        $ret = true;
-      }
-      else if ( $this->perms[$type] == AUTH_DISALLOW )
-      {
-        $ret = false;
-      }
-    }
-    else if(isset($this->acl_types[$type]))
-    {
-      if ( $this->acl_types[$type] == AUTH_DENY )
-        $ret = false;
-      else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
-        $ret = true;
-      else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
-        $ret = false;
-      else if ( $this->acl_types[$type] == AUTH_ALLOW )
-        $ret = true;
-      else if ( $this->acl_types[$type] == AUTH_DISALLOW )
-        $ret = false;
-    }
-    else
-    {
-      // ACL type is undefined
-      $caller = 'unknown';
-      if ( function_exists('debug_backtrace') )
-      {
-        if ( $bt = @debug_backtrace() )
-        {
-          foreach ( $bt as $trace )
-          {
-            $file = basename($trace['file']);
-            if ( $file != 'sessions.php' )
-            {
-              $caller = $file . ':' . $trace['line'];
-              break;
-            }
-          }
-        }
-      }
-      trigger_error('Unknown access type "' . $type . '", called from ' . $caller . '', E_USER_WARNING);
-      return false; // Be on the safe side and deny access
-    }
-    if ( !$no_deps )
-    {
-      if ( !$this->acl_check_deps($type) )
-        return false;
-    }
-    return $ret;
-  }
-  
-  /**
-   * Tell us if the dependencies for a given permission are met.
-   * @param string The ACL permission ID
-   * @param bool If true, does not return a boolean value, but instead returns array of dependencies that fail
-   * @return bool
-   */
-   
-  function acl_check_deps($type, $debug = false)
-  {
-    // This will only happen if the permissions table is hacked or improperly accessed
-    if(!isset($this->acl_deps[$type]))
-      return $debug ? array() : true;
-    // Permission has no dependencies?
-    if(sizeof($this->acl_deps[$type]) < 1)
-      return $debug ? array() : true;
-    // go through them all and build a flat list of dependencies
-    $deps = $this->acl_deps[$type];
-    while(true)
-    {
-      $j = sizeof($deps);
-      for ( $i = 0; $i < $j; $i++ )
-      {
-        $b = $deps;
-        if ( !isset($this->acl_deps[$deps[$i]]) )
-        {
-          // Action $type depends on action $deps[$i] which cannot be satisfied because $deps[$i] is out of scope.
-          trigger_error("acl_check_deps: $type depends on {$deps[$i]} which is not within scope of $this->namespace; this indicats a bug in ACL rule specification", E_USER_WARNING);
-          return false;
-        }
-        $deps = array_merge($deps, $this->acl_deps[$deps[$i]]);
-        if( $b == $deps )
-        {
-          break 2;
-        }
-        $j = sizeof($deps);
-      }
-    }
-    $debugdata = array();
-    foreach($deps as $d)
-    {
-      // Our dependencies are fully resolved, so tell get_permissions() to not recursively call this function
-      if ( !$this->get_permissions($d, true) )
-      {
-        if ( $debug )
-        {
-          $debugdata[] = $d;
-        }
-        else
-        {
-          return false;
-        }
-      }
-    }
-    return $debug ? $debugdata : true;
-  }
-  
-  /**
-   * Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
-   * @param array The array to merge into the master ACL list
-   * @param bool If true, $perm is treated as the "new default"
-   * @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
-   */
-  
-  function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
-  {
-    foreach ( $this->perms as $i => $p )
-    {
-      if ( isset($perm[$i]) )
-      {
-        if ( $is_everyone && !@$this->acl_defaults_used[$i] )
-          continue;
-        // Decide precedence
-        if ( isset($this->acl_defaults_used[$i]) )
-        {
-          //echo "$i: default in use, overriding to: {$perm[$i]}<br />";
-          // Defaults are in use, override
-          $this->perms[$i] = $perm[$i];
-          $this->acl_defaults_used[$i] = ( $is_everyone );
-        }
-        else
-        {
-          //echo "$i: default NOT in use";
-          // Defaults are not in use, merge as normal
-          if ( $this->perms[$i] != AUTH_DENY )
-          {
-            //echo ", but overriding";
-            $this->perms[$i] = $perm[$i];
-          }
-          //echo "<br />";
-        }
-      }
-    }
-  }
-  
+	
+	/**
+ 	* The page ID of this ACL info package
+ 	* @var string
+ 	*/
+ 	
+	var $page_id;
+	
+	/**
+ 	* The namespace of the page being checked
+ 	* @var string
+ 	*/
+ 	
+	var $namespace;
+	
+	/**
+ 	* Our list of permission types.
+ 	* @access private
+ 	* @var array
+ 	*/
+ 	
+	var $acl_types = Array();
+	
+	/**
+ 	* The list of descriptions for the permission types
+ 	* @var array
+ 	*/
+ 	
+	var $acl_descs = Array();
+	
+	/**
+ 	* A list of dependencies for ACL types.
+ 	* @var array
+ 	*/
+ 	
+	var $acl_deps = Array();
+	
+	/**
+ 	* Our tell-all list of permissions. Do not even try to change this.
+ 	* @access private
+ 	* @var array
+ 	*/
+ 	
+	var $perms = Array();
+	
+	/**
+ 	* Array to track which default permissions are being used
+ 	* @var array
+ 	* @access private
+ 	*/
+ 	
+	var $acl_defaults_used = Array();
+	
+	/**
+ 	* Tracks whether Wiki Mode is on for the page we're operating on.
+ 	* @var bool
+ 	*/
+	
+	var $wiki_mode = false;
+	
+	/**
+ 	* Tracks where permissions were calculated using the ACL_INHERIT_* constants. Layout:
+ 	* array(
+ 	*   [permission_name] => array(
+ 	*       [src] => ACL_INHERIT_*
+ 	*       [rule_id] => integer
+ 	*     ),
+ 	*   ...
+ 	* )
+ 	*
+ 	* @var array
+ 	*/
+	
+	var $perm_resolve_table = array();
+	
+	#
+	# USER PARAMETERS
+	#
+	
+	/**
+ 	* User ID
+ 	* @var int
+ 	*/
+	
+	var $user_id = 1;
+	
+	/**
+ 	* Group membership associative array (group_id => group_name)
+ 	* @var array
+ 	*/
+	
+	var $groups = array();
+	
+	/**
+ 	* Constructor.
+ 	* @param string $page_id The ID of the page to check
+ 	* @param string $namespace The namespace of the page to check.
+ 	* @param array $acl_types List of ACL types
+ 	* @param array $acl_descs List of human-readable descriptions for permissions (associative)
+ 	* @param array $acl_deps List of dependencies for permissions. For example, viewing history/diffs depends on the ability to read the page.
+ 	* @param array $base What to start with - this is an attempt to reduce the number of SQL queries.
+ 	* @param int|string $user_id_or_name Username or ID to search for, defaults to current user
+ 	* @param array $resolve_table Debugging info for tracking where rules came from, defaults to a blank array.
+ 	*/
+ 	
+	function __construct($page_id, $namespace, $acl_types, $acl_descs, $acl_deps, $base, $user_id = null, $groups = null, $resolve_table = array())
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// hack
+		if ( isset($base['__resolve_table']) )
+		{
+			unset($base['__resolve_table']);
+		}
+		
+		foreach ( $acl_types as $perm_type => $_ )
+		{
+			if ( !$session->check_acl_scope($perm_type, $namespace) )
+			{
+				unset($acl_types[$perm_type]);
+				unset($acl_deps[$perm_type]);
+				unset($acl_descs[$perm_type]);
+				unset($base[$perm_type]);
+			}
+		}
+		
+		$this->acl_deps = $acl_deps;
+		$this->acl_types = $acl_types;
+		$this->acl_descs = $acl_descs;
+		
+		$this->perms = $acl_types;
+		$this->perms = $session->acl_merge_complete($this->perms, $base);
+		
+		$this->perm_resolve_table = array();
+		if ( is_array($resolve_table) )
+			$this->perm_resolve_table = $resolve_table;
+		
+		if ( is_int($user_id) && is_array($groups) )
+		{
+			$this->user_id = $user_id;
+			$this->groups = $groups;
+		}
+		else
+		{
+			$this->user_id = $session->user_id;
+			$this->groups = $session->groups;
+		}
+		
+		$this->page_id = $page_id;
+		$this->namespace = $namespace;
+		
+		$this->__calculate();
+	}
+	
+	/**
+ 	* Performs the actual permission calculation.
+ 	* @access private
+ 	*/
+	
+	private function __calculate()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$page_id =& $this->page_id;
+		$namespace =& $this->namespace;
+		
+		// PAGE group info
+		$pg_list = $paths->get_page_groups($page_id, $namespace);
+		$pg_info = '';
+		foreach ( $pg_list as $g_id )
+		{
+			$pg_info .= ' ( page_id=\'' . $g_id . '\' AND namespace=\'__PageGroup\' ) OR';
+		}
+		
+		// Build a query to grab ACL info
+		$bs = 'SELECT rules,target_type,target_id,page_id,namespace,rule_id,pg.pg_name,g.group_name FROM '.table_prefix."acl AS a\n"
+				. "  LEFT JOIN " . table_prefix . "page_groups AS pg\n"
+				. "    ON ( ( a.page_id = CAST(pg.pg_id AS char) AND a.namespace = '__PageGroup' ) OR ( a.namespace != '__PageGroup' ) )\n"
+				. "  LEFT JOIN " . table_prefix . "groups AS g\n"
+				. "    ON ( ( a.target_type = " . ACL_TYPE_GROUP . " AND a.target_id = g.group_id ) OR ( a.target_type != " . ACL_TYPE_GROUP . " ) )\n";
+		
+		$bs .= '  WHERE ' . "\n"
+					. '  ( ';
+		$q = Array();
+		$q[] = '( target_type='.ACL_TYPE_USER.' AND target_id='.$this->user_id.' )';
+		if(count($this->groups) > 0)
+		{
+			foreach($this->groups as $g_id => $g_name)
+			{
+				$q[] = '( target_type='.ACL_TYPE_GROUP.' AND target_id='.intval($g_id).' )';
+			}
+		}
+		// The reason we're using an ORDER BY statement here is because ACL_TYPE_GROUP is less than ACL_TYPE_USER, causing the user's individual
+		// permissions to override group permissions.
+		$bs .= implode(" OR\n    ", $q) . ' ) AND (' . $pg_info . ' ( page_id=\''.$db->escape($page_id).'\' AND namespace=\''.$db->escape($namespace).'\' ) )     
+			ORDER BY target_type ASC, page_id ASC, namespace ASC;';
+			
+		$q = $session->sql($bs);
+		if ( $row = $db->fetchrow($q, true) )
+		{
+			do
+			{
+				$rules = $session->string_to_perm($row['rules']);
+				$is_everyone = ( $row['target_type'] == ACL_TYPE_GROUP && $row['target_id'] == 1 );
+				// log where this comes from
+				if ( $row['namespace'] == '__PageGroup' )
+				{
+					$src = ( $is_everyone ) ? ACL_INHERIT_PG_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_PG_GROUP : ACL_INHERIT_PG_USER );
+					$pg_name = $row['pg_name'];
+				}
+				else
+				{
+					$src = ( $is_everyone ) ? ACL_INHERIT_LOCAL_EVERYONE : ( $row['target_type'] == ACL_TYPE_GROUP ? ACL_INHERIT_LOCAL_GROUP : ACL_INHERIT_LOCAL_USER );
+				}
+				if ( $row['group_name'] )
+				{
+					$group_name = $row['group_name'];
+				}
+				foreach ( $rules as $perm_type => $perm_value )
+				{
+					if ( !isset($this->perms[$perm_type]) )
+						continue;
+					
+					if ( $this->perms[$perm_type] == AUTH_DENY )
+						continue;
+					
+					if ( !$session->check_acl_scope($perm_type, $this->namespace) )
+						continue;
+					
+					$this->perm_resolve_table[$perm_type] = array(
+							'src' => $src,
+							'rule_id' => $row['rule_id']
+						);
+					if ( isset($pg_name) )
+					{
+						$this->perm_resolve_table[$perm_type]['pg_name'] = $pg_name;
+					}
+					if ( isset($group_name) )
+					{
+						$this->perm_resolve_table[$perm_type]['group_name'] = $group_name;
+					}
+				}
+				$this->acl_merge_with_current($rules, $is_everyone);
+			} while ( $row = $db->fetchrow() );
+		}
+		
+		$this->page_id = $page_id;
+		$this->namespace = $namespace;
+		
+		$pathskey = $paths->nslist[$this->namespace].sanitize_page_id($this->page_id);
+		$ns = namespace_factory($this->page_id, $this->namespace);
+		$cdata = $ns->get_cdata();
+		$ppwm = $cdata['wiki_mode'];
+		unset($ns, $cdata);
+		
+		if ( $ppwm == 1 && ( $session->user_logged_in || getConfig('wiki_mode_require_login') != '1' ) )
+			$this->wiki_mode = true;
+		else if ( $ppwm == 1 && !$session->user_logged_in && getConfig('wiki_mode_require_login') == '1' )
+			$this->wiki_mode = true;
+		else if ( $ppwm == 0 )
+			$this->wiki_mode = false;
+		else if ( $ppwm == 2 )
+		{
+			if ( $this->user_id > 1 )
+			{
+				$this->wiki_mode = ( getConfig('wiki_mode') == '1' );
+			}
+			else
+			{
+				$this->wiki_mode = ( getConfig('wiki_mode') == '1' && getConfig('wiki_mode_require_login') != '1' );
+			}
+		}
+		else
+		{
+			// Ech. Internal logic failure, this should never happen.
+			return false;
+		}
+	}
+	
+	/**
+ 	* Tells us whether permission $type is allowed or not based on the current rules.
+ 	* @param string $type The permission identifier ($acl_type passed to sessionManager::register_acl_type())
+ 	* @param bool $no_deps If true, disables dependency checking
+ 	* @return bool True if allowed, false if denied or if an error occured
+ 	*/
+ 	
+	function get_permissions($type, $no_deps = false)
+	{
+		// echo '<pre>' . print_r($this->perms, true) . '</pre>';
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( isset( $this->perms[$type] ) )
+		{
+			if ( $this->perms[$type] == AUTH_DENY )
+			{
+				$ret = false;
+			}
+			else if ( $this->perms[$type] == AUTH_WIKIMODE && $this->wiki_mode )
+			{
+				$ret = true;
+			}
+			else if ( $this->perms[$type] == AUTH_WIKIMODE && !$this->wiki_mode )
+			{
+				$ret = false;
+			}
+			else if ( $this->perms[$type] == AUTH_ALLOW )
+			{
+				$ret = true;
+			}
+			else if ( $this->perms[$type] == AUTH_DISALLOW )
+			{
+				$ret = false;
+			}
+		}
+		else if(isset($this->acl_types[$type]))
+		{
+			if ( $this->acl_types[$type] == AUTH_DENY )
+				$ret = false;
+			else if ( $this->acl_types[$type] == AUTH_WIKIMODE && $paths->wiki_mode )
+				$ret = true;
+			else if ( $this->acl_types[$type] == AUTH_WIKIMODE && !$paths->wiki_mode )
+				$ret = false;
+			else if ( $this->acl_types[$type] == AUTH_ALLOW )
+				$ret = true;
+			else if ( $this->acl_types[$type] == AUTH_DISALLOW )
+				$ret = false;
+		}
+		else
+		{
+			// ACL type is undefined
+			$caller = 'unknown';
+			if ( function_exists('debug_backtrace') )
+			{
+				if ( $bt = @debug_backtrace() )
+				{
+					foreach ( $bt as $trace )
+					{
+						$file = basename($trace['file']);
+						if ( $file != 'sessions.php' )
+						{
+							$caller = $file . ':' . $trace['line'];
+							break;
+						}
+					}
+				}
+			}
+			trigger_error('Unknown access type "' . $type . '", called from ' . $caller . '', E_USER_WARNING);
+			return false; // Be on the safe side and deny access
+		}
+		if ( !$no_deps )
+		{
+			if ( !$this->acl_check_deps($type) )
+				return false;
+		}
+		return $ret;
+	}
+	
+	/**
+ 	* Tell us if the dependencies for a given permission are met.
+ 	* @param string The ACL permission ID
+ 	* @param bool If true, does not return a boolean value, but instead returns array of dependencies that fail
+ 	* @return bool
+ 	*/
+ 	
+	function acl_check_deps($type, $debug = false)
+	{
+		// This will only happen if the permissions table is hacked or improperly accessed
+		if(!isset($this->acl_deps[$type]))
+			return $debug ? array() : true;
+		// Permission has no dependencies?
+		if(sizeof($this->acl_deps[$type]) < 1)
+			return $debug ? array() : true;
+		// go through them all and build a flat list of dependencies
+		$deps = $this->acl_deps[$type];
+		while(true)
+		{
+			$j = sizeof($deps);
+			for ( $i = 0; $i < $j; $i++ )
+			{
+				$b = $deps;
+				if ( !isset($this->acl_deps[$deps[$i]]) )
+				{
+					// Action $type depends on action $deps[$i] which cannot be satisfied because $deps[$i] is out of scope.
+					trigger_error("acl_check_deps: $type depends on {$deps[$i]} which is not within scope of $this->namespace; this indicats a bug in ACL rule specification", E_USER_WARNING);
+					return false;
+				}
+				$deps = array_merge($deps, $this->acl_deps[$deps[$i]]);
+				if( $b == $deps )
+				{
+					break 2;
+				}
+				$j = sizeof($deps);
+			}
+		}
+		$debugdata = array();
+		foreach($deps as $d)
+		{
+			// Our dependencies are fully resolved, so tell get_permissions() to not recursively call this function
+			if ( !$this->get_permissions($d, true) )
+			{
+				if ( $debug )
+				{
+					$debugdata[] = $d;
+				}
+				else
+				{
+					return false;
+				}
+			}
+		}
+		return $debug ? $debugdata : true;
+	}
+	
+	/**
+ 	* Merges the ACL array sent with the current permissions table, deciding precedence based on whether defaults are in effect or not.
+ 	* @param array The array to merge into the master ACL list
+ 	* @param bool If true, $perm is treated as the "new default"
+ 	* @param int 1 if this is a site-wide ACL, 2 if page-specific. Defaults to 2.
+ 	*/
+	
+	function acl_merge_with_current($perm, $is_everyone = false, $scope = 2)
+	{
+		foreach ( $this->perms as $i => $p )
+		{
+			if ( isset($perm[$i]) )
+			{
+				if ( $is_everyone && !@$this->acl_defaults_used[$i] )
+					continue;
+				// Decide precedence
+				if ( isset($this->acl_defaults_used[$i]) )
+				{
+					//echo "$i: default in use, overriding to: {$perm[$i]}<br />";
+					// Defaults are in use, override
+					$this->perms[$i] = $perm[$i];
+					$this->acl_defaults_used[$i] = ( $is_everyone );
+				}
+				else
+				{
+					//echo "$i: default NOT in use";
+					// Defaults are not in use, merge as normal
+					if ( $this->perms[$i] != AUTH_DENY )
+					{
+						//echo ", but overriding";
+						$this->perms[$i] = $perm[$i];
+					}
+					//echo "<br />";
+				}
+			}
+		}
+	}
+	
 }
 
 /**
@@ -4617,12 +4617,12 @@
 
 function cron_clean_login_cache()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  if ( !$db->sql_query('DELETE FROM ' . table_prefix . 'diffiehellman;') )
-    $db->_die();
-  
-  setConfig('login_key_cache', '');
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	if ( !$db->sql_query('DELETE FROM ' . table_prefix . 'diffiehellman;') )
+		$db->_die();
+	
+	setConfig('login_key_cache', '');
 }
 
 register_cron_task('cron_clean_login_cache', 72);
@@ -4633,12 +4633,12 @@
 
 function cron_clean_old_admin_keys()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  $threshold = time() - ( 15 * 60 );
-  $ul_member = USER_LEVEL_MEMBER;
-  if ( !$db->sql_query('DELETE FROM ' . table_prefix . "session_keys WHERE time < $threshold AND auth_level > $ul_member;") )
-    $db->_die();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	$threshold = time() - ( 15 * 60 );
+	$ul_member = USER_LEVEL_MEMBER;
+	if ( !$db->sql_query('DELETE FROM ' . table_prefix . "session_keys WHERE time < $threshold AND auth_level > $ul_member;") )
+		$db->_die();
 }
 
 // Once a week
--- a/includes/sql_parse.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/sql_parse.php	Sun Mar 28 23:10:46 2010 -0400
@@ -22,134 +22,134 @@
 
 class SQL_Parser
 {
-  /**
-   * The SQL to be parsed.
-   * @var string
-   * @access private
-   */
-  
-  private $sql_string;
-  
-  /**
-   * Parsed SQL array
-   * @var array
-   * @access private
-   */
-  
-  private $sql_array;
-  
-  /**
-   * Template variables.
-   * @var array
-   * @access private
-   */
-  
-  private $tpl_strings;
-  
-  /**
-   * Constructor.
-   * @param string If this contains newlines, it will be treated as the target SQL. If not, will be treated as a filename.
-   * @param string If true, force as raw SQL, i.e. don't treat as a filename no matter what
-   */
-  
-  public function __construct($sql, $force_file = false)
-  {
-    if ( strpos($sql, "\n") || $force_file )
-    {
-      $this->sql_string = $sql;
-    }
-    else
-    {
-      if ( file_exists($sql) )
-      {
-        $this->sql_string = @file_get_contents($sql);
-        if ( empty($this->sql_string) )
-        {
-          throw new Exception('SQL file is blank or permissions are bad');
-        }
-      }
-      else
-      {
-        throw new Exception('SQL file doesn\'t exist');
-      }
-      
-    }
-    $this->sql_array = false;
-    $this->tpl_strings = array();
-    
-    // convert \r\n in the schema to \n, in case some FTP client or zip utility ran unix2dos for us
-    // thanks to InvisGhost for reporting this error
-    $this->sql_string = str_replace("\r\n", "\n", $this->sql_string);
-  }
-  
-  /**
-   * Sets template variables.
-   * @param array Associative array of template variables to assign
-   */
-  
-  public function assign_vars($vars)
-  {
-    if ( !is_array($vars) )
-      return false;
-    $this->tpl_strings = array_merge($this->tpl_strings, $vars);
-  }
-  
-  /**
-   * Internal function to parse the SQL.
-   * @access private
-   */
-  
-  private function parse_sql()
-  {
-    $this->sql_array = $this->sql_string;
-    foreach ( $this->tpl_strings as $key => $value )
-    {
-      $this->sql_array = str_replace("{{{$key}}}", $value, $this->sql_array);
-    }
-    
-    // Strip out comments
-    $this->sql_array = explode("\n", $this->sql_array);
-    
-    foreach ( $this->sql_array as $i => $sql )
-    {
-      $query =& $this->sql_array[$i];
-      $t = trim($query);
-      if ( preg_match('/^(\#|--)/i', $t) )
-      {
-        unset($this->sql_array[$i]);
-        unset($query);
-      }
-    }
-    unset($query);
-    
-    $this->sql_array = array_values($this->sql_array);
-    $this->sql_array = implode("\n", $this->sql_array);
-    $this->sql_array = explode(";\n", trim($this->sql_array));
-    
-    foreach ( $this->sql_array as $i => $sql )
-    {
-      $query =& $this->sql_array[$i];
-      $query = trim($query);
-      if ( substr($query, ( strlen($query) - 1 ), 1 ) != ';' )
-      {
-        $query .= ';';
-      }
-    }
-    unset($query);
-  }
-  
-  /**
-   * Returns the parsed array of SQL queries.
-   * @param bool Optional. Defaults to false. If true, a parse is performed even if it already happened.
-   * @return array
-   */
-  
-  public function parse($force_reparse = false)
-  {
-    if ( !$this->sql_array || $force_reparse )
-      $this->parse_sql();
-    return $this->sql_array;
-  }
+	/**
+ 	* The SQL to be parsed.
+ 	* @var string
+ 	* @access private
+ 	*/
+	
+	private $sql_string;
+	
+	/**
+ 	* Parsed SQL array
+ 	* @var array
+ 	* @access private
+ 	*/
+	
+	private $sql_array;
+	
+	/**
+ 	* Template variables.
+ 	* @var array
+ 	* @access private
+ 	*/
+	
+	private $tpl_strings;
+	
+	/**
+ 	* Constructor.
+ 	* @param string If this contains newlines, it will be treated as the target SQL. If not, will be treated as a filename.
+ 	* @param string If true, force as raw SQL, i.e. don't treat as a filename no matter what
+ 	*/
+	
+	public function __construct($sql, $force_file = false)
+	{
+		if ( strpos($sql, "\n") || $force_file )
+		{
+			$this->sql_string = $sql;
+		}
+		else
+		{
+			if ( file_exists($sql) )
+			{
+				$this->sql_string = @file_get_contents($sql);
+				if ( empty($this->sql_string) )
+				{
+					throw new Exception('SQL file is blank or permissions are bad');
+				}
+			}
+			else
+			{
+				throw new Exception('SQL file doesn\'t exist');
+			}
+			
+		}
+		$this->sql_array = false;
+		$this->tpl_strings = array();
+		
+		// convert \r\n in the schema to \n, in case some FTP client or zip utility ran unix2dos for us
+		// thanks to InvisGhost for reporting this error
+		$this->sql_string = str_replace("\r\n", "\n", $this->sql_string);
+	}
+	
+	/**
+ 	* Sets template variables.
+ 	* @param array Associative array of template variables to assign
+ 	*/
+	
+	public function assign_vars($vars)
+	{
+		if ( !is_array($vars) )
+			return false;
+		$this->tpl_strings = array_merge($this->tpl_strings, $vars);
+	}
+	
+	/**
+ 	* Internal function to parse the SQL.
+ 	* @access private
+ 	*/
+	
+	private function parse_sql()
+	{
+		$this->sql_array = $this->sql_string;
+		foreach ( $this->tpl_strings as $key => $value )
+		{
+			$this->sql_array = str_replace("{{{$key}}}", $value, $this->sql_array);
+		}
+		
+		// Strip out comments
+		$this->sql_array = explode("\n", $this->sql_array);
+		
+		foreach ( $this->sql_array as $i => $sql )
+		{
+			$query =& $this->sql_array[$i];
+			$t = trim($query);
+			if ( preg_match('/^(\#|--)/i', $t) )
+			{
+				unset($this->sql_array[$i]);
+				unset($query);
+			}
+		}
+		unset($query);
+		
+		$this->sql_array = array_values($this->sql_array);
+		$this->sql_array = implode("\n", $this->sql_array);
+		$this->sql_array = explode(";\n", trim($this->sql_array));
+		
+		foreach ( $this->sql_array as $i => $sql )
+		{
+			$query =& $this->sql_array[$i];
+			$query = trim($query);
+			if ( substr($query, ( strlen($query) - 1 ), 1 ) != ';' )
+			{
+				$query .= ';';
+			}
+		}
+		unset($query);
+	}
+	
+	/**
+ 	* Returns the parsed array of SQL queries.
+ 	* @param bool Optional. Defaults to false. If true, a parse is performed even if it already happened.
+ 	* @return array
+ 	*/
+	
+	public function parse($force_reparse = false)
+	{
+		if ( !$this->sql_array || $force_reparse )
+			$this->parse_sql();
+		return $this->sql_array;
+	}
 }
 
 ?>
--- a/includes/stats.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/stats.php	Sun Mar 28 23:10:46 2010 -0400
@@ -16,33 +16,33 @@
 
 function doStats( $page_id = false, $namespace = false )
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if(getConfig('log_hits') == '1')
-  {
-    if(!$page_id || !$namespace)
-    {
-      $page_id = $paths->page_id;
-      $namespace = $paths->namespace;
-    }
-    if($namespace == 'Special' || $namespace == 'Admin') 
-    {
-      return false;
-    }
-    static $stats_done = false;
-    static $stats_data = Array();
-    if(!$stats_done)
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'hits (username,time,page_id,namespace) VALUES(\''.$db->escape($session->username).'\', '.time().', \''.$db->escape($page_id).'\', \''.$db->escape($namespace).'\')');
-      if(!$q)
-      {
-        echo $db->get_error();
-        return false;
-      }
-      $db->free_result();
-      $stats_done = true;
-      return true;
-    }
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	if(getConfig('log_hits') == '1')
+	{
+		if(!$page_id || !$namespace)
+		{
+			$page_id = $paths->page_id;
+			$namespace = $paths->namespace;
+		}
+		if($namespace == 'Special' || $namespace == 'Admin') 
+		{
+			return false;
+		}
+		static $stats_done = false;
+		static $stats_data = Array();
+		if(!$stats_done)
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'hits (username,time,page_id,namespace) VALUES(\''.$db->escape($session->username).'\', '.time().', \''.$db->escape($page_id).'\', \''.$db->escape($namespace).'\')');
+			if(!$q)
+			{
+				echo $db->get_error();
+				return false;
+			}
+			$db->free_result();
+			$stats_done = true;
+			return true;
+		}
+	}
 }
 
 /**
@@ -53,26 +53,26 @@
 
 function stats_top_pages($num = 5)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if(!is_int($num)) 
-  {
-    return false;
-  }
-  
-  $data = array();
-  $q = $db->sql_query('SELECT COUNT(hit_id) AS num_hits, page_id, namespace FROM '.table_prefix.'hits GROUP BY page_id, namespace ORDER BY num_hits DESC LIMIT ' . $num . ';');
-  
-  while ( $row = $db->fetchrow($q) )
-  {
-    $title = get_page_title_ns($row['page_id'], $row['namespace']);
-    $data[] = array(
-        'page_urlname' => $paths->get_pathskey($row['page_id'], $row['namespace']),
-        'page_title' => $title,
-        'num_hits' => $row['num_hits']
-      );
-  }
-  
-  return $data;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	if(!is_int($num)) 
+	{
+		return false;
+	}
+	
+	$data = array();
+	$q = $db->sql_query('SELECT COUNT(hit_id) AS num_hits, page_id, namespace FROM '.table_prefix.'hits GROUP BY page_id, namespace ORDER BY num_hits DESC LIMIT ' . $num . ';');
+	
+	while ( $row = $db->fetchrow($q) )
+	{
+		$title = get_page_title_ns($row['page_id'], $row['namespace']);
+		$data[] = array(
+				'page_urlname' => $paths->get_pathskey($row['page_id'], $row['namespace']),
+				'page_title' => $title,
+				'num_hits' => $row['num_hits']
+			);
+	}
+	
+	return $data;
 }
 
 ?>
--- a/includes/tagcloud.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/tagcloud.php	Sun Mar 28 23:10:46 2010 -0400
@@ -21,141 +21,141 @@
 
 class TagCloud
 {
-  
-  /**
-   * The list of words in the cloud.
-   * @var array
-   */
-  
-  var $words = array();
-  
-  /**
-   * Constructor.
-   * @param array Optional. An initial list of words, just a plain old array.
-   */
-  
-  function __construct($words = array())
-  {
-    if ( count($words) > 0 )
-    {
-      foreach ( $words as $word )
-        $this->add_word($word);
-    }
-  }
-  
-  /**
-   * Adds a word into the word list.
-   * @param string The word to add
-   */
-  
-  function add_word($word)
-  {
-    $word = sanitize_tag($word);
-    
-    if ( isset($this->words[$word]) )
-      $this->words[$word] += 1;
-    else
-      $this->words[$word] = 1;
-  }
-  
-  /**
-   * Returns the total size of the cloud.
-   * @return int
-   */
-  
-  function get_cloud_size()
-  {
-    return array_sum($this->words);
-  }
-  
-  /**
-   * Shuffles the cloud.
-   */
-  
-  function shuffle_cloud()
-  {
-    $keys = array_keys($this->words);
-    if ( !$keys || empty($keys) || !is_array($keys) )
-      return null;
-    
-    shuffle($keys);
-    if ( !$keys || empty($keys) || !is_array($keys) )
-      return null;
-    
-    $temp = $this->words;
-    $this->words = array();
-    foreach ( $keys as $word )
-    {
-      $this->words[$word] = $temp[$word];
-    }
-    
-    unset($temp);
-  }
-  
-  /**
-   * Returns the popularity index (scale class) for a 1-100 number.
-   * @param int
-   * @return int
-   */
-  
-  function get_scale_class($val)
-  {
-    $ret = 0;
-    if ( $val >= 99 )
-      $ret = 1;
-    else if ( $val >= 70 )
-      $ret = 2;
-    else if ( $val >= 60 )
-      $ret = 3;
-    else if ( $val >= 50 )
-      $ret = 4;
-    else if ( $val >= 40 )
-      $ret = 5;
-    else if ( $val >= 30 )
-      $ret = 6;
-    else if ( $val >= 20 )
-      $ret = 7;
-    else if ( $val >= 10 )
-      $ret = 8;
-    else if ( $val >= 5 )
-      $ret = 9;
-    return $ret;
-  }
-  
-  /**
-   * Generates and returns HTML for the cloud.
-   * @param string Optional. The CSS class applied to all <span> tags. Can be "normal" or "small". Defaults to "normal".
-   * @param string Optional. The alignment for the div. Defaults to "center".
-   * @return string
-   */
-   
-  function make_html($span_class = 'normal', $div_align = 'center')
-  {
-    global $lang;
-    $html = array();
-    $max  = max($this->words);
-    $size = $this->get_cloud_size();
-    $inc = 0;
-    if ( count($this->words) > 0 )
-    {
-      foreach ( $this->words as $word => $popularity )
-      {
-        $inc++;
-        $word = htmlspecialchars($word);
-        $percent = ( $popularity / $max ) * 100;
-        $index = $this->get_scale_class($percent);
-        $newline = ( $inc == 5 ) ? "<br />" : '';
-        ( $inc == 5 ) ? $inc = 0 : null;
-        $url = makeUrlNS('Special', 'TagCloud/' . htmlspecialchars($word));
-        $popstring = ( $popularity == 1 ) ? $lang->get('pagetools_tagcloud_tip_popularity_one') : $lang->get('pagetools_tagcloud_tip_popularity_plural', array('popularity' => $popularity));
-        $html[] = "<span class='tc_word_{$span_class} tc_{$span_class}_index_{$index}'><a href='$url' title='$popstring'>$word</a></span>"; // $newline";
-      }
-    }
-    $html = '<div style="text-align: ' . $div_align . '; margin: 0 auto; max-width: 400px;">' . implode("\n", $html) . '</div>';
-    return $html;
-  }
-   
-  
+	
+	/**
+ 	* The list of words in the cloud.
+ 	* @var array
+ 	*/
+	
+	var $words = array();
+	
+	/**
+ 	* Constructor.
+ 	* @param array Optional. An initial list of words, just a plain old array.
+ 	*/
+	
+	function __construct($words = array())
+	{
+		if ( count($words) > 0 )
+		{
+			foreach ( $words as $word )
+				$this->add_word($word);
+		}
+	}
+	
+	/**
+ 	* Adds a word into the word list.
+ 	* @param string The word to add
+ 	*/
+	
+	function add_word($word)
+	{
+		$word = sanitize_tag($word);
+		
+		if ( isset($this->words[$word]) )
+			$this->words[$word] += 1;
+		else
+			$this->words[$word] = 1;
+	}
+	
+	/**
+ 	* Returns the total size of the cloud.
+ 	* @return int
+ 	*/
+	
+	function get_cloud_size()
+	{
+		return array_sum($this->words);
+	}
+	
+	/**
+ 	* Shuffles the cloud.
+ 	*/
+	
+	function shuffle_cloud()
+	{
+		$keys = array_keys($this->words);
+		if ( !$keys || empty($keys) || !is_array($keys) )
+			return null;
+		
+		shuffle($keys);
+		if ( !$keys || empty($keys) || !is_array($keys) )
+			return null;
+		
+		$temp = $this->words;
+		$this->words = array();
+		foreach ( $keys as $word )
+		{
+			$this->words[$word] = $temp[$word];
+		}
+		
+		unset($temp);
+	}
+	
+	/**
+ 	* Returns the popularity index (scale class) for a 1-100 number.
+ 	* @param int
+ 	* @return int
+ 	*/
+	
+	function get_scale_class($val)
+	{
+		$ret = 0;
+		if ( $val >= 99 )
+			$ret = 1;
+		else if ( $val >= 70 )
+			$ret = 2;
+		else if ( $val >= 60 )
+			$ret = 3;
+		else if ( $val >= 50 )
+			$ret = 4;
+		else if ( $val >= 40 )
+			$ret = 5;
+		else if ( $val >= 30 )
+			$ret = 6;
+		else if ( $val >= 20 )
+			$ret = 7;
+		else if ( $val >= 10 )
+			$ret = 8;
+		else if ( $val >= 5 )
+			$ret = 9;
+		return $ret;
+	}
+	
+	/**
+ 	* Generates and returns HTML for the cloud.
+ 	* @param string Optional. The CSS class applied to all <span> tags. Can be "normal" or "small". Defaults to "normal".
+ 	* @param string Optional. The alignment for the div. Defaults to "center".
+ 	* @return string
+ 	*/
+ 	
+	function make_html($span_class = 'normal', $div_align = 'center')
+	{
+		global $lang;
+		$html = array();
+		$max  = max($this->words);
+		$size = $this->get_cloud_size();
+		$inc = 0;
+		if ( count($this->words) > 0 )
+		{
+			foreach ( $this->words as $word => $popularity )
+			{
+				$inc++;
+				$word = htmlspecialchars($word);
+				$percent = ( $popularity / $max ) * 100;
+				$index = $this->get_scale_class($percent);
+				$newline = ( $inc == 5 ) ? "<br />" : '';
+				( $inc == 5 ) ? $inc = 0 : null;
+				$url = makeUrlNS('Special', 'TagCloud/' . htmlspecialchars($word));
+				$popstring = ( $popularity == 1 ) ? $lang->get('pagetools_tagcloud_tip_popularity_one') : $lang->get('pagetools_tagcloud_tip_popularity_plural', array('popularity' => $popularity));
+				$html[] = "<span class='tc_word_{$span_class} tc_{$span_class}_index_{$index}'><a href='$url' title='$popstring'>$word</a></span>"; // $newline";
+			}
+		}
+		$html = '<div style="text-align: ' . $div_align . '; margin: 0 auto; max-width: 400px;">' . implode("\n", $html) . '</div>';
+		return $html;
+	}
+ 	
+	
 }
 
 ?>
--- a/includes/template.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/template.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,1686 +13,1686 @@
  
 class template
 {
-  var $tpl_strings, $tpl_bool, $vars_assign_history, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list, $default_theme, $default_style, $plugin_blocks, $plugin_blocks_content, $namespace_string, $style_list, $theme_loaded;
-  
-  var $theme_initted = false;
-  var $page_initted = false;
-  var $elements = false;
-  var $page_id = false;
-  var $namespace = false;
-  var $js_preload = array();
-  var $js_append = '';
-  
-  /**
-   * Page action conditions
-   * @var array
-   */
-  
-  var $conds = array();
-  
-  /**
-   * The PageProcessor for the current page
-   * @var object
-   */
-  
-  var $page = false;
-  
-  /**
-   * The list of themes that are critical for Enano operation. This doesn't include oxygen which
-   * remains a user theme. By default this is admin and printable which have to be loaded on demand.
-   * @var array
-   */
-  
-  var $system_themes = array('admin', 'printable');
-  
-  /**
-   * Set to true if the site is disabled and thus a message needs to be shown. This should ONLY be changed by common.php.
-   * @var bool
-   * @access private
-   */
-  
-  var $site_disabled = false;
-  
-  /**
-   * One of the absolute best parts of Enano :-P
-   * @var string
-   */
-  
-  var $fading_button = '';
-  
-  function __construct()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $cache;
-    
-    $this->tpl_bool    = Array();
-    $this->tpl_strings = Array();
-    $this->sidebar_extra = '';
-    $this->toolbar_menu = '';
-    $this->additional_headers = '';
-    $this->plugin_blocks = Array();
-    $this->plugin_blocks_content = array();
-    $this->theme_loaded = false;
-    
-    $this->theme_list = Array();
-    $this->named_theme_list = Array();
-    
-    $this->vars_assign_history = array(
-        'strings' => array(),
-        'bool' => array()
-      );
-    
-    if ( defined('IN_ENANO_UPGRADE') )
-    {
-      return $this->construct_compat();
-    }
-    
-    if ( !$this->theme_list = $cache->fetch('themes') )
-    {
-      $q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style, group_policy, group_list FROM ' . table_prefix . 'themes;');
-      if ( !$q )
-        $db->_die('template.php selecting theme list');
-      
-      $i = 0;
-      while ( $row = $db->fetchrow() )
-      {
-        $this->theme_list[$i] = $row;
-        $i++;
-      }
-      unset($theme);
-      $this->theme_list = array_values($this->theme_list);
-      $cache->store('themes', $this->theme_list, -1);
-    }
-    
-    // Create associative array of themes
-    foreach ( $this->theme_list as $i => &$theme )
-      $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
-    
-    unset($theme);
-    
-    $this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id'];
-    $this->named_theme_list[ $this->default_theme ]['css'] = $this->get_theme_css_files($this->default_theme);
-    // Come up with the default style. If the CSS file specified in default_style exists, we're good, just
-    // use that. Otherwise, use the first stylesheet that comes to mind.
-    $df_data =& $this->named_theme_list[ $this->default_theme ];
-    $this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
-  }
-  
-  /**
-   * Gets the list of available CSS files (styles) for the specified theme.
-   * @param string Theme ID
-   * @return array
-   */
-  
-  function get_theme_css_files($theme_id)
-  {
-    $css = array();
-    $dir = ENANO_ROOT . "/themes/{$theme_id}/css";
-    if ( $dh = @opendir($dir) )
-    {
-      while ( ( $file = @readdir($dh) ) !== false )
-      {
-        if ( preg_match('/\.css$/', $file) )
-          $css[] = preg_replace('/\.css$/', '', $file);
-      }
-      closedir($dh);
-    }
-    // No CSS files? If so, nuke it.
-    if ( count($css) < 1 )
-    {
-      unset($this->theme_list[$theme_id]);
-    }
-    return $css;
-  }
-  
-  /**
-   * Failsafe constructor for upgrades.
-   */
-  
-  function construct_compat()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $this->tpl_bool    = Array();
-    $this->tpl_strings = Array();
-    $this->sidebar_extra = '';
-    $this->toolbar_menu = '';
-    $this->additional_headers = '';
-    $this->plugin_blocks = Array();
-    $this->theme_loaded = false;
-    
-    $this->fading_button = '<div style="background-image: url('.scriptPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
-                              <a style="background-image: none; padding-right: 0;" href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.scriptPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
-                            </div>';
-    
-    $this->theme_list = Array();
-    $this->named_theme_list = Array();
-    
-    $q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style FROM ' . table_prefix . 'themes;');
-    if ( !$q )
-      $db->_die('template.php selecting theme list');
-    
-    $i = 0;
-    while ( $row = $db->fetchrow() )
-    {
-      $this->theme_list[$i] = $row;
-      $i++;
-    }
-    // List out all CSS files for this theme
-    foreach ( $this->theme_list as $i => &$theme )
-    {
-      $theme['css'] = array();
-      $dir = ENANO_ROOT . "/themes/{$theme['theme_id']}/css";
-      if ( $dh = @opendir($dir) )
-      {
-        while ( ( $file = @readdir($dh) ) !== false )
-        {
-          if ( preg_match('/\.css$/', $file) )
-            $theme['css'][] = preg_replace('/\.css$/', '', $file);
-        }
-        closedir($dh);
-      }
-      // No CSS files? If so, nuke it.
-      if ( count($theme['css']) < 1 )
-      {
-        unset($this->theme_list[$i]);
-      }
-    }
-    $this->theme_list = array_values($this->theme_list);
-    // Create associative array of themes
-    foreach ( $this->theme_list as $i => &$theme )
-      $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
-    
-    $this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id'];
-    // Come up with the default style. If the CSS file specified in default_style exists, we're good, just
-    // use that. Otherwise, use the first stylesheet that comes to mind.
-    $df_data =& $this->named_theme_list[ $this->default_theme ];
-    $this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
-  }
-  
-  /**
-   * Systematically deletes themes from available list if they're blocked by theme security settings. Called when session->start() finishes.
-   */
-  
-  function process_theme_acls()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    // generate the fading button - needs to be done after sessions are started
-    $admintitle = ( $session->user_level >= USER_LEVEL_ADMIN && is_object(@$lang) ) ? ' title="' . $lang->get('sidebar_btn_enanopowered_admin_tip') . '"' : '';
-    $this->fading_button = '<div style="background-image: url('.cdnPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
-                              <a style="background-image: none; padding-right: 0;" href="http://enanocms.org/" onclick="window.open(this.href); return false;"' . $admintitle . '><img style="border-width: 0;" alt=" " src="'.cdnPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
-                            </div>';
-    
-    // For each theme, check ACLs and delete from RAM if not authorized
-    foreach ( $this->theme_list as $i => $theme )
-    {
-      if ( !@$theme['group_list'] )
-        continue;
-      if ( $theme['theme_id'] === getConfig('theme_default') )
-        continue;
-      switch ( $theme['group_policy'] )
-      {
-        case 'allow_all':
-          // Unconditionally allowed
-          continue;
-          break;
-        case 'whitelist':
-          // If we're not on the list, off to the left please
-          $list = enano_json_decode($theme['group_list']);
-          $allowed = false;
-          foreach ( $list as $acl )
-          {
-            if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) )
-              // Invalid list entry, silently allow (maybe not a good idea but
-              // really, these things are checked before they're inserted)
-              continue 2;
-            $mode = $match[1];
-            $id = intval($match[2]);
-            switch ( $mode )
-            {
-              case 'u':
-                $allowed = ( $id == $session->user_id );
-                if ( $allowed )
-                  break 2;
-                break;
-              case 'g':
-                $allowed = ( isset($session->groups[$id]) );
-                if ( $allowed )
-                  break 2;
-            }
-          }
-          if ( !$allowed )
-          {
-            unset($this->theme_list[$i]);
-          }
-          break;
-        case 'blacklist':
-          // If we're ON the list, off to the left please
-          $list = enano_json_decode($theme['group_list']);
-          $allowed = true;
-          foreach ( $list as $acl )
-          {
-            if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) )
-              // Invalid list entry, silently allow (maybe not a good idea but
-              // really, these things are checked before they're inserted)
-              continue 2;
-            $mode = $match[1];
-            $id = intval($match[2]);
-            switch ( $mode )
-            {
-              case 'u':
-                $allowed = ( $id != $session->user_id );
-                if ( !$allowed )
-                  break 2;
-                break;
-              case 'g':
-                $allowed = ( !isset($session->groups[$id]) );
-                if ( !$allowed )
-                  break 2;
-            }
-          }
-          if ( !$allowed )
-          {
-            unset($this->theme_list[$i]);
-          }
-          break;
-      }
-    }
-    
-    $this->theme_list = array_values($this->theme_list);
-    
-    // Rebuild associative theme list
-    $this->named_theme_list = array();
-    foreach ( $this->theme_list as $i => &$theme )
-      $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
-  }
-  
-  /**
-   * Register a new sidebar block.
-   * @param string Block title
-   * @param string Block HTML
-   * @param bool If true, the class of the block will be the same as the one used for blocks that are a list of links instead of HTML content. This can do some wacky things like make all your <a>s block level.
-   */
-  
-  function sidebar_widget($t, $h, $use_normal_section = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( !$this->theme_loaded )
-    {
-      $this->load_theme($session->theme, $session->style);
-    }
-    if(!$this->sidebar_widgets)
-      $this->sidebar_widgets = '';
-    $tplvars = $this->extract_vars('elements.tpl');
-    
-    if ( $use_normal_section )
-    {
-      $parser = $this->makeParserText($tplvars['sidebar_section']);
-    }
-    else
-    {
-      $parser = $this->makeParserText($tplvars['sidebar_section_raw']);
-    }
-    
-    $parser->assign_vars(Array('TITLE' => '{TITLE}','CONTENT' => $h));
-    $this->plugin_blocks[$t] = $parser->run();
-    $this->plugin_blocks_content[$t] = $h;
-    $this->sidebar_widgets .= $parser->run();
-  }
-  function add_header($html)
-  {
-    /* debug only **
-    $bt = debug_backtrace();
-    $bt = $bt[1];
-    $this->additional_headers .= "\n    <!-- {$bt['file']}:{$bt['line']} -->\n    " . $html;
-    */
-    $this->additional_headers .= "\n   " . $html;
-  }
-  function get_css($s = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $this->init_vars();
-    
-    $path = ( $s ) ? 'css/'.$s : 'css/'.$this->style.'.css';
-    
-    if ( !file_exists(ENANO_ROOT . '/themes/' . $this->theme . '/' . $path) )
-    {
-      echo "/* WARNING: Falling back to default file because file $path does not exist */\n";
-      $path = 'css/' . $this->style_list[0] . '.css';
-    }
-    
-    return $this->process_template($path);
-  }
-  function load_theme($name = false, $css = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $this->theme = ( $name ) ? $name : $session->theme;
-    $this->style = ( $css ) ? $css : $session->style;
-    if ( !$this->theme )
-    {
-      $this->theme = $this->theme_list[0]['theme_id'];
-      $this->style = preg_replace('/\.css$/', '', $this->theme_list[0]['default_style']);
-    }
-    // Make sure we're allowed to use this theme.
-    if ( (
-        // If it was removed, it's probably blocked by an ACL, or it was uninstalled
-        !isset($this->named_theme_list[$this->theme]) ||
-        // Check if the theme is disabled
-        ( isset($this->named_theme_list[$this->theme]) && isset($this->named_theme_list[$this->theme]['enabled']) && $this->named_theme_list[$this->theme]['enabled'] == 0 ) )
-        // Above all, if it's a system theme, don't inhibit the loading process.
-        && !in_array($this->theme, $this->system_themes)
-      )
-    {
-      // No, something is preventing it - fall back to site default
-      $this->theme = $this->default_theme;
-      
-      // Come up with the default style. If the CSS file specified in default_style exists, we're good, just
-      // use that. Otherwise, use the first stylesheet that comes to mind.
-      $df_data =& $this->named_theme_list[ $this->theme ];
-      $this->style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
-    }
-    // The list of styles for the currently selected theme
-    $this->style_list =& $this->named_theme_list[ $this->theme ]['css'];
-    $this->theme_loaded = true;
-  }
-  
-  /**
-   * Change the theme we're supposed to display.
-   * @param string Theme name
-   * @param string Style name; optional
-   */
-  
-  function set_theme($theme = false, $style = false)
-  {
-    $this->theme_initted = false;
-    $this->load_theme($theme, $style);
-  }
-  
-  /**
-   * Change the page we're supposed to generate for
-   * @param mixed Page ID *or* PageProcessor. If a PageProcessor, pulls permission info and such from that; if not, starts a PageProcessor. YOU SHOULD USE A PageProcessor WHENEVER POSSIBLE! It improves efficiency.
-   * @param string Namespace; not required if including a PageProcessor.
-   */
-  
-  function set_page($page_id_or_pp, $namespace = false)
-  {
-    if ( is_object($page_id_or_pp) && get_class($page_id_or_pp) === 'PageProcessor' )
-    {
-      $this->page_initted = false;
-      $page =& $page_id_or_pp;
-      $this->page = $page;
-      $this->page_id = $page->page_id;
-      $this->namespace = $page->namespace;
-    }
-    else if ( is_string($page_id_or_pp) )
-    {
-      if ( !is_string($namespace) )
-        return false;
-      
-      if ( $page_id_or_pp === $this->page_id && $namespace === $this->namespace )
-        return true;
-      
-      $this->page_initted = false;
-      $this->page = false;
-      $this->page_id = sanitize_page_id($page_id_or_pp);
-      $this->namespace = $namespace;
-    }
-    else
-    {
-      return false;
-    }
-    return true;
-  }
-  
-  /**
-   * Queue a Javascript file to be loaded with the page instead of on demand.
-   * @param mixed Javascript file string or array thereof, extensions are optional
-   * @example
-   <code>
-   $template->preload_js(array('jquery', 'jquery-ui'));
-   $template->preload_js('admin-menu.js');
-   </code>
-   * @return null
-   */
-  
-  function preload_js($filemixed)
-  {
-    if ( is_string($filemixed) )
-      $files = array($filemixed);
-    else if ( is_array($filemixed) )
-      $files = $filemixed;
-    else
-      // :-/
-      return null;
-      
-    $this->js_preload = array_values(array_merge($this->js_preload, $files));
-  }
-  
-  /**
-   * Queue some HTML to be inserted after the Javascript runtime.
-   * @param string HTML glob
-   */
-  
-  function add_header_js($html)
-  {
-    $this->js_append .= "$html\n    ";
-  }
-  
-  /**
-   * Global, only-called-once init. Goes to all themes.
-   */
-  
-  function init_global_vars()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $email;
-    
-    $is_opera = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')) ? true : false;
-    $is_msie = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? true : false;
-    
-    $this->assign_bool(array(
-        'auth_admin' => $session->user_level >= USER_LEVEL_ADMIN ? true : false,
-        'user_logged_in' => $session->user_logged_in,
-        'opera' => $is_opera,
-        'msie' => $is_msie
-      ));
-    
-    if ( $session->sid_super )
-    {
-      $ash = '&amp;auth=' . $session->sid_super;
-      $asq = "?auth=" . $session->sid_super;
-      $asa = "&auth=" . $session->sid_super;
-      $as2 = htmlspecialchars(urlSeparator) . 'auth='.$session->sid_super;
-    }
-    else
-    {
-      $asq = '';
-      $asa = '';
-      $as2 = '';
-      $ash = '';
-    }
-    
-    // Append the Enano version to URLs to break the cache on upgrades
-    $enano_version = enano_version();
-    
-    // Set up javascript includes
-    // these depend heavily on whether we have a CDN to work with or not
-    if ( getConfig('cdn_path') )
-    {
-      // we're on a CDN, point to static includes
-      // CLI javascript compression script: includes/clientside/jscompress.php
-      $js_head = '<script type="text/javascript" src="' . cdnPath . '/includes/clientside/static/enano-lib-basic.js?' . $enano_version . '"></script>';
-      
-      if ( !empty($this->js_preload) )
-      {
-        $loadlines = array();
-        
-        // make unique
-        foreach ( $this->js_preload as &$script )
-        {
-          $script = preg_replace('/\.js$/', '', $script) . '.js';
-        }
-        unset($script);
-        $this->js_preload = array_unique($this->js_preload);
-        
-        foreach ( $this->js_preload as $script )
-        {
-          $js_head .= "\n    <script type=\"text/javascript\" src=\"" . cdnPath . "/includes/clientside/static/$script?$enano_version\"></script>";
-          // special case for l10n: also load strings
-          if ( $script == 'l10n.js' )
-          {
-            global $lang;
-            $js_head .= "\n    <script type=\"text/javascript\" src=\"" . makeUrlNS("Special", "LangExportJSON/$lang->lang_id", "$enano_version") . "\"></script>";
-          }
-          $loadlines[] = "loaded_components['$script'] = true;";
-        }
-        
-        // tell the system that this stuff is already loaded
-        $loadlines = implode("\n      ", $loadlines);
-        $js_head .= "\n    <script type=\"text/javascript\">
-      var loaded_components = loaded_components || {};
-      $loadlines
-    </script>";
-      }
-      
-      $js_foot = <<<JSEOF
-    <script type="text/javascript">
-      // This initializes the Javascript runtime when the DOM is ready - not when the page is
-      // done loading, because enano-lib-basic still has to load some 15 other script files
-      // check for the init function - this is a KHTML fix
-      // This doesn't seem to work properly in IE in 1.1.x - there are some problems with
-      // tinyMCE and l10n.
-      if ( typeof ( enano_init ) == 'function' && !IE )
-      {
-        enano_init();
-        window.onload = function(e) {  };
-      }
-    </script>
-    $this->js_append
+	var $tpl_strings, $tpl_bool, $vars_assign_history, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list, $default_theme, $default_style, $plugin_blocks, $plugin_blocks_content, $namespace_string, $style_list, $theme_loaded;
+	
+	var $theme_initted = false;
+	var $page_initted = false;
+	var $elements = false;
+	var $page_id = false;
+	var $namespace = false;
+	var $js_preload = array();
+	var $js_append = '';
+	
+	/**
+ 	* Page action conditions
+ 	* @var array
+ 	*/
+	
+	var $conds = array();
+	
+	/**
+ 	* The PageProcessor for the current page
+ 	* @var object
+ 	*/
+	
+	var $page = false;
+	
+	/**
+ 	* The list of themes that are critical for Enano operation. This doesn't include oxygen which
+ 	* remains a user theme. By default this is admin and printable which have to be loaded on demand.
+ 	* @var array
+ 	*/
+	
+	var $system_themes = array('admin', 'printable');
+	
+	/**
+ 	* Set to true if the site is disabled and thus a message needs to be shown. This should ONLY be changed by common.php.
+ 	* @var bool
+ 	* @access private
+ 	*/
+	
+	var $site_disabled = false;
+	
+	/**
+ 	* One of the absolute best parts of Enano :-P
+ 	* @var string
+ 	*/
+	
+	var $fading_button = '';
+	
+	function __construct()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $cache;
+		
+		$this->tpl_bool    = Array();
+		$this->tpl_strings = Array();
+		$this->sidebar_extra = '';
+		$this->toolbar_menu = '';
+		$this->additional_headers = '';
+		$this->plugin_blocks = Array();
+		$this->plugin_blocks_content = array();
+		$this->theme_loaded = false;
+		
+		$this->theme_list = Array();
+		$this->named_theme_list = Array();
+		
+		$this->vars_assign_history = array(
+				'strings' => array(),
+				'bool' => array()
+			);
+		
+		if ( defined('IN_ENANO_UPGRADE') )
+		{
+			return $this->construct_compat();
+		}
+		
+		if ( !$this->theme_list = $cache->fetch('themes') )
+		{
+			$q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style, group_policy, group_list FROM ' . table_prefix . 'themes;');
+			if ( !$q )
+				$db->_die('template.php selecting theme list');
+			
+			$i = 0;
+			while ( $row = $db->fetchrow() )
+			{
+				$this->theme_list[$i] = $row;
+				$i++;
+			}
+			unset($theme);
+			$this->theme_list = array_values($this->theme_list);
+			$cache->store('themes', $this->theme_list, -1);
+		}
+		
+		// Create associative array of themes
+		foreach ( $this->theme_list as $i => &$theme )
+			$this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
+		
+		unset($theme);
+		
+		$this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id'];
+		$this->named_theme_list[ $this->default_theme ]['css'] = $this->get_theme_css_files($this->default_theme);
+		// Come up with the default style. If the CSS file specified in default_style exists, we're good, just
+		// use that. Otherwise, use the first stylesheet that comes to mind.
+		$df_data =& $this->named_theme_list[ $this->default_theme ];
+		$this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
+	}
+	
+	/**
+ 	* Gets the list of available CSS files (styles) for the specified theme.
+ 	* @param string Theme ID
+ 	* @return array
+ 	*/
+	
+	function get_theme_css_files($theme_id)
+	{
+		$css = array();
+		$dir = ENANO_ROOT . "/themes/{$theme_id}/css";
+		if ( $dh = @opendir($dir) )
+		{
+			while ( ( $file = @readdir($dh) ) !== false )
+			{
+				if ( preg_match('/\.css$/', $file) )
+					$css[] = preg_replace('/\.css$/', '', $file);
+			}
+			closedir($dh);
+		}
+		// No CSS files? If so, nuke it.
+		if ( count($css) < 1 )
+		{
+			unset($this->theme_list[$theme_id]);
+		}
+		return $css;
+	}
+	
+	/**
+ 	* Failsafe constructor for upgrades.
+ 	*/
+	
+	function construct_compat()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$this->tpl_bool    = Array();
+		$this->tpl_strings = Array();
+		$this->sidebar_extra = '';
+		$this->toolbar_menu = '';
+		$this->additional_headers = '';
+		$this->plugin_blocks = Array();
+		$this->theme_loaded = false;
+		
+		$this->fading_button = '<div style="background-image: url('.scriptPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
+															<a style="background-image: none; padding-right: 0;" href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.scriptPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
+														</div>';
+		
+		$this->theme_list = Array();
+		$this->named_theme_list = Array();
+		
+		$q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style FROM ' . table_prefix . 'themes;');
+		if ( !$q )
+			$db->_die('template.php selecting theme list');
+		
+		$i = 0;
+		while ( $row = $db->fetchrow() )
+		{
+			$this->theme_list[$i] = $row;
+			$i++;
+		}
+		// List out all CSS files for this theme
+		foreach ( $this->theme_list as $i => &$theme )
+		{
+			$theme['css'] = array();
+			$dir = ENANO_ROOT . "/themes/{$theme['theme_id']}/css";
+			if ( $dh = @opendir($dir) )
+			{
+				while ( ( $file = @readdir($dh) ) !== false )
+				{
+					if ( preg_match('/\.css$/', $file) )
+						$theme['css'][] = preg_replace('/\.css$/', '', $file);
+				}
+				closedir($dh);
+			}
+			// No CSS files? If so, nuke it.
+			if ( count($theme['css']) < 1 )
+			{
+				unset($this->theme_list[$i]);
+			}
+		}
+		$this->theme_list = array_values($this->theme_list);
+		// Create associative array of themes
+		foreach ( $this->theme_list as $i => &$theme )
+			$this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
+		
+		$this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id'];
+		// Come up with the default style. If the CSS file specified in default_style exists, we're good, just
+		// use that. Otherwise, use the first stylesheet that comes to mind.
+		$df_data =& $this->named_theme_list[ $this->default_theme ];
+		$this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
+	}
+	
+	/**
+ 	* Systematically deletes themes from available list if they're blocked by theme security settings. Called when session->start() finishes.
+ 	*/
+	
+	function process_theme_acls()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		// generate the fading button - needs to be done after sessions are started
+		$admintitle = ( $session->user_level >= USER_LEVEL_ADMIN && is_object(@$lang) ) ? ' title="' . $lang->get('sidebar_btn_enanopowered_admin_tip') . '"' : '';
+		$this->fading_button = '<div style="background-image: url('.cdnPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
+															<a style="background-image: none; padding-right: 0;" href="http://enanocms.org/" onclick="window.open(this.href); return false;"' . $admintitle . '><img style="border-width: 0;" alt=" " src="'.cdnPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
+														</div>';
+		
+		// For each theme, check ACLs and delete from RAM if not authorized
+		foreach ( $this->theme_list as $i => $theme )
+		{
+			if ( !@$theme['group_list'] )
+				continue;
+			if ( $theme['theme_id'] === getConfig('theme_default') )
+				continue;
+			switch ( $theme['group_policy'] )
+			{
+				case 'allow_all':
+					// Unconditionally allowed
+					continue;
+					break;
+				case 'whitelist':
+					// If we're not on the list, off to the left please
+					$list = enano_json_decode($theme['group_list']);
+					$allowed = false;
+					foreach ( $list as $acl )
+					{
+						if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) )
+							// Invalid list entry, silently allow (maybe not a good idea but
+							// really, these things are checked before they're inserted)
+							continue 2;
+						$mode = $match[1];
+						$id = intval($match[2]);
+						switch ( $mode )
+						{
+							case 'u':
+								$allowed = ( $id == $session->user_id );
+								if ( $allowed )
+									break 2;
+								break;
+							case 'g':
+								$allowed = ( isset($session->groups[$id]) );
+								if ( $allowed )
+									break 2;
+						}
+					}
+					if ( !$allowed )
+					{
+						unset($this->theme_list[$i]);
+					}
+					break;
+				case 'blacklist':
+					// If we're ON the list, off to the left please
+					$list = enano_json_decode($theme['group_list']);
+					$allowed = true;
+					foreach ( $list as $acl )
+					{
+						if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) )
+							// Invalid list entry, silently allow (maybe not a good idea but
+							// really, these things are checked before they're inserted)
+							continue 2;
+						$mode = $match[1];
+						$id = intval($match[2]);
+						switch ( $mode )
+						{
+							case 'u':
+								$allowed = ( $id != $session->user_id );
+								if ( !$allowed )
+									break 2;
+								break;
+							case 'g':
+								$allowed = ( !isset($session->groups[$id]) );
+								if ( !$allowed )
+									break 2;
+						}
+					}
+					if ( !$allowed )
+					{
+						unset($this->theme_list[$i]);
+					}
+					break;
+			}
+		}
+		
+		$this->theme_list = array_values($this->theme_list);
+		
+		// Rebuild associative theme list
+		$this->named_theme_list = array();
+		foreach ( $this->theme_list as $i => &$theme )
+			$this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i];
+	}
+	
+	/**
+ 	* Register a new sidebar block.
+ 	* @param string Block title
+ 	* @param string Block HTML
+ 	* @param bool If true, the class of the block will be the same as the one used for blocks that are a list of links instead of HTML content. This can do some wacky things like make all your <a>s block level.
+ 	*/
+	
+	function sidebar_widget($t, $h, $use_normal_section = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( !$this->theme_loaded )
+		{
+			$this->load_theme($session->theme, $session->style);
+		}
+		if(!$this->sidebar_widgets)
+			$this->sidebar_widgets = '';
+		$tplvars = $this->extract_vars('elements.tpl');
+		
+		if ( $use_normal_section )
+		{
+			$parser = $this->makeParserText($tplvars['sidebar_section']);
+		}
+		else
+		{
+			$parser = $this->makeParserText($tplvars['sidebar_section_raw']);
+		}
+		
+		$parser->assign_vars(Array('TITLE' => '{TITLE}','CONTENT' => $h));
+		$this->plugin_blocks[$t] = $parser->run();
+		$this->plugin_blocks_content[$t] = $h;
+		$this->sidebar_widgets .= $parser->run();
+	}
+	function add_header($html)
+	{
+		/* debug only **
+		$bt = debug_backtrace();
+		$bt = $bt[1];
+		$this->additional_headers .= "\n    <!-- {$bt['file']}:{$bt['line']} -->\n    " . $html;
+		*/
+		$this->additional_headers .= "\n   " . $html;
+	}
+	function get_css($s = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$this->init_vars();
+		
+		$path = ( $s ) ? 'css/'.$s : 'css/'.$this->style.'.css';
+		
+		if ( !file_exists(ENANO_ROOT . '/themes/' . $this->theme . '/' . $path) )
+		{
+			echo "/* WARNING: Falling back to default file because file $path does not exist */\n";
+			$path = 'css/' . $this->style_list[0] . '.css';
+		}
+		
+		return $this->process_template($path);
+	}
+	function load_theme($name = false, $css = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$this->theme = ( $name ) ? $name : $session->theme;
+		$this->style = ( $css ) ? $css : $session->style;
+		if ( !$this->theme )
+		{
+			$this->theme = $this->theme_list[0]['theme_id'];
+			$this->style = preg_replace('/\.css$/', '', $this->theme_list[0]['default_style']);
+		}
+		// Make sure we're allowed to use this theme.
+		if ( (
+				// If it was removed, it's probably blocked by an ACL, or it was uninstalled
+				!isset($this->named_theme_list[$this->theme]) ||
+				// Check if the theme is disabled
+				( isset($this->named_theme_list[$this->theme]) && isset($this->named_theme_list[$this->theme]['enabled']) && $this->named_theme_list[$this->theme]['enabled'] == 0 ) )
+				// Above all, if it's a system theme, don't inhibit the loading process.
+				&& !in_array($this->theme, $this->system_themes)
+			)
+		{
+			// No, something is preventing it - fall back to site default
+			$this->theme = $this->default_theme;
+			
+			// Come up with the default style. If the CSS file specified in default_style exists, we're good, just
+			// use that. Otherwise, use the first stylesheet that comes to mind.
+			$df_data =& $this->named_theme_list[ $this->theme ];
+			$this->style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0];
+		}
+		// The list of styles for the currently selected theme
+		$this->style_list =& $this->named_theme_list[ $this->theme ]['css'];
+		$this->theme_loaded = true;
+	}
+	
+	/**
+ 	* Change the theme we're supposed to display.
+ 	* @param string Theme name
+ 	* @param string Style name; optional
+ 	*/
+	
+	function set_theme($theme = false, $style = false)
+	{
+		$this->theme_initted = false;
+		$this->load_theme($theme, $style);
+	}
+	
+	/**
+ 	* Change the page we're supposed to generate for
+ 	* @param mixed Page ID *or* PageProcessor. If a PageProcessor, pulls permission info and such from that; if not, starts a PageProcessor. YOU SHOULD USE A PageProcessor WHENEVER POSSIBLE! It improves efficiency.
+ 	* @param string Namespace; not required if including a PageProcessor.
+ 	*/
+	
+	function set_page($page_id_or_pp, $namespace = false)
+	{
+		if ( is_object($page_id_or_pp) && get_class($page_id_or_pp) === 'PageProcessor' )
+		{
+			$this->page_initted = false;
+			$page =& $page_id_or_pp;
+			$this->page = $page;
+			$this->page_id = $page->page_id;
+			$this->namespace = $page->namespace;
+		}
+		else if ( is_string($page_id_or_pp) )
+		{
+			if ( !is_string($namespace) )
+				return false;
+			
+			if ( $page_id_or_pp === $this->page_id && $namespace === $this->namespace )
+				return true;
+			
+			$this->page_initted = false;
+			$this->page = false;
+			$this->page_id = sanitize_page_id($page_id_or_pp);
+			$this->namespace = $namespace;
+		}
+		else
+		{
+			return false;
+		}
+		return true;
+	}
+	
+	/**
+ 	* Queue a Javascript file to be loaded with the page instead of on demand.
+ 	* @param mixed Javascript file string or array thereof, extensions are optional
+ 	* @example
+ 	<code>
+ 	$template->preload_js(array('jquery', 'jquery-ui'));
+ 	$template->preload_js('admin-menu.js');
+ 	</code>
+ 	* @return null
+ 	*/
+	
+	function preload_js($filemixed)
+	{
+		if ( is_string($filemixed) )
+			$files = array($filemixed);
+		else if ( is_array($filemixed) )
+			$files = $filemixed;
+		else
+			// :-/
+			return null;
+			
+		$this->js_preload = array_values(array_merge($this->js_preload, $files));
+	}
+	
+	/**
+ 	* Queue some HTML to be inserted after the Javascript runtime.
+ 	* @param string HTML glob
+ 	*/
+	
+	function add_header_js($html)
+	{
+		$this->js_append .= "$html\n    ";
+	}
+	
+	/**
+ 	* Global, only-called-once init. Goes to all themes.
+ 	*/
+	
+	function init_global_vars()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $email;
+		
+		$is_opera = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'Opera')) ? true : false;
+		$is_msie = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? true : false;
+		
+		$this->assign_bool(array(
+				'auth_admin' => $session->user_level >= USER_LEVEL_ADMIN ? true : false,
+				'user_logged_in' => $session->user_logged_in,
+				'opera' => $is_opera,
+				'msie' => $is_msie
+			));
+		
+		if ( $session->sid_super )
+		{
+			$ash = '&amp;auth=' . $session->sid_super;
+			$asq = "?auth=" . $session->sid_super;
+			$asa = "&auth=" . $session->sid_super;
+			$as2 = htmlspecialchars(urlSeparator) . 'auth='.$session->sid_super;
+		}
+		else
+		{
+			$asq = '';
+			$asa = '';
+			$as2 = '';
+			$ash = '';
+		}
+		
+		// Append the Enano version to URLs to break the cache on upgrades
+		$enano_version = enano_version();
+		
+		// Set up javascript includes
+		// these depend heavily on whether we have a CDN to work with or not
+		if ( getConfig('cdn_path') )
+		{
+			// we're on a CDN, point to static includes
+			// CLI javascript compression script: includes/clientside/jscompress.php
+			$js_head = '<script type="text/javascript" src="' . cdnPath . '/includes/clientside/static/enano-lib-basic.js?' . $enano_version . '"></script>';
+			
+			if ( !empty($this->js_preload) )
+			{
+				$loadlines = array();
+				
+				// make unique
+				foreach ( $this->js_preload as &$script )
+				{
+					$script = preg_replace('/\.js$/', '', $script) . '.js';
+				}
+				unset($script);
+				$this->js_preload = array_unique($this->js_preload);
+				
+				foreach ( $this->js_preload as $script )
+				{
+					$js_head .= "\n    <script type=\"text/javascript\" src=\"" . cdnPath . "/includes/clientside/static/$script?$enano_version\"></script>";
+					// special case for l10n: also load strings
+					if ( $script == 'l10n.js' )
+					{
+						global $lang;
+						$js_head .= "\n    <script type=\"text/javascript\" src=\"" . makeUrlNS("Special", "LangExportJSON/$lang->lang_id", "$enano_version") . "\"></script>";
+					}
+					$loadlines[] = "loaded_components['$script'] = true;";
+				}
+				
+				// tell the system that this stuff is already loaded
+				$loadlines = implode("\n      ", $loadlines);
+				$js_head .= "\n    <script type=\"text/javascript\">
+			var loaded_components = loaded_components || {};
+			$loadlines
+		</script>";
+			}
+			
+			$js_foot = <<<JSEOF
+		<script type="text/javascript">
+			// This initializes the Javascript runtime when the DOM is ready - not when the page is
+			// done loading, because enano-lib-basic still has to load some 15 other script files
+			// check for the init function - this is a KHTML fix
+			// This doesn't seem to work properly in IE in 1.1.x - there are some problems with
+			// tinyMCE and l10n.
+			if ( typeof ( enano_init ) == 'function' && !IE )
+			{
+				enano_init();
+				window.onload = function(e) {  };
+			}
+		</script>
+		$this->js_append
 JSEOF;
-    }
-    else
-    {
-      $cdnpath = cdnPath;
-      $js_head = '';
-      
-      // point to jsres compressor
-      $js_head .= <<<JSEOF
-      <!-- Only load a basic set of functions for now. Let the rest of the API load when the page is finished. -->
-      <script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?early&amp;$enano_version"></script>
+		}
+		else
+		{
+			$cdnpath = cdnPath;
+			$js_head = '';
+			
+			// point to jsres compressor
+			$js_head .= <<<JSEOF
+			<!-- Only load a basic set of functions for now. Let the rest of the API load when the page is finished. -->
+			<script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?early&amp;$enano_version"></script>
 JSEOF;
-      $js_foot = '';
-      
-      if ( !empty($this->js_preload) )
-      {
-        foreach ( $this->js_preload as &$script )
-        {
-          $script = preg_replace('/\.js$/', '', $script) . '.js';
-        }
-        $this->js_preload = array_unique($this->js_preload);
-        if ( in_array('l10n.js', $this->js_preload) )
-        {
-          // special case for l10n: also load strings
-          global $lang;
-          $js_foot .= "\n    <script type=\"text/javascript\" src=\"" . makeUrlNS("Special", "LangExportJSON/$lang->lang_id", $enano_version, true) . "\"></script>";
-        }
-        $scripts = implode(',', $this->js_preload);
-        $js_foot .= "\n    <script type=\"text/javascript\" src=\"" . cdnPath . "/includes/clientside/jsres.php?f=$scripts&amp;$enano_version\"></script>";
-        
-      }
-      
-      $js_foot .= <<<JSEOF
-    <!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests -->
-    <script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?$enano_version"></script>
-    <script type="text/javascript">//<![CDATA[
-      // This initializes the Javascript runtime when the DOM is ready - not when the page is
-      // done loading, because enano-lib-basic still has to load some 15 other script files
-      // check for the init function - this is a KHTML fix
-      // This doesn't seem to work properly in IE in 1.1.x - there are some problems with
-      // tinyMCE and l10n.
-      if ( typeof ( enano_init ) == 'function' && !IE )
-      {
-        enano_init();
-        window.onload = function(e) {  };
-      }
-    //]]></script>
-    $this->js_append
+			$js_foot = '';
+			
+			if ( !empty($this->js_preload) )
+			{
+				foreach ( $this->js_preload as &$script )
+				{
+					$script = preg_replace('/\.js$/', '', $script) . '.js';
+				}
+				$this->js_preload = array_unique($this->js_preload);
+				if ( in_array('l10n.js', $this->js_preload) )
+				{
+					// special case for l10n: also load strings
+					global $lang;
+					$js_foot .= "\n    <script type=\"text/javascript\" src=\"" . makeUrlNS("Special", "LangExportJSON/$lang->lang_id", $enano_version, true) . "\"></script>";
+				}
+				$scripts = implode(',', $this->js_preload);
+				$js_foot .= "\n    <script type=\"text/javascript\" src=\"" . cdnPath . "/includes/clientside/jsres.php?f=$scripts&amp;$enano_version\"></script>";
+				
+			}
+			
+			$js_foot .= <<<JSEOF
+		<!-- jsres.php is a wrapper script that compresses and caches single JS files to minimize requests -->
+		<script type="text/javascript" src="$cdnpath/includes/clientside/jsres.php?$enano_version"></script>
+		<script type="text/javascript">//<![CDATA[
+			// This initializes the Javascript runtime when the DOM is ready - not when the page is
+			// done loading, because enano-lib-basic still has to load some 15 other script files
+			// check for the init function - this is a KHTML fix
+			// This doesn't seem to work properly in IE in 1.1.x - there are some problems with
+			// tinyMCE and l10n.
+			if ( typeof ( enano_init ) == 'function' && !IE )
+			{
+				enano_init();
+				window.onload = function(e) {  };
+			}
+		//]]></script>
+		$this->js_append
 JSEOF;
-    }
-  
-    
-    $this->assign_bool(array(
-        'fixed_menus' => false,
-        'export' => false,
-        'right_sidebar' => true,
-        'enable_uploads' => ( getConfig('enable_uploads') == '1' && $session->get_permissions('upload_files') ) ? true : false,
-        'stupid_mode' => false,
-      ));
-    
-    // Add the e-mail address client code to the header
-    $this->add_header($email->jscode());
-    
-    // Assign our main variables
-    $this->assign_vars(array(
-        'SITE_NAME' => htmlspecialchars(getConfig('site_name')),
-        'USERNAME' => $session->username,
-        'SITE_DESC' => htmlspecialchars(getConfig('site_desc')),
-        'SCRIPTPATH' => scriptPath,
-        'CONTENTPATH' => contentPath,
-        'CDNPATH' => cdnPath,
-        'ADMIN_SID_QUES' => $asq,
-        'ADMIN_SID_AMP' => $asa,
-        'ADMIN_SID_AMP_HTML' => $ash,
-        'ADMIN_SID_AUTO' => $as2,
-        'ADMIN_SID_RAW' =>  ( is_string($session->sid_super) ? $session->sid_super : '' ),
-        'CSRF_TOKEN' => $session->csrf_token,
-        'COPYRIGHT' => RenderMan::parse_internal_links(getConfig('copyright_notice')),
-        'REQUEST_URI' => ( defined('ENANO_CLI') ? '' : $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] ),
-        'SEARCH_ACTION' => makeUrlNS('Special', 'Search'),
-        'INPUT_TITLE' => ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->get_pathskey($this->page_id, $this->namespace) ) . '" />' : ''),
-        'INPUT_AUTH' => ( $session->sid_super ? '<input type="hidden" name="auth"  value="' . $session->sid_super . '" />' : ''),
-        'MAIN_PAGE' => get_main_page(),
-        'UNREAD_PMS' => $session->unread_pms,
-        'JS_HEADER' => $js_head,
-        'JS_FOOTER' => $js_foot,
-        'URL_ABOUT_ENANO' => makeUrlNS('Special', 'About_Enano', '', true),
-        'ENANO_VERSION' => enano_version()
-      ), true);
-    
-    $tpl_strings = array();
-    foreach ( $paths->nslist as $ns_id => $ns_prefix )
-    {
-      $tpl_strings[ 'NS_' . strtoupper($ns_id) ] = $ns_prefix;
-    }
-    
-    $this->assign_vars($tpl_strings, true);
-  }
-  
-  /**
-   * Init theme vars, like sidebar, global JS, that kind of stuff.
-   */
-  
-  function init_theme_vars()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    // allows conditional testing of the theme ID (a bit crude, came from my NSIS days)
-    $this->assign_bool(array(
-        "theme_is_{$this->theme}" => true
-      ));
-    
-    $this->elements = $this->extract_vars('elements.tpl');
-    
-    // Generate the code for the Administration and Edit Sidebar buttons
-    // Once again, the new template parsing system can be used here
-    
-    $parser = $this->makeParserText($this->elements['sidebar_button']);
-    
-    $parser->assign_vars(Array(
-        'HREF'=>makeUrlNS('Special', 'Administration'),
-        'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { void(ajaxStartAdminLogin()); return false; }"',
-        'TEXT'=>$lang->get('sidebar_btn_administration'),
-      ));
-    
-    $admin_link = $parser->run();
-    
-    $parser->assign_vars(Array(
-        'HREF'=>makeUrlNS('Special', 'EditSidebar'),
-        'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { void(ajaxLoginNavTo(\'Special\', \'EditSidebar\', ' . USER_LEVEL_ADMIN . ')); return false; }"',
-        'TEXT'=>$lang->get('sidebar_btn_editsidebar'),
-      ));
-    
-    $sidebar_link = $parser->run();
-    
-    $this->assign_vars(array(
-        'ADMIN_LINK' => $admin_link,
-        'SIDEBAR_LINK' => $sidebar_link,
-        'THEME_ID' => $this->theme,
-        'STYLE_ID' => $this->style
-      ));
-    
-    // Add the site description sidebar block
-    $this->sidebar_widget($lang->get('sidebar_title_about'), '<p>' . htmlspecialchars(getConfig('site_desc')) . '</p>');
-    
-    $this->theme_initted = true;
-  }
-  
-  /**
-   * Init page vars, like the toolbar, local JS, etc.
-   */
-  
-  function init_page_vars()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if ( !$this->page )
-    {
-      $this->page = new PageProcessor($this->page_id, $this->namespace);
-    }
-    
-    $conds = $this->page->ns->get_conds();
-    
-    $this->assign_bool(array(
-        'in_admin' => ( ( $this->page_id == 'Administration' && $this->namespace == 'Special' ) || $this->namespace == 'Admin' ),
-        'auth_rename' => ( $conds['rename'] )
-      ));
-    
-    // Get the "article" button text (depends on namespace)
-    switch ( $this->namespace )
-    {
-      case "Article":
-      default:
-        $ns = $lang->get('onpage_lbl_page_article');
-        break;
-      case "Admin":
-        $ns = $lang->get('onpage_lbl_page_admin');
-        break;
-      case "System":
-        $ns = $lang->get('onpage_lbl_page_system');
-        break;
-      case "File":
-        $ns = $lang->get('onpage_lbl_page_file');
-        break;
-      case "Help":
-        $ns = $lang->get('onpage_lbl_page_help');
-        break;
-      case "User":
-        $ns = $lang->get('onpage_lbl_page_user');
-        break;
-      case "Special":
-        $ns = $lang->get('onpage_lbl_page_special');
-        break;
-      case "Template":
-        $ns = $lang->get('onpage_lbl_page_template');
-        break;
-      case "Project":
-        $ns = $lang->get('onpage_lbl_page_project');
-        break;
-      case "Category":
-        $ns = $lang->get('onpage_lbl_page_category');
-        break;
-      case "API":
-        $ns = $lang->get('onpage_lbl_page_external');
-        break;
-    }
-    $this->namespace_string = $ns;
-    unset($ns);
-    // compatibility
-    $local_namespace =& $this->namespace;
-    $local_page_id =& $this->page_id;
-    $code = $plugins->setHook('page_type_string_set');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    $ns =& $this->namespace_string;
-    
-    //
-    // PAGE TOOLBAR (on-page controls/actions)
-    //
-    
-    $local_page = $paths->get_pathskey($this->page_id, $this->namespace);
-    $local_cdata = $this->page->ns->get_cdata();
-    
-    // Initialize the toolbar
-    $tb = '';
-    $this->toolbar_menu = '';
-    
-    // Create "xx page" button
-    
-    $btn_selected = ( isset($this->elements['toolbar_button_selected'])) ? $this->elements['toolbar_button_selected'] : $this->elements['toolbar_button'];
-    $parser = $this->makeParserText($btn_selected);
-    
-    if ( $conds['article'] )
-    {
-      $parser->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxReset()); return false; }" title="' . $lang->get('onpage_tip_article') . '" accesskey="a"',
-          'PARENTFLAGS' => 'id="mdgToolbar_article"',
-          'HREF' => makeUrl($local_page, null, true),
-          'TEXT' => $this->namespace_string
-        ));
-      
-      $tb .= $parser->run();
-    }
-    
-    $button = $this->makeParserText($this->elements['toolbar_button']);
-    
-    // Page toolbar
-    // Comments button
-    if ( $conds['comments'] )
-    {
-      $cdata = $this->page->ns->get_cdata();
-      if ( isset($cdata['comments_approved']) )
-      {
-        $approval_counts = array(
-            COMMENT_APPROVED => $cdata['comments_approved'],
-            COMMENT_UNAPPROVED => $cdata['comments_unapproved'],
-            COMMENT_SPAM => $cdata['comments_spam']
-          );
-        $num_comments = $session->check_acl_scope('mod_comments', $this->namespace) && $this->page->perms->get_permissions('mod_comments')
-                          ? array_sum($approval_counts)
-                          : $approval_counts[COMMENT_APPROVED];
-      }
-      else
-      {
-        $e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$this->page_id.'\' AND namespace=\''.$this->namespace.'\';');
-        if ( !$e )
-        {
-          $db->_die();
-        }
-        $num_comments = $db->numrows();
-        $approval_counts = array(COMMENT_UNAPPROVED => 0, COMMENT_APPROVED => 0, COMMENT_SPAM => 0);
-        
-        while ( $r = $db->fetchrow() )
-        {  
-          $approval_counts[$r['approved']]++;
-        }
-      }
-      
-      $db->free_result();
-      // $n = ( $session->check_acl_scope('mod_comments', $this->namespace) && $perms->get_permissions('mod_comments') ) ? (string)$num_comments : (string)$na;
-      if ( $session->check_acl_scope('mod_comments', $this->namespace) && $this->page->perms->get_permissions('mod_comments') && ( $approval_counts[COMMENT_UNAPPROVED] + $approval_counts[COMMENT_SPAM] ) > 0 )
-      {
-        $subst = array(
-            'num_comments' => $num_comments,
-            'num_app' => $approval_counts[COMMENT_APPROVED],
-            'num_unapp' => $approval_counts[COMMENT_UNAPPROVED],
-            'num_spam' => $approval_counts[COMMENT_SPAM]
-          );
-        $btn_text = $lang->get('onpage_btn_discussion_unapp', $subst);
-      }
-      else
-      {
-        $subst = array(
-          'num_comments' => $num_comments
-        );
-        $btn_text = $lang->get('onpage_btn_discussion', $subst);
-      }
-      
-      $button->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxComments()); return false; }" title="' . $lang->get('onpage_tip_comments') . '" accesskey="c"',
-          'PARENTFLAGS' => 'id="mdgToolbar_discussion"',
-          'HREF' => makeUrl($local_page, 'do=comments', true),
-          'TEXT' => $btn_text,
-        ));
-      
-      $tb .= $button->run();
-    }
-    // Edit button
-    if( $conds['edit'] )
-    {
-      $button->assign_vars(array(
-        'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_edit') . '" accesskey="e"',
-        'PARENTFLAGS' => 'id="mdgToolbar_edit"',
-        'HREF' => makeUrl($local_page, 'do=edit', true),
-        'TEXT' => $lang->get('onpage_btn_edit')
-        ));
-      $tb .= $button->run();
-    // View source button
-    }
-    else if ( $conds['viewsource'] ) 
-    {
-      $button->assign_vars(array(
-        'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_viewsource') . '" accesskey="e"',
-        'PARENTFLAGS' => 'id="mdgToolbar_edit"',
-        'HREF' => makeUrl($local_page, 'do=viewsource', true),
-        'TEXT' => $lang->get('onpage_btn_viewsource')
-        ));
-      $tb .= $button->run();
-    }
-    // History button
-    if ( $conds['history'] )
-    {
-      $button->assign_vars(array(
-        'FLAGS'       => 'onclick="if ( !KILL_SWITCH ) { void(ajaxHistory()); return false; }" title="' . $lang->get('onpage_tip_history') . '" accesskey="h"',
-        'PARENTFLAGS' => 'id="mdgToolbar_history"',
-        'HREF'        => makeUrl($local_page, 'do=history', true),
-        'TEXT'        => $lang->get('onpage_btn_history')
-        ));
-      $tb .= $button->run();
-    }
-    
-    $menubtn = $this->makeParserText($this->elements['toolbar_menu_button']);
-    
-    // Additional actions menu
-    // Rename button
-    if ( $conds['rename'] )
-    {
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxRename()); return false; }" title="' . $lang->get('onpage_tip_rename') . '" accesskey="r"',
-          'HREF'  => makeUrl($local_page, 'do=rename', true),
-          'TEXT'  => $lang->get('onpage_btn_rename'),
-        ));
-      $this->toolbar_menu .= $menubtn->run();
-    }
-    
-    // Vote-to-delete button
-    if ( $conds['delvote'] )
-    {
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDelVote()); return false; }" title="' . $lang->get('onpage_tip_delvote') . '" accesskey="d"',
-          'HREF'  => makeUrl($local_page, 'do=delvote', true),
-          'TEXT'  => $lang->get('onpage_btn_votedelete'),
-        ));
-      $this->toolbar_menu .= $menubtn->run();
-    }
-    
-    // Clear-votes button
-    if ( $conds['resetvotes'] )
-    {
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxResetDelVotes()); return false; }" title="' . $lang->get('onpage_tip_resetvotes') . '" accesskey="y"',
-          'HREF'  => makeUrl($local_page, 'do=resetvotes', true),
-          'TEXT'  => $lang->get('onpage_btn_votedelete_reset'),
-        ));
-      $this->toolbar_menu .= $menubtn->run();
-    }
-    
-    // Printable page button
-    if ( $conds['printable'] )
-    {
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'title="' . $lang->get('onpage_tip_printable') . '"',
-          'HREF'  => makeUrl($local_page, 'printable=yes', true),
-          'TEXT'  => $lang->get('onpage_btn_printable'),
-        ));
-      $this->toolbar_menu .= $menubtn->run();
-    }
-    
-    // Protect button
-    if ( $conds['protect'] )
-    {
-      switch($this->page->ns->cdata['protected'])
-      {
-        case PROTECT_FULL: $protect_status = $lang->get('onpage_btn_protect_on'); break;
-        case PROTECT_SEMI: $protect_status = $lang->get('onpage_btn_protect_semi'); break;
-        case PROTECT_NONE: $protect_status = $lang->get('onpage_btn_protect_off'); break;
-      }
-      
-      $label = $this->makeParserText($this->elements['toolbar_label']);
-      $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_protect') . ' ' . "<b><span id=\"tb_ajax_protect_status\">$protect_status</span></b>"));
-      $t0 = $label->run();
-      
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'accesskey="p" onclick="ajaxProtect(' . $this->page->ns->cdata['protected'] . '); return false;" id="tb_ajax_protect_btn" title="' . $lang->get('onpage_tip_protect') . '"',
-          'HREF' => makeUrl($local_page, 'do=protect', true),
-          'TEXT' => $lang->get('onpage_btn_protect_change')
-        ));
-      $t1 = $menubtn->run();
-      
-      $this->toolbar_menu .= '        <table border="0" cellspacing="0" cellpadding="0">
-          <tr>
-            <td>'.$t0.'</td>
-            <td>'.$t1.'</td>
-          </tr>
-        </table>';
-    }
-    
-    // Wiki mode button
-    if ( $conds['setwikimode'] )
-    {
-      // label at start
-      $label = $this->makeParserText($this->elements['toolbar_label']);
-      $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_wikimode')));
-      $t0 = $label->run();
-      
-      // on button
-      $ctmp = '';
-      if ( $local_cdata['wiki_mode'] == 1 )
-      {
-        $ctmp = ' style="text-decoration: underline;"';
-      }
-      $menubtn->assign_vars(array(
-          'FLAGS' => $ctmp,
-          'HREF' => makeUrl($local_page, 'do=setwikimode&level=1', true),
-          'TEXT' => $lang->get('onpage_btn_wikimode_on')
-        ));
-      $t1 = $menubtn->run();
-      
-      // off button
-      $ctmp = '';
-      if ( $local_cdata['wiki_mode'] == 0 )
-      {
-        $ctmp=' style="text-decoration: underline;"';
-      }
-      $menubtn->assign_vars(array(
-          'FLAGS' => $ctmp,
-          'HREF' => makeUrl($local_page, 'do=setwikimode&level=0', true),
-          'TEXT' => $lang->get('onpage_btn_wikimode_off')
-        ));
-      $t2 = $menubtn->run();
-      
-      // global button
-      $ctmp = ''; 
-      if ( $local_cdata['wiki_mode'] == 2 )
-      {
-        $ctmp=' style="text-decoration: underline;"';
-      }
-      $menubtn->assign_vars(array(
-          'FLAGS' => $ctmp,
-          'HREF' => makeUrl($local_page, 'do=setwikimode&level=2', true),
-          'TEXT' => $lang->get('onpage_btn_wikimode_global')
-        ));
-      $t3 = $menubtn->run();
-      
-      // Tack it onto the list of buttons that are already there...
-      $this->toolbar_menu .= '        <table border="0" cellspacing="0" cellpadding="0">
-          <tr>
-            <td>'.$t0.'</td>
-            <td>'.$t1.'</td>
-            <td>'.$t2.'</td>
-            <td>'.$t3.'</td>
-          </tr>
-        </table>';
-    }
-    
-    // Clear logs button
-    if ( $conds['clearlogs'] )
-    {
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxClearLogs()); return false; }" title="' . $lang->get('onpage_tip_flushlogs') . '" accesskey="l"',
-          'HREF'  => makeUrl($local_page, 'do=flushlogs', true),
-          'TEXT'  => $lang->get('onpage_btn_clearlogs'),
-        ));
-      $this->toolbar_menu .= $menubtn->run();
-    }
-    
-    // Delete page button
-    if ( $conds['delete'] )
-    {
-      $s = $lang->get('onpage_btn_deletepage');
-      if ( $this->page->ns->cdata['delvotes'] == 1 )
-      {
-        $subst = array(
-          'num_votes' => $this->page->ns->cdata['delvotes'],
-          'plural' => ''
-          );
-        $s .= $lang->get('onpage_btn_deletepage_votes', $subst);
-      }
-      else if ( $this->page->ns->cdata['delvotes'] > 1 )
-      {
-        $subst = array(
-          'num_votes' => $this->page->ns->cdata['delvotes'],
-          'plural' => $lang->get('meta_plural')
-          );
-        $s .= $lang->get('onpage_btn_deletepage_votes', $subst);
-      }
-      
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDeletePage()); return false; }" title="' . $lang->get('onpage_tip_deletepage') . '" accesskey="k"',
-          'HREF'  => makeUrl($local_page, 'do=deletepage', true),
-          'TEXT'  => $s,
-        ));
-      $this->toolbar_menu .= $menubtn->run();
-      
-    }
-    
-    // Password-protect button
-    if ( $conds['password'] )
-    {
-      // label at start
-      $label = $this->makeParserText($this->elements['toolbar_label']);
-      $label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_password')));
-      $t0 = $label->run();
-      
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxSetPassword()); return false; }" title="' . $lang->get('onpage_tip_password') . '"',
-          'HREF'  => '#',
-          'TEXT'  => $lang->get('onpage_btn_password_set'),
-        ));
-      $t = $menubtn->run();
-      
-      $this->toolbar_menu .= '<table border="0" cellspacing="0" cellpadding="0"><tr><td>'.$t0.'</td><td><input type="password" id="mdgPassSetField" size="10" /></td><td>'.$t.'</td></tr></table>';
-    }
-    
-    // Manage ACLs button
-    if ( $conds['acledit'] )
-    {
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { var s = ajaxOpenACLManager(); console.debug(s); return false; }" title="' . $lang->get('onpage_tip_aclmanager') . '" accesskey="m"',
-          'HREF'  => makeUrl($local_page, 'do=aclmanager', true),
-          'TEXT'  => $lang->get('onpage_btn_acl'),
-        ));
-      $this->toolbar_menu .= $menubtn->run();
-    }
-    
-    // Administer page button
-    if ( $conds['adminpage'] )
-    {
-      $menubtn->assign_vars(array(
-          'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxAdminPage()); return false; }" title="' . $lang->get('onpage_tip_adminoptions') . '" accesskey="g"',
-          'HREF'  => makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'PageManager', true),
-          'TEXT'  => $lang->get('onpage_btn_admin'),
-        ));
-      $this->toolbar_menu .= $menubtn->run();
-    }
-    
-    if ( strlen($this->toolbar_menu) > 0 )
-    {
-      $button->assign_vars(array(
-        'FLAGS'       => 'id="mdgToolbar_moreoptions" onclick="if ( !KILL_SWITCH ) { return false; }" title="' . $lang->get('onpage_tip_moreoptions') . '"',
-        'PARENTFLAGS' => '',
-        'HREF'        => makeUrl($local_page, 'do=moreoptions', true),
-        'TEXT'        => $lang->get('onpage_btn_moreoptions')
-        ));
-      $tb .= $button->run();
-    }
-    
-    // Generate the code for the Log in, Log out, Change theme, Administration, and Edit Sidebar buttons
-    // Once again, the new template parsing system can be used here
-    
-    $parser = $this->makeParserText($this->elements['sidebar_button']);
-    
-    $parser->assign_vars(Array(
-        'HREF'=>makeUrlNS('Special', "Logout/{$session->csrf_token}/{$local_page}"),
-        'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { mb_logout(); return false; }"',
-        'TEXT'=>$lang->get('sidebar_btn_logout'),
-      ));
-    
-    $logout_link = $parser->run();
-    
-    $parser->assign_vars(Array(
-        'HREF'=>makeUrlNS('Special', 'Login/' . $local_page),
-        'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { ajaxStartLogin(); return false; }"' . ( $local_page_id == 'Login' && $local_namespace == 'Special' ? ' class="currentpage"' : '' ),
-        'TEXT'=>$lang->get('sidebar_btn_login'),
-      ));
-    
-    $login_link = $parser->run();
-    
-    $parser->assign_vars(Array(
-        'HREF'=>makeUrlNS('Special', 'ChangeStyle/'.$local_page),
-        'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { ajaxChangeStyle(); return false; }"' . ( $local_page_id == 'ChangeStyle' && $local_namespace == 'Special' ? ' class="currentpage"' : '' ),
-        'TEXT'=>$lang->get('sidebar_btn_changestyle'),
-      ));
-    
-    $theme_link = $parser->run();
-    
-    // Run hooks
-    $code = $plugins->setHook('tpl_compile_toolbar');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    //
-    // ASSIGN VARIABLES
-    //
-    
-    $this->assign_vars(array(
-        'PAGE_NAME' => htmlspecialchars($this->page->ns->cdata['name']),
-        'PAGE_URLNAME' => $paths->nslist[$this->namespace] . sanitize_page_id($this->page_id),
-        'TOOLBAR' => $tb,
-        'TOOLBAR_EXTRAS' => $this->toolbar_menu,
-        'STYLE_LINK' => makeUrlNS('Special', 'CSS', null, true), //contentPath.$paths->nslist['Special'].'CSS' . $p,
-        'LOGIN_LINK' => $login_link,
-        'LOGOUT_LINK' => $logout_link,
-        'THEME_LINK' => $theme_link
-      ), true);
-    $this->page_initted = true;
-  }
-  
-  /**
-   * Generates and assigns the Javascript system variables
-   */
-  
-  function generate_js_header()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $SID = ($session->sid_super) ? $session->sid_super : '';
-    
-    $local_page = $paths->get_pathskey($this->page_id, $this->namespace);
-    $local_fullpage = $paths->get_pathskey($this->page_id, $this->namespace) . substr($paths->fullpage, strlen($paths->page));
-    
-    $urlname_clean = str_replace('\'', '\\\'', str_replace('\\', '\\\\', dirtify_page_id($local_fullpage)));
-    $urlname_clean = strtr( $urlname_clean, array( '<' => '&lt;', '>' => '&gt;' ) );
-    
-    $urlname_jssafe = sanitize_page_id($local_fullpage);
-    $physical_urlname_jssafe = sanitize_page_id($paths->fullpage);
-    
-    $protected = is_object($this->page) ? $this->page->ns->cdata['really_protected'] : false;
-    
-    // Generate the dynamic javascript vars
-    // Sorry. I know. This block is a mess.
-    $js_dynamic = '    <script type="text/javascript">// <![CDATA[
-      // This section defines some basic and very important variables that are used later in the static Javascript library.
-      // SKIN DEVELOPERS: The template variable for this code block is {JS_DYNAMIC_VARS}. This MUST be inserted BEFORE the tag that links to the main Javascript lib.
-      var title = \''. $urlname_jssafe .'\';
-      var physical_title = \'' . $physical_urlname_jssafe . '\';
-      var on_main_page = ' . ( $local_page == get_main_page() ? 'true' : 'false' ) . ';
-      var main_page_members = \'' . addslashes(get_main_page(true)) . '\';
-      var page_exists = '. ( ( is_object($this->page) ? $this->page->ns->exists() : true ) ? 'true' : 'false' ) .';
-      var scriptPath = \'' . addslashes(scriptPath) . '\';
-      var contentPath = \'' . addslashes(contentPath) . '\';
-      var cdnPath = \'' . addslashes(cdnPath) . '\';
-      var ENANO_SID = \'' . $SID . '\';
-      var user_level = ' . $session->user_level . ';
-      var auth_level = ' . $session->auth_level . ';
-      var USER_LEVEL_GUEST = ' . USER_LEVEL_GUEST . ';
-      var USER_LEVEL_MEMBER = ' . USER_LEVEL_MEMBER . ';
-      var USER_LEVEL_CHPREF = ' . USER_LEVEL_CHPREF . ';
-      var USER_LEVEL_MOD = ' . USER_LEVEL_MOD . ';
-      var USER_LEVEL_ADMIN = ' . USER_LEVEL_ADMIN . ';
-      var disable_redirect = ' . ( isset($_GET['redirect']) && $_GET['redirect'] == 'no' ? 'true' : 'false' ) . ';
-      var pref_disable_js_fx = ' . ( @$session->user_extra['disable_js_fx'] == 1 ? 'true' : 'false' ) . ';
-      var csrf_token = "' . $session->csrf_token . '";
-      var prot = ' . ( ($protected) ? 'true' : 'false' ) .';
-      var ENANO_SPECIAL_CREATEPAGE = \''. makeUrl($paths->nslist['Special'].'CreatePage') .'\';
-      var ENANO_CREATEPAGE_PARAMS = \'_do=&pagename='. $this->page_id .'&namespace=' . $this->namespace . '\';
-      var ENANO_SPECIAL_CHANGESTYLE = \''. makeUrlNS('Special', 'ChangeStyle') .'\';
-      var namespace_list = [];
-      var msg_loading_component = \'' . addslashes($lang->get('ajax_msg_loading_component')) . '\';
-      var AES_BITS = '.AES_BITS.';
-      var AES_BLOCKSIZE = '.AES_BLOCKSIZE.';
-      var pagepass = \''. ( ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '' ) .'\';
-      var ENANO_LANG_ID = ' . $lang->lang_id . ';
-      var ENANO_PAGE_TYPE = "' . addslashes($this->namespace_string) . '";
-      var enano_version = "' . enano_version() . '";';
-    
-    foreach ( $paths->nslist as $k => $c )
-    {
-      $js_dynamic .= "namespace_list['{$k}'] = '" . addslashes($c) . "';";
-    }
-    $js_dynamic .= "\n    //]]>\n    </script>";
-    
-    $this->assign_vars(array(
-        'JS_DYNAMIC_VARS' => $js_dynamic,
-        'REPORT_URI' => makeUrl($local_fullpage, 'do=sql_report', true)
-      ), true);
-  }
-  
-  /**
-   * Fetches, parses, and assigns the sidebar.
-   */
-  
-  function assign_sidebar()
-  {
-    //
-    // COMPILE THE SIDEBAR
-    //
-    
-    // This is done after the big assign_vars() so that sidebar code has access to the newly assigned variables
-    
-    list($this->tpl_strings['SIDEBAR_LEFT'], $this->tpl_strings['SIDEBAR_RIGHT'], $min) = $this->fetch_sidebar();
-    $this->tpl_bool['sidebar_left']  = ( $this->tpl_strings['SIDEBAR_LEFT']  != $min) ? true : false;
-    $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != $min) ? true : false;
-    $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility
-  }
-  
-  /**
-   * Initializes all variables related to on-page content. This includes sidebars and what have you.
-   * @param object Optional PageProcessor object to use for passing metadata and permissions on. If omitted, uses information from $paths and $session.
-   * @param bool If true, re-inits even if already initted with this page_id and namespace
-   */
-  
-  function init_vars($page = false, $force_init = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $need_shared_init = ( !$this->theme_initted || !$this->page_initted );
-    
-    if ( $need_shared_init )
-    {
-      if ( !$this->theme || !$this->style )
-      {
-        $this->load_theme();
-      }
-      $code = $plugins->setHook('compile_template', true);
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-    }
-    
-    if ( !$this->theme_loaded )
-      $this->load_theme();
-    
-    require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg");
-    
-    if ( !$this->page_id || !$this->namespace )
-    {
-      $this->page_id = $paths->page_id;
-      $this->namespace = $paths->namespace;
-    }
-    
-    profiler_log('template: prepped for var set (loaded theme, ran compile_template hook)');
-    
-    $this->init_global_vars();
-    profiler_log('template: global vars set');
-    
-    if ( !$this->theme_initted )
-      $this->init_theme_vars();
-    
-    profiler_log('template: theme vars set');
-    
-    if ( !$this->page_initted && !empty($this->namespace) )
-    {
-      profiler_log('template: page vars set');
-      $this->init_page_vars();
-    }
-    else
-    {
-      profiler_message('template: skipped setting page vars');
-    }
-    
-    // Perform shared init (combine javascript, etc.)
-    if ( $need_shared_init )
-    {
-      $this->generate_js_header();
-      $this->assign_sidebar();
-      profiler_log('template: assigned sidebar and JS');
-    }
-    
-    // and finally one string value that needs to be symlinked...
-    if ( !isset($this->tpl_strings['ADDITIONAL_HEADERS']) )
-    {
-      $this->tpl_strings['ADDITIONAL_HEADERS'] =& $this->additional_headers;
-    }
-    
-    // done!
-    $code = $plugins->setHook('template_var_init_end');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-  }
-  
-  function header($simple = false) 
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    define('ENANO_HEADERS_SENT', true);
-    
-    echo $this->getHeader($simple);
-  }
-  
-  function footer($simple = false)
-  {
-    echo $this->getFooter($simple);
-  }
-  
-  function getHeader($simple = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if ( !$this->theme_loaded )
-    {
-      $this->load_theme($session->theme, $session->style);
-    }
-    
-    if ( !$this->page_initted || !$this->theme_initted )
-      $this->init_vars();
-    
-    // I feel awful doing this.
-    if ( preg_match('/^W3C_Validator/', @$_SERVER['HTTP_USER_AGENT']) )
-    {
-      header('Content-type: application/xhtml+xml');
-    }
-    
-    $header = '';
-    
-    if ( !$this->no_headers )
-    {
-      $header = ( $simple ) ?
-        $this->process_template('simple-header.tpl') :
-        $this->process_template('header.tpl');
-    }
-    if ( !$simple && $session->user_logged_in && $session->unread_pms > 0 )
-    {
-      $header .= $this->notify_unread_pms();
-    }
-    if ( !$simple && $session->sw_timed_out )
-    {
-      $login_link = makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . $session->user_level, true);
-      $header .= '<div class="usermessage">';
-      $header .= $lang->get('user_msg_elev_timed_out', array( 'login_link' => $login_link ));
-      $header .= '</div>';
-    }
-    if ( $this->site_disabled && $session->user_level >= USER_LEVEL_ADMIN && ( $paths->page != $paths->nslist['Special'] . 'Administration' ) )
-    {
-      $admin_link = makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'GeneralConfig', true);
-      $header .= '<div class="usermessage"><b>' . $lang->get('page_sitedisabled_admin_msg_title') . '</b><br />
-            ' . $lang->get('page_sitedisabled_admin_msg_body', array('admin_link' => $admin_link)) . '
-            </div>';
-    }
-    
-    return $header;
-  }
-  
-  function getFooter($simple = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    if ( !$this->no_headers )
-    {
-      
-      global $_starttime;
-      if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc'))
-      {
-        echo '<h3>' . $lang->get('page_heading_sql_list') . '</h3><pre style="margin-left: 1em">';
-        echo htmlspecialchars($db->sql_backtrace());
-        echo '</pre>';
-      }
-      
-      $t = ( $simple ) ? $this->process_template('simple-footer.tpl') : $this->process_template('footer.tpl');
-      
-      $f = microtime_float();
-      $f = $f - $_starttime;
-      $f = round($f, 2);
-      
-      $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f));
-      $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f));
-      $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => $db->num_queries)) . '</a>';
-      $dbg = $t_loc;
-      $dbg_long = $t_loc_long;
-      if ( $session->user_level >= USER_LEVEL_ADMIN || defined('ENANO_DEBUG') )
-      {
-        $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-        $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-      }
-      
-      $t = str_replace('[[Stats]]', $dbg, $t);
-      $t = str_replace('[[StatsLong]]', $dbg_long, $t);
-      $t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t);
-      $t = str_replace('[[GenTime]]', (string)$f, $t);
-      $t = str_replace('[[NumQueriesLoc]]', $q_loc, $t);
-      $t = str_replace('[[GenTimeLoc]]', $t_loc, $t);
-      $t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
-      $t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
-      
-      if ( defined('ENANO_PROFILE') )
-      {
-        $t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
-        // ob_end_clean();
-        // return profiler_make_html();
-      }
-      
-      return $t;
-    }
-    else
-    {
-      return '';
-    }
-  }
-  
-  /**
-   * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
-   * @param $vars array
-   * @param $from_internal bool Internal switch, just omit (@todo document)
-   */
-  
-  function assign_vars($vars, $from_internal = false)
-  {
-    foreach ( $vars as $key => $value )
-    {
-      $replace = true;
-      if ( isset($this->vars_assign_history['strings'][$key]) )
-      {
-        if ( $this->vars_assign_history['strings'][$key] == 'api' )
-        {
-          $replace = false;
-        }
-      }
-      if ( $replace )
-      {
-        $this->tpl_strings[$key] = $value;
-        $this->vars_assign_history['strings'][$key] = ( $from_internal ) ? 'internal' : 'api';
-      }
-    }
-  }
-  
-  /**
-   * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
-   * @param $vars array
-   * @param $from_internal bool Internal switch, just omit (@todo document)
-   */
-  
-  function assign_bool($vars, $from_internal = false)
-  {
-    foreach ( $vars as $key => $value )
-    {
-      $replace = true;
-      if ( isset($this->vars_assign_history['bool'][$key]) )
-      {
-        if ( $this->vars_assign_history['bool'][$key] == 'api' )
-        {
-          $replace = false;
-        }
-      }
-      if ( $replace )
-      {
-        $this->tpl_bool[$key] = $value;
-        $this->vars_assign_history['bool'][$key] = ( $from_internal ) ? 'internal' : 'api';
-      }
-    }
-  }
-  
-  #
-  # COMPILER
-  #
-  
-  /**
-   * Compiles and executes a template based on the current variables and booleans. Loads
-   * the theme and initializes variables if needed. This mostly just calls child functions.
-   * @param string File to process
-   * @return string
-   */
-  
-  function process_template($file)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    if ( !$this->theme_initted || !$this->page_initted )
-    {
-      $this->init_vars();
-    }
-    
-    $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $file) . '.php';
-    if ( file_exists($cache_file) )
-    {
-      // this is about the time of the breaking change to cache file format
-      if ( ($m = filemtime($cache_file)) > 1215038089 )
-      {
-        $result = @include($cache_file);
-        if ( isset($md5) )
-        {
-          if ( $m >= filemtime(ENANO_ROOT . "/themes/{$this->theme}/$file") )
-          {
-            $result = $this->compile_template_text_post($result);
-            return $result;
-          }
-        }
-      }
-    }
-    
-    $compiled = $this->compile_template($file);
-    $result = eval($compiled);
-    
-    return $result;
-  }
-  
-  /**
-   * Loads variables from the specified template file. Returns an associative array containing the variables.
-   * @param string Template file to process (elements.tpl)
-   * @return array
-   */
-  
-  function extract_vars($file)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Sometimes this function gets called before the theme is loaded
-    // This is a bad coding practice so this function will always be picky.
-    if ( !$this->theme )
-    {
-      die('$template->extract_vars(): theme not yet loaded, so we can\'t open template files yet...this is a bug and should be reported.<br /><br />Backtrace, most recent call first:<pre>'.enano_debug_print_backtrace(true).'</pre>');
-    }
-    
-    // Full pathname of template file
-    $tpl_file_fullpath = ( strstr($file, '/') ) ? $file : ENANO_ROOT . '/themes/' . $this->theme . '/' . $file;
-    
-    // Make sure the template even exists
-    if ( !is_file($tpl_file_fullpath) )
-    {
-      die_semicritical('Cannot find template file',
-                       '<p>The template parser was asked to load the file "' . htmlspecialchars($tpl_file_fullpath) . '", but that file couldn\'t be found in the directory for
-                           the current theme.</p>
-                        <p>Additional debugging information:<br />
-                           <b>Theme currently in use: </b>' . $this->theme . '<br />
-                           <b>Requested file: </b>' . $file . '
-                           </p>');
-    }
-    // Retrieve file contents
-    $text = file_get_contents($tpl_file_fullpath);
-    if ( !$text )
-    {
-      return false;
-    }
-    
-    // Get variables, regular expressions FTW
-    preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches);
-    
-    // Initialize return values
-    $tplvars = Array();
-    
-    // Loop through each match, setting $tplvars[ $first_subpattern ] to $second_subpattern
-    for ( $i = 0; $i < sizeof($matches[1]); $i++ )
-    {
-      $tplvars[ $matches[1][$i] ] = $matches[2][$i];
-    }
-    
-    // All done!
-    return $tplvars;
-  }
-  
-  /**
-   * Compiles a block of template code.
-   * @param string The text to process
-   * @return string
-   */
-  
-  function compile_tpl_code($text)
-  {
-    return template_compiler_core($text);  
-  }
-  
-  /**
-   * Compiles the contents of a given template file, possibly using a cached copy, and returns the compiled code.
-   * @param string Filename of template (header.tpl)
-   * @return string
-   */
-  
-  function compile_template($filename)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // Full path to template file
-    $tpl_file_fullpath = ENANO_ROOT . '/themes/' . $this->theme . '/' . $filename;
-    
-    // Make sure the file exists
-    if ( !is_file($tpl_file_fullpath) )
-    {
-      die_semicritical('Cannot find template file',
-                       '<p>The template parser was asked to load the file "' . htmlspecialchars($filename) . '", but that file couldn\'t be found in the directory for
-                           the current theme.</p>
-                        <p>Additional debugging information:<br />
-                           <b>Theme currently in use: </b>' . $this->theme . '<br />
-                           <b>Requested file: </b>' . $file . '
-                           </p>');
-    }
-    
-    // We won't use the cached copy here.
-    $text = file_get_contents($tpl_file_fullpath);
-    
-    // This will be used later when writing the cached file
-    $md5 = md5($text);
-    
-    // Preprocessing and checks complete - compile the code
-    $text = $this->compile_tpl_code($text);
-    
-    // Generate cache filename
-    $cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $filename) . '.php';
-    
-    // Perhaps caching is enabled and the admin has changed the template?
-    if ( is_writable( ENANO_ROOT . '/cache/' ) && getConfig('cache_thumbs') == '1' )
-    {
-      $h = fopen($cache_file, 'w');
-      if ( !$h )
-      {
-        // Couldn't open the file - silently ignore and return
-        return $text;
-      }
-      
-      // Final contents of cache file
-      $file_contents = <<<EOF
+		}
+	
+		
+		$this->assign_bool(array(
+				'fixed_menus' => false,
+				'export' => false,
+				'right_sidebar' => true,
+				'enable_uploads' => ( getConfig('enable_uploads') == '1' && $session->get_permissions('upload_files') ) ? true : false,
+				'stupid_mode' => false,
+			));
+		
+		// Add the e-mail address client code to the header
+		$this->add_header($email->jscode());
+		
+		// Assign our main variables
+		$this->assign_vars(array(
+				'SITE_NAME' => htmlspecialchars(getConfig('site_name')),
+				'USERNAME' => $session->username,
+				'SITE_DESC' => htmlspecialchars(getConfig('site_desc')),
+				'SCRIPTPATH' => scriptPath,
+				'CONTENTPATH' => contentPath,
+				'CDNPATH' => cdnPath,
+				'ADMIN_SID_QUES' => $asq,
+				'ADMIN_SID_AMP' => $asa,
+				'ADMIN_SID_AMP_HTML' => $ash,
+				'ADMIN_SID_AUTO' => $as2,
+				'ADMIN_SID_RAW' =>  ( is_string($session->sid_super) ? $session->sid_super : '' ),
+				'CSRF_TOKEN' => $session->csrf_token,
+				'COPYRIGHT' => RenderMan::parse_internal_links(getConfig('copyright_notice')),
+				'REQUEST_URI' => ( defined('ENANO_CLI') ? '' : $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] ),
+				'SEARCH_ACTION' => makeUrlNS('Special', 'Search'),
+				'INPUT_TITLE' => ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->get_pathskey($this->page_id, $this->namespace) ) . '" />' : ''),
+				'INPUT_AUTH' => ( $session->sid_super ? '<input type="hidden" name="auth"  value="' . $session->sid_super . '" />' : ''),
+				'MAIN_PAGE' => get_main_page(),
+				'UNREAD_PMS' => $session->unread_pms,
+				'JS_HEADER' => $js_head,
+				'JS_FOOTER' => $js_foot,
+				'URL_ABOUT_ENANO' => makeUrlNS('Special', 'About_Enano', '', true),
+				'ENANO_VERSION' => enano_version()
+			), true);
+		
+		$tpl_strings = array();
+		foreach ( $paths->nslist as $ns_id => $ns_prefix )
+		{
+			$tpl_strings[ 'NS_' . strtoupper($ns_id) ] = $ns_prefix;
+		}
+		
+		$this->assign_vars($tpl_strings, true);
+	}
+	
+	/**
+ 	* Init theme vars, like sidebar, global JS, that kind of stuff.
+ 	*/
+	
+	function init_theme_vars()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		// allows conditional testing of the theme ID (a bit crude, came from my NSIS days)
+		$this->assign_bool(array(
+				"theme_is_{$this->theme}" => true
+			));
+		
+		$this->elements = $this->extract_vars('elements.tpl');
+		
+		// Generate the code for the Administration and Edit Sidebar buttons
+		// Once again, the new template parsing system can be used here
+		
+		$parser = $this->makeParserText($this->elements['sidebar_button']);
+		
+		$parser->assign_vars(Array(
+				'HREF'=>makeUrlNS('Special', 'Administration'),
+				'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { void(ajaxStartAdminLogin()); return false; }"',
+				'TEXT'=>$lang->get('sidebar_btn_administration'),
+			));
+		
+		$admin_link = $parser->run();
+		
+		$parser->assign_vars(Array(
+				'HREF'=>makeUrlNS('Special', 'EditSidebar'),
+				'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { void(ajaxLoginNavTo(\'Special\', \'EditSidebar\', ' . USER_LEVEL_ADMIN . ')); return false; }"',
+				'TEXT'=>$lang->get('sidebar_btn_editsidebar'),
+			));
+		
+		$sidebar_link = $parser->run();
+		
+		$this->assign_vars(array(
+				'ADMIN_LINK' => $admin_link,
+				'SIDEBAR_LINK' => $sidebar_link,
+				'THEME_ID' => $this->theme,
+				'STYLE_ID' => $this->style
+			));
+		
+		// Add the site description sidebar block
+		$this->sidebar_widget($lang->get('sidebar_title_about'), '<p>' . htmlspecialchars(getConfig('site_desc')) . '</p>');
+		
+		$this->theme_initted = true;
+	}
+	
+	/**
+ 	* Init page vars, like the toolbar, local JS, etc.
+ 	*/
+	
+	function init_page_vars()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if ( !$this->page )
+		{
+			$this->page = new PageProcessor($this->page_id, $this->namespace);
+		}
+		
+		$conds = $this->page->ns->get_conds();
+		
+		$this->assign_bool(array(
+				'in_admin' => ( ( $this->page_id == 'Administration' && $this->namespace == 'Special' ) || $this->namespace == 'Admin' ),
+				'auth_rename' => ( $conds['rename'] )
+			));
+		
+		// Get the "article" button text (depends on namespace)
+		switch ( $this->namespace )
+		{
+			case "Article":
+			default:
+				$ns = $lang->get('onpage_lbl_page_article');
+				break;
+			case "Admin":
+				$ns = $lang->get('onpage_lbl_page_admin');
+				break;
+			case "System":
+				$ns = $lang->get('onpage_lbl_page_system');
+				break;
+			case "File":
+				$ns = $lang->get('onpage_lbl_page_file');
+				break;
+			case "Help":
+				$ns = $lang->get('onpage_lbl_page_help');
+				break;
+			case "User":
+				$ns = $lang->get('onpage_lbl_page_user');
+				break;
+			case "Special":
+				$ns = $lang->get('onpage_lbl_page_special');
+				break;
+			case "Template":
+				$ns = $lang->get('onpage_lbl_page_template');
+				break;
+			case "Project":
+				$ns = $lang->get('onpage_lbl_page_project');
+				break;
+			case "Category":
+				$ns = $lang->get('onpage_lbl_page_category');
+				break;
+			case "API":
+				$ns = $lang->get('onpage_lbl_page_external');
+				break;
+		}
+		$this->namespace_string = $ns;
+		unset($ns);
+		// compatibility
+		$local_namespace =& $this->namespace;
+		$local_page_id =& $this->page_id;
+		$code = $plugins->setHook('page_type_string_set');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		$ns =& $this->namespace_string;
+		
+		//
+		// PAGE TOOLBAR (on-page controls/actions)
+		//
+		
+		$local_page = $paths->get_pathskey($this->page_id, $this->namespace);
+		$local_cdata = $this->page->ns->get_cdata();
+		
+		// Initialize the toolbar
+		$tb = '';
+		$this->toolbar_menu = '';
+		
+		// Create "xx page" button
+		
+		$btn_selected = ( isset($this->elements['toolbar_button_selected'])) ? $this->elements['toolbar_button_selected'] : $this->elements['toolbar_button'];
+		$parser = $this->makeParserText($btn_selected);
+		
+		if ( $conds['article'] )
+		{
+			$parser->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxReset()); return false; }" title="' . $lang->get('onpage_tip_article') . '" accesskey="a"',
+					'PARENTFLAGS' => 'id="mdgToolbar_article"',
+					'HREF' => makeUrl($local_page, null, true),
+					'TEXT' => $this->namespace_string
+				));
+			
+			$tb .= $parser->run();
+		}
+		
+		$button = $this->makeParserText($this->elements['toolbar_button']);
+		
+		// Page toolbar
+		// Comments button
+		if ( $conds['comments'] )
+		{
+			$cdata = $this->page->ns->get_cdata();
+			if ( isset($cdata['comments_approved']) )
+			{
+				$approval_counts = array(
+						COMMENT_APPROVED => $cdata['comments_approved'],
+						COMMENT_UNAPPROVED => $cdata['comments_unapproved'],
+						COMMENT_SPAM => $cdata['comments_spam']
+					);
+				$num_comments = $session->check_acl_scope('mod_comments', $this->namespace) && $this->page->perms->get_permissions('mod_comments')
+													? array_sum($approval_counts)
+													: $approval_counts[COMMENT_APPROVED];
+			}
+			else
+			{
+				$e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$this->page_id.'\' AND namespace=\''.$this->namespace.'\';');
+				if ( !$e )
+				{
+					$db->_die();
+				}
+				$num_comments = $db->numrows();
+				$approval_counts = array(COMMENT_UNAPPROVED => 0, COMMENT_APPROVED => 0, COMMENT_SPAM => 0);
+				
+				while ( $r = $db->fetchrow() )
+				{  
+					$approval_counts[$r['approved']]++;
+				}
+			}
+			
+			$db->free_result();
+			// $n = ( $session->check_acl_scope('mod_comments', $this->namespace) && $perms->get_permissions('mod_comments') ) ? (string)$num_comments : (string)$na;
+			if ( $session->check_acl_scope('mod_comments', $this->namespace) && $this->page->perms->get_permissions('mod_comments') && ( $approval_counts[COMMENT_UNAPPROVED] + $approval_counts[COMMENT_SPAM] ) > 0 )
+			{
+				$subst = array(
+						'num_comments' => $num_comments,
+						'num_app' => $approval_counts[COMMENT_APPROVED],
+						'num_unapp' => $approval_counts[COMMENT_UNAPPROVED],
+						'num_spam' => $approval_counts[COMMENT_SPAM]
+					);
+				$btn_text = $lang->get('onpage_btn_discussion_unapp', $subst);
+			}
+			else
+			{
+				$subst = array(
+					'num_comments' => $num_comments
+				);
+				$btn_text = $lang->get('onpage_btn_discussion', $subst);
+			}
+			
+			$button->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxComments()); return false; }" title="' . $lang->get('onpage_tip_comments') . '" accesskey="c"',
+					'PARENTFLAGS' => 'id="mdgToolbar_discussion"',
+					'HREF' => makeUrl($local_page, 'do=comments', true),
+					'TEXT' => $btn_text,
+				));
+			
+			$tb .= $button->run();
+		}
+		// Edit button
+		if( $conds['edit'] )
+		{
+			$button->assign_vars(array(
+				'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_edit') . '" accesskey="e"',
+				'PARENTFLAGS' => 'id="mdgToolbar_edit"',
+				'HREF' => makeUrl($local_page, 'do=edit', true),
+				'TEXT' => $lang->get('onpage_btn_edit')
+				));
+			$tb .= $button->run();
+		// View source button
+		}
+		else if ( $conds['viewsource'] ) 
+		{
+			$button->assign_vars(array(
+				'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_viewsource') . '" accesskey="e"',
+				'PARENTFLAGS' => 'id="mdgToolbar_edit"',
+				'HREF' => makeUrl($local_page, 'do=viewsource', true),
+				'TEXT' => $lang->get('onpage_btn_viewsource')
+				));
+			$tb .= $button->run();
+		}
+		// History button
+		if ( $conds['history'] )
+		{
+			$button->assign_vars(array(
+				'FLAGS'       => 'onclick="if ( !KILL_SWITCH ) { void(ajaxHistory()); return false; }" title="' . $lang->get('onpage_tip_history') . '" accesskey="h"',
+				'PARENTFLAGS' => 'id="mdgToolbar_history"',
+				'HREF'        => makeUrl($local_page, 'do=history', true),
+				'TEXT'        => $lang->get('onpage_btn_history')
+				));
+			$tb .= $button->run();
+		}
+		
+		$menubtn = $this->makeParserText($this->elements['toolbar_menu_button']);
+		
+		// Additional actions menu
+		// Rename button
+		if ( $conds['rename'] )
+		{
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxRename()); return false; }" title="' . $lang->get('onpage_tip_rename') . '" accesskey="r"',
+					'HREF'  => makeUrl($local_page, 'do=rename', true),
+					'TEXT'  => $lang->get('onpage_btn_rename'),
+				));
+			$this->toolbar_menu .= $menubtn->run();
+		}
+		
+		// Vote-to-delete button
+		if ( $conds['delvote'] )
+		{
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDelVote()); return false; }" title="' . $lang->get('onpage_tip_delvote') . '" accesskey="d"',
+					'HREF'  => makeUrl($local_page, 'do=delvote', true),
+					'TEXT'  => $lang->get('onpage_btn_votedelete'),
+				));
+			$this->toolbar_menu .= $menubtn->run();
+		}
+		
+		// Clear-votes button
+		if ( $conds['resetvotes'] )
+		{
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxResetDelVotes()); return false; }" title="' . $lang->get('onpage_tip_resetvotes') . '" accesskey="y"',
+					'HREF'  => makeUrl($local_page, 'do=resetvotes', true),
+					'TEXT'  => $lang->get('onpage_btn_votedelete_reset'),
+				));
+			$this->toolbar_menu .= $menubtn->run();
+		}
+		
+		// Printable page button
+		if ( $conds['printable'] )
+		{
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'title="' . $lang->get('onpage_tip_printable') . '"',
+					'HREF'  => makeUrl($local_page, 'printable=yes', true),
+					'TEXT'  => $lang->get('onpage_btn_printable'),
+				));
+			$this->toolbar_menu .= $menubtn->run();
+		}
+		
+		// Protect button
+		if ( $conds['protect'] )
+		{
+			switch($this->page->ns->cdata['protected'])
+			{
+				case PROTECT_FULL: $protect_status = $lang->get('onpage_btn_protect_on'); break;
+				case PROTECT_SEMI: $protect_status = $lang->get('onpage_btn_protect_semi'); break;
+				case PROTECT_NONE: $protect_status = $lang->get('onpage_btn_protect_off'); break;
+			}
+			
+			$label = $this->makeParserText($this->elements['toolbar_label']);
+			$label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_protect') . ' ' . "<b><span id=\"tb_ajax_protect_status\">$protect_status</span></b>"));
+			$t0 = $label->run();
+			
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'accesskey="p" onclick="ajaxProtect(' . $this->page->ns->cdata['protected'] . '); return false;" id="tb_ajax_protect_btn" title="' . $lang->get('onpage_tip_protect') . '"',
+					'HREF' => makeUrl($local_page, 'do=protect', true),
+					'TEXT' => $lang->get('onpage_btn_protect_change')
+				));
+			$t1 = $menubtn->run();
+			
+			$this->toolbar_menu .= '        <table border="0" cellspacing="0" cellpadding="0">
+					<tr>
+						<td>'.$t0.'</td>
+						<td>'.$t1.'</td>
+					</tr>
+				</table>';
+		}
+		
+		// Wiki mode button
+		if ( $conds['setwikimode'] )
+		{
+			// label at start
+			$label = $this->makeParserText($this->elements['toolbar_label']);
+			$label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_wikimode')));
+			$t0 = $label->run();
+			
+			// on button
+			$ctmp = '';
+			if ( $local_cdata['wiki_mode'] == 1 )
+			{
+				$ctmp = ' style="text-decoration: underline;"';
+			}
+			$menubtn->assign_vars(array(
+					'FLAGS' => $ctmp,
+					'HREF' => makeUrl($local_page, 'do=setwikimode&level=1', true),
+					'TEXT' => $lang->get('onpage_btn_wikimode_on')
+				));
+			$t1 = $menubtn->run();
+			
+			// off button
+			$ctmp = '';
+			if ( $local_cdata['wiki_mode'] == 0 )
+			{
+				$ctmp=' style="text-decoration: underline;"';
+			}
+			$menubtn->assign_vars(array(
+					'FLAGS' => $ctmp,
+					'HREF' => makeUrl($local_page, 'do=setwikimode&level=0', true),
+					'TEXT' => $lang->get('onpage_btn_wikimode_off')
+				));
+			$t2 = $menubtn->run();
+			
+			// global button
+			$ctmp = ''; 
+			if ( $local_cdata['wiki_mode'] == 2 )
+			{
+				$ctmp=' style="text-decoration: underline;"';
+			}
+			$menubtn->assign_vars(array(
+					'FLAGS' => $ctmp,
+					'HREF' => makeUrl($local_page, 'do=setwikimode&level=2', true),
+					'TEXT' => $lang->get('onpage_btn_wikimode_global')
+				));
+			$t3 = $menubtn->run();
+			
+			// Tack it onto the list of buttons that are already there...
+			$this->toolbar_menu .= '        <table border="0" cellspacing="0" cellpadding="0">
+					<tr>
+						<td>'.$t0.'</td>
+						<td>'.$t1.'</td>
+						<td>'.$t2.'</td>
+						<td>'.$t3.'</td>
+					</tr>
+				</table>';
+		}
+		
+		// Clear logs button
+		if ( $conds['clearlogs'] )
+		{
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxClearLogs()); return false; }" title="' . $lang->get('onpage_tip_flushlogs') . '" accesskey="l"',
+					'HREF'  => makeUrl($local_page, 'do=flushlogs', true),
+					'TEXT'  => $lang->get('onpage_btn_clearlogs'),
+				));
+			$this->toolbar_menu .= $menubtn->run();
+		}
+		
+		// Delete page button
+		if ( $conds['delete'] )
+		{
+			$s = $lang->get('onpage_btn_deletepage');
+			if ( $this->page->ns->cdata['delvotes'] == 1 )
+			{
+				$subst = array(
+					'num_votes' => $this->page->ns->cdata['delvotes'],
+					'plural' => ''
+					);
+				$s .= $lang->get('onpage_btn_deletepage_votes', $subst);
+			}
+			else if ( $this->page->ns->cdata['delvotes'] > 1 )
+			{
+				$subst = array(
+					'num_votes' => $this->page->ns->cdata['delvotes'],
+					'plural' => $lang->get('meta_plural')
+					);
+				$s .= $lang->get('onpage_btn_deletepage_votes', $subst);
+			}
+			
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDeletePage()); return false; }" title="' . $lang->get('onpage_tip_deletepage') . '" accesskey="k"',
+					'HREF'  => makeUrl($local_page, 'do=deletepage', true),
+					'TEXT'  => $s,
+				));
+			$this->toolbar_menu .= $menubtn->run();
+			
+		}
+		
+		// Password-protect button
+		if ( $conds['password'] )
+		{
+			// label at start
+			$label = $this->makeParserText($this->elements['toolbar_label']);
+			$label->assign_vars(array('TEXT' => $lang->get('onpage_lbl_password')));
+			$t0 = $label->run();
+			
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxSetPassword()); return false; }" title="' . $lang->get('onpage_tip_password') . '"',
+					'HREF'  => '#',
+					'TEXT'  => $lang->get('onpage_btn_password_set'),
+				));
+			$t = $menubtn->run();
+			
+			$this->toolbar_menu .= '<table border="0" cellspacing="0" cellpadding="0"><tr><td>'.$t0.'</td><td><input type="password" id="mdgPassSetField" size="10" /></td><td>'.$t.'</td></tr></table>';
+		}
+		
+		// Manage ACLs button
+		if ( $conds['acledit'] )
+		{
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { var s = ajaxOpenACLManager(); console.debug(s); return false; }" title="' . $lang->get('onpage_tip_aclmanager') . '" accesskey="m"',
+					'HREF'  => makeUrl($local_page, 'do=aclmanager', true),
+					'TEXT'  => $lang->get('onpage_btn_acl'),
+				));
+			$this->toolbar_menu .= $menubtn->run();
+		}
+		
+		// Administer page button
+		if ( $conds['adminpage'] )
+		{
+			$menubtn->assign_vars(array(
+					'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxAdminPage()); return false; }" title="' . $lang->get('onpage_tip_adminoptions') . '" accesskey="g"',
+					'HREF'  => makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'PageManager', true),
+					'TEXT'  => $lang->get('onpage_btn_admin'),
+				));
+			$this->toolbar_menu .= $menubtn->run();
+		}
+		
+		if ( strlen($this->toolbar_menu) > 0 )
+		{
+			$button->assign_vars(array(
+				'FLAGS'       => 'id="mdgToolbar_moreoptions" onclick="if ( !KILL_SWITCH ) { return false; }" title="' . $lang->get('onpage_tip_moreoptions') . '"',
+				'PARENTFLAGS' => '',
+				'HREF'        => makeUrl($local_page, 'do=moreoptions', true),
+				'TEXT'        => $lang->get('onpage_btn_moreoptions')
+				));
+			$tb .= $button->run();
+		}
+		
+		// Generate the code for the Log in, Log out, Change theme, Administration, and Edit Sidebar buttons
+		// Once again, the new template parsing system can be used here
+		
+		$parser = $this->makeParserText($this->elements['sidebar_button']);
+		
+		$parser->assign_vars(Array(
+				'HREF'=>makeUrlNS('Special', "Logout/{$session->csrf_token}/{$local_page}"),
+				'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { mb_logout(); return false; }"',
+				'TEXT'=>$lang->get('sidebar_btn_logout'),
+			));
+		
+		$logout_link = $parser->run();
+		
+		$parser->assign_vars(Array(
+				'HREF'=>makeUrlNS('Special', 'Login/' . $local_page),
+				'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { ajaxStartLogin(); return false; }"' . ( $local_page_id == 'Login' && $local_namespace == 'Special' ? ' class="currentpage"' : '' ),
+				'TEXT'=>$lang->get('sidebar_btn_login'),
+			));
+		
+		$login_link = $parser->run();
+		
+		$parser->assign_vars(Array(
+				'HREF'=>makeUrlNS('Special', 'ChangeStyle/'.$local_page),
+				'FLAGS'=>'onclick="if ( !KILL_SWITCH ) { ajaxChangeStyle(); return false; }"' . ( $local_page_id == 'ChangeStyle' && $local_namespace == 'Special' ? ' class="currentpage"' : '' ),
+				'TEXT'=>$lang->get('sidebar_btn_changestyle'),
+			));
+		
+		$theme_link = $parser->run();
+		
+		// Run hooks
+		$code = $plugins->setHook('tpl_compile_toolbar');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		//
+		// ASSIGN VARIABLES
+		//
+		
+		$this->assign_vars(array(
+				'PAGE_NAME' => htmlspecialchars($this->page->ns->cdata['name']),
+				'PAGE_URLNAME' => $paths->nslist[$this->namespace] . sanitize_page_id($this->page_id),
+				'TOOLBAR' => $tb,
+				'TOOLBAR_EXTRAS' => $this->toolbar_menu,
+				'STYLE_LINK' => makeUrlNS('Special', 'CSS', null, true), //contentPath.$paths->nslist['Special'].'CSS' . $p,
+				'LOGIN_LINK' => $login_link,
+				'LOGOUT_LINK' => $logout_link,
+				'THEME_LINK' => $theme_link
+			), true);
+		$this->page_initted = true;
+	}
+	
+	/**
+ 	* Generates and assigns the Javascript system variables
+ 	*/
+	
+	function generate_js_header()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$SID = ($session->sid_super) ? $session->sid_super : '';
+		
+		$local_page = $paths->get_pathskey($this->page_id, $this->namespace);
+		$local_fullpage = $paths->get_pathskey($this->page_id, $this->namespace) . substr($paths->fullpage, strlen($paths->page));
+		
+		$urlname_clean = str_replace('\'', '\\\'', str_replace('\\', '\\\\', dirtify_page_id($local_fullpage)));
+		$urlname_clean = strtr( $urlname_clean, array( '<' => '&lt;', '>' => '&gt;' ) );
+		
+		$urlname_jssafe = sanitize_page_id($local_fullpage);
+		$physical_urlname_jssafe = sanitize_page_id($paths->fullpage);
+		
+		$protected = is_object($this->page) ? $this->page->ns->cdata['really_protected'] : false;
+		
+		// Generate the dynamic javascript vars
+		// Sorry. I know. This block is a mess.
+		$js_dynamic = '    <script type="text/javascript">// <![CDATA[
+			// This section defines some basic and very important variables that are used later in the static Javascript library.
+			// SKIN DEVELOPERS: The template variable for this code block is {JS_DYNAMIC_VARS}. This MUST be inserted BEFORE the tag that links to the main Javascript lib.
+			var title = \''. $urlname_jssafe .'\';
+			var physical_title = \'' . $physical_urlname_jssafe . '\';
+			var on_main_page = ' . ( $local_page == get_main_page() ? 'true' : 'false' ) . ';
+			var main_page_members = \'' . addslashes(get_main_page(true)) . '\';
+			var page_exists = '. ( ( is_object($this->page) ? $this->page->ns->exists() : true ) ? 'true' : 'false' ) .';
+			var scriptPath = \'' . addslashes(scriptPath) . '\';
+			var contentPath = \'' . addslashes(contentPath) . '\';
+			var cdnPath = \'' . addslashes(cdnPath) . '\';
+			var ENANO_SID = \'' . $SID . '\';
+			var user_level = ' . $session->user_level . ';
+			var auth_level = ' . $session->auth_level . ';
+			var USER_LEVEL_GUEST = ' . USER_LEVEL_GUEST . ';
+			var USER_LEVEL_MEMBER = ' . USER_LEVEL_MEMBER . ';
+			var USER_LEVEL_CHPREF = ' . USER_LEVEL_CHPREF . ';
+			var USER_LEVEL_MOD = ' . USER_LEVEL_MOD . ';
+			var USER_LEVEL_ADMIN = ' . USER_LEVEL_ADMIN . ';
+			var disable_redirect = ' . ( isset($_GET['redirect']) && $_GET['redirect'] == 'no' ? 'true' : 'false' ) . ';
+			var pref_disable_js_fx = ' . ( @$session->user_extra['disable_js_fx'] == 1 ? 'true' : 'false' ) . ';
+			var csrf_token = "' . $session->csrf_token . '";
+			var prot = ' . ( ($protected) ? 'true' : 'false' ) .';
+			var ENANO_SPECIAL_CREATEPAGE = \''. makeUrl($paths->nslist['Special'].'CreatePage') .'\';
+			var ENANO_CREATEPAGE_PARAMS = \'_do=&pagename='. $this->page_id .'&namespace=' . $this->namespace . '\';
+			var ENANO_SPECIAL_CHANGESTYLE = \''. makeUrlNS('Special', 'ChangeStyle') .'\';
+			var namespace_list = [];
+			var msg_loading_component = \'' . addslashes($lang->get('ajax_msg_loading_component')) . '\';
+			var AES_BITS = '.AES_BITS.';
+			var AES_BLOCKSIZE = '.AES_BLOCKSIZE.';
+			var pagepass = \''. ( ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '' ) .'\';
+			var ENANO_LANG_ID = ' . $lang->lang_id . ';
+			var ENANO_PAGE_TYPE = "' . addslashes($this->namespace_string) . '";
+			var enano_version = "' . enano_version() . '";';
+		
+		foreach ( $paths->nslist as $k => $c )
+		{
+			$js_dynamic .= "namespace_list['{$k}'] = '" . addslashes($c) . "';";
+		}
+		$js_dynamic .= "\n    //]]>\n    </script>";
+		
+		$this->assign_vars(array(
+				'JS_DYNAMIC_VARS' => $js_dynamic,
+				'REPORT_URI' => makeUrl($local_fullpage, 'do=sql_report', true)
+			), true);
+	}
+	
+	/**
+ 	* Fetches, parses, and assigns the sidebar.
+ 	*/
+	
+	function assign_sidebar()
+	{
+		//
+		// COMPILE THE SIDEBAR
+		//
+		
+		// This is done after the big assign_vars() so that sidebar code has access to the newly assigned variables
+		
+		list($this->tpl_strings['SIDEBAR_LEFT'], $this->tpl_strings['SIDEBAR_RIGHT'], $min) = $this->fetch_sidebar();
+		$this->tpl_bool['sidebar_left']  = ( $this->tpl_strings['SIDEBAR_LEFT']  != $min) ? true : false;
+		$this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != $min) ? true : false;
+		$this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility
+	}
+	
+	/**
+ 	* Initializes all variables related to on-page content. This includes sidebars and what have you.
+ 	* @param object Optional PageProcessor object to use for passing metadata and permissions on. If omitted, uses information from $paths and $session.
+ 	* @param bool If true, re-inits even if already initted with this page_id and namespace
+ 	*/
+	
+	function init_vars($page = false, $force_init = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$need_shared_init = ( !$this->theme_initted || !$this->page_initted );
+		
+		if ( $need_shared_init )
+		{
+			if ( !$this->theme || !$this->style )
+			{
+				$this->load_theme();
+			}
+			$code = $plugins->setHook('compile_template', true);
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+		}
+		
+		if ( !$this->theme_loaded )
+			$this->load_theme();
+		
+		require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg");
+		
+		if ( !$this->page_id || !$this->namespace )
+		{
+			$this->page_id = $paths->page_id;
+			$this->namespace = $paths->namespace;
+		}
+		
+		profiler_log('template: prepped for var set (loaded theme, ran compile_template hook)');
+		
+		$this->init_global_vars();
+		profiler_log('template: global vars set');
+		
+		if ( !$this->theme_initted )
+			$this->init_theme_vars();
+		
+		profiler_log('template: theme vars set');
+		
+		if ( !$this->page_initted && !empty($this->namespace) )
+		{
+			profiler_log('template: page vars set');
+			$this->init_page_vars();
+		}
+		else
+		{
+			profiler_message('template: skipped setting page vars');
+		}
+		
+		// Perform shared init (combine javascript, etc.)
+		if ( $need_shared_init )
+		{
+			$this->generate_js_header();
+			$this->assign_sidebar();
+			profiler_log('template: assigned sidebar and JS');
+		}
+		
+		// and finally one string value that needs to be symlinked...
+		if ( !isset($this->tpl_strings['ADDITIONAL_HEADERS']) )
+		{
+			$this->tpl_strings['ADDITIONAL_HEADERS'] =& $this->additional_headers;
+		}
+		
+		// done!
+		$code = $plugins->setHook('template_var_init_end');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+	}
+	
+	function header($simple = false) 
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		define('ENANO_HEADERS_SENT', true);
+		
+		echo $this->getHeader($simple);
+	}
+	
+	function footer($simple = false)
+	{
+		echo $this->getFooter($simple);
+	}
+	
+	function getHeader($simple = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if ( !$this->theme_loaded )
+		{
+			$this->load_theme($session->theme, $session->style);
+		}
+		
+		if ( !$this->page_initted || !$this->theme_initted )
+			$this->init_vars();
+		
+		// I feel awful doing this.
+		if ( preg_match('/^W3C_Validator/', @$_SERVER['HTTP_USER_AGENT']) )
+		{
+			header('Content-type: application/xhtml+xml');
+		}
+		
+		$header = '';
+		
+		if ( !$this->no_headers )
+		{
+			$header = ( $simple ) ?
+				$this->process_template('simple-header.tpl') :
+				$this->process_template('header.tpl');
+		}
+		if ( !$simple && $session->user_logged_in && $session->unread_pms > 0 )
+		{
+			$header .= $this->notify_unread_pms();
+		}
+		if ( !$simple && $session->sw_timed_out )
+		{
+			$login_link = makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . $session->user_level, true);
+			$header .= '<div class="usermessage">';
+			$header .= $lang->get('user_msg_elev_timed_out', array( 'login_link' => $login_link ));
+			$header .= '</div>';
+		}
+		if ( $this->site_disabled && $session->user_level >= USER_LEVEL_ADMIN && ( $paths->page != $paths->nslist['Special'] . 'Administration' ) )
+		{
+			$admin_link = makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'GeneralConfig', true);
+			$header .= '<div class="usermessage"><b>' . $lang->get('page_sitedisabled_admin_msg_title') . '</b><br />
+						' . $lang->get('page_sitedisabled_admin_msg_body', array('admin_link' => $admin_link)) . '
+						</div>';
+		}
+		
+		return $header;
+	}
+	
+	function getFooter($simple = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		if ( !$this->no_headers )
+		{
+			
+			global $_starttime;
+			if(isset($_GET['sqldbg']) && $session->get_permissions('mod_misc'))
+			{
+				echo '<h3>' . $lang->get('page_heading_sql_list') . '</h3><pre style="margin-left: 1em">';
+				echo htmlspecialchars($db->sql_backtrace());
+				echo '</pre>';
+			}
+			
+			$t = ( $simple ) ? $this->process_template('simple-footer.tpl') : $this->process_template('footer.tpl');
+			
+			$f = microtime_float();
+			$f = $f - $_starttime;
+			$f = round($f, 2);
+			
+			$t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f));
+			$t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f));
+			$q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => $db->num_queries)) . '</a>';
+			$dbg = $t_loc;
+			$dbg_long = $t_loc_long;
+			if ( $session->user_level >= USER_LEVEL_ADMIN || defined('ENANO_DEBUG') )
+			{
+				$dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+				$dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+			}
+			
+			$t = str_replace('[[Stats]]', $dbg, $t);
+			$t = str_replace('[[StatsLong]]', $dbg_long, $t);
+			$t = str_replace('[[NumQueries]]', (string)$db->num_queries, $t);
+			$t = str_replace('[[GenTime]]', (string)$f, $t);
+			$t = str_replace('[[NumQueriesLoc]]', $q_loc, $t);
+			$t = str_replace('[[GenTimeLoc]]', $t_loc, $t);
+			$t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
+			$t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
+			
+			if ( defined('ENANO_PROFILE') )
+			{
+				$t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
+				// ob_end_clean();
+				// return profiler_make_html();
+			}
+			
+			return $t;
+		}
+		else
+		{
+			return '';
+		}
+	}
+	
+	/**
+ 	* Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
+ 	* @param $vars array
+ 	* @param $from_internal bool Internal switch, just omit (@todo document)
+ 	*/
+	
+	function assign_vars($vars, $from_internal = false)
+	{
+		foreach ( $vars as $key => $value )
+		{
+			$replace = true;
+			if ( isset($this->vars_assign_history['strings'][$key]) )
+			{
+				if ( $this->vars_assign_history['strings'][$key] == 'api' )
+				{
+					$replace = false;
+				}
+			}
+			if ( $replace )
+			{
+				$this->tpl_strings[$key] = $value;
+				$this->vars_assign_history['strings'][$key] = ( $from_internal ) ? 'internal' : 'api';
+			}
+		}
+	}
+	
+	/**
+ 	* Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
+ 	* @param $vars array
+ 	* @param $from_internal bool Internal switch, just omit (@todo document)
+ 	*/
+	
+	function assign_bool($vars, $from_internal = false)
+	{
+		foreach ( $vars as $key => $value )
+		{
+			$replace = true;
+			if ( isset($this->vars_assign_history['bool'][$key]) )
+			{
+				if ( $this->vars_assign_history['bool'][$key] == 'api' )
+				{
+					$replace = false;
+				}
+			}
+			if ( $replace )
+			{
+				$this->tpl_bool[$key] = $value;
+				$this->vars_assign_history['bool'][$key] = ( $from_internal ) ? 'internal' : 'api';
+			}
+		}
+	}
+	
+	#
+	# COMPILER
+	#
+	
+	/**
+ 	* Compiles and executes a template based on the current variables and booleans. Loads
+ 	* the theme and initializes variables if needed. This mostly just calls child functions.
+ 	* @param string File to process
+ 	* @return string
+ 	*/
+	
+	function process_template($file)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		if ( !$this->theme_initted || !$this->page_initted )
+		{
+			$this->init_vars();
+		}
+		
+		$cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $file) . '.php';
+		if ( file_exists($cache_file) )
+		{
+			// this is about the time of the breaking change to cache file format
+			if ( ($m = filemtime($cache_file)) > 1215038089 )
+			{
+				$result = @include($cache_file);
+				if ( isset($md5) )
+				{
+					if ( $m >= filemtime(ENANO_ROOT . "/themes/{$this->theme}/$file") )
+					{
+						$result = $this->compile_template_text_post($result);
+						return $result;
+					}
+				}
+			}
+		}
+		
+		$compiled = $this->compile_template($file);
+		$result = eval($compiled);
+		
+		return $result;
+	}
+	
+	/**
+ 	* Loads variables from the specified template file. Returns an associative array containing the variables.
+ 	* @param string Template file to process (elements.tpl)
+ 	* @return array
+ 	*/
+	
+	function extract_vars($file)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Sometimes this function gets called before the theme is loaded
+		// This is a bad coding practice so this function will always be picky.
+		if ( !$this->theme )
+		{
+			die('$template->extract_vars(): theme not yet loaded, so we can\'t open template files yet...this is a bug and should be reported.<br /><br />Backtrace, most recent call first:<pre>'.enano_debug_print_backtrace(true).'</pre>');
+		}
+		
+		// Full pathname of template file
+		$tpl_file_fullpath = ( strstr($file, '/') ) ? $file : ENANO_ROOT . '/themes/' . $this->theme . '/' . $file;
+		
+		// Make sure the template even exists
+		if ( !is_file($tpl_file_fullpath) )
+		{
+			die_semicritical('Cannot find template file',
+ 											'<p>The template parser was asked to load the file "' . htmlspecialchars($tpl_file_fullpath) . '", but that file couldn\'t be found in the directory for
+ 													the current theme.</p>
+												<p>Additional debugging information:<br />
+ 													<b>Theme currently in use: </b>' . $this->theme . '<br />
+ 													<b>Requested file: </b>' . $file . '
+ 													</p>');
+		}
+		// Retrieve file contents
+		$text = file_get_contents($tpl_file_fullpath);
+		if ( !$text )
+		{
+			return false;
+		}
+		
+		// Get variables, regular expressions FTW
+		preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches);
+		
+		// Initialize return values
+		$tplvars = Array();
+		
+		// Loop through each match, setting $tplvars[ $first_subpattern ] to $second_subpattern
+		for ( $i = 0; $i < sizeof($matches[1]); $i++ )
+		{
+			$tplvars[ $matches[1][$i] ] = $matches[2][$i];
+		}
+		
+		// All done!
+		return $tplvars;
+	}
+	
+	/**
+ 	* Compiles a block of template code.
+ 	* @param string The text to process
+ 	* @return string
+ 	*/
+	
+	function compile_tpl_code($text)
+	{
+		return template_compiler_core($text);  
+	}
+	
+	/**
+ 	* Compiles the contents of a given template file, possibly using a cached copy, and returns the compiled code.
+ 	* @param string Filename of template (header.tpl)
+ 	* @return string
+ 	*/
+	
+	function compile_template($filename)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// Full path to template file
+		$tpl_file_fullpath = ENANO_ROOT . '/themes/' . $this->theme . '/' . $filename;
+		
+		// Make sure the file exists
+		if ( !is_file($tpl_file_fullpath) )
+		{
+			die_semicritical('Cannot find template file',
+ 											'<p>The template parser was asked to load the file "' . htmlspecialchars($filename) . '", but that file couldn\'t be found in the directory for
+ 													the current theme.</p>
+												<p>Additional debugging information:<br />
+ 													<b>Theme currently in use: </b>' . $this->theme . '<br />
+ 													<b>Requested file: </b>' . $file . '
+ 													</p>');
+		}
+		
+		// We won't use the cached copy here.
+		$text = file_get_contents($tpl_file_fullpath);
+		
+		// This will be used later when writing the cached file
+		$md5 = md5($text);
+		
+		// Preprocessing and checks complete - compile the code
+		$text = $this->compile_tpl_code($text);
+		
+		// Generate cache filename
+		$cache_file = ENANO_ROOT . '/cache/' . $this->theme . '-' . str_replace('/', '-', $filename) . '.php';
+		
+		// Perhaps caching is enabled and the admin has changed the template?
+		if ( is_writable( ENANO_ROOT . '/cache/' ) && getConfig('cache_thumbs') == '1' )
+		{
+			$h = fopen($cache_file, 'w');
+			if ( !$h )
+			{
+				// Couldn't open the file - silently ignore and return
+				return $text;
+			}
+			
+			// Final contents of cache file
+			$file_contents = <<<EOF
 <?php
 
 /*
@@ -1704,943 +1704,943 @@
 
 $text
 EOF;
-      // This is really just a normal PHP file that sets a variable or two and exits.
-      // $tpl_text actually will contain the compiled code
-      fwrite($h, $file_contents);
-      fclose($h);
-    }
-    
-    return $this->compile_template_text_post($text); //('<pre>'.htmlspecialchars($text).'</pre>');
-  }
-  
-  
-  /**
-   * Compiles (parses) some template code with the current master set of variables and booleans.
-   * @param string Text to process
-   * @return string
-   */
-  
-  function compile_template_text($text)
-  {
-    // this might do something else in the future, possibly cache large templates
-    return $this->compile_template_text_post($this->compile_tpl_code($text));
-  }
-  
-  /**
-   * For convenience - compiles AND parses some template code.
-   * @param string Text to process
-   * @return string
-   */
-  
-  function parse($text)
-  {
-    $text = $this->compile_template_text($text);
-    $text = $this->compile_template_text_post($text);
-    return eval($text);
-  }
-  
-  /**
-   * Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks.
-   * @param string Mostly-processed TPL code
-   * @param bool Post-eval switch. If true, does not escape code.
-   * @return string
-   */
-  
-  function compile_template_text_post($text, $post_eval = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    // Language strings
-    preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches);
-    foreach ( $matches[1] as $i => $string_id )
-    {
-      $string = $lang->get($string_id);
-      if ( !$post_eval )
-      {
-        $string = str_replace('\\', '\\\\', $string);
-        $string = str_replace('\'', '\\\'', $string);
-      }
-      $text = str_replace_once($matches[0][$i], $string, $text);
-    }
-    
-    // URLs
-    preg_match_all('/\{url:([A-z0-9]+):([^\}]+?)(?:;([^\s\}]+?))?(?:\|(escape))?\}/i', $text, $matches);
-    foreach ( $matches[1] as $i => $string_id )
-    {
-      $namespace =& $matches[1][$i];
-      $page_id =& $matches[2][$i];
-      $params =& $matches[3][$i];
-      $escape =& $matches[4][$i];
-      
-      if ( !$params )
-        $params = false;
-      $escape = !empty($escape);
-      
-      $result = makeUrlNS($namespace, sanitize_page_id($page_id), $params, $escape);
-      
-      if ( !$post_eval )
-      {
-        $result = str_replace('\\', '\\\\', $result);
-        $result = str_replace('\'', '\\\'', $result);
-      }
-      
-      $text = str_replace_once($matches[0][$i], $result, $text);
-    }
-    
-    $code = $plugins->setHook('compie_template_text_post');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    return $text;
-  }
-  
-  /**
-   * Returns the output of a theme hook
-   * @param string Hook name
-   * @return string
-   */
-  
-  function get_theme_hook($hook)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    ob_start();
-    $code = $plugins->setHook($hook);
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    $out = ob_get_contents();
-    ob_end_clean();
-    
-    return $out;
-  }
-  
-  // n00bish comments removed from here. 2008-03-13 @ 12:02AM when I had nothing else to do.
-  
-  /**
-   * Takes a blob of HTML with the specially formatted template-oriented wikitext and formats it. Does not use eval().
-   * This function butchers every coding standard in Enano and should eventually be rewritten. The fact is that the
-   * code _works_ and does a good job of checking for errors and cleanly complaining about them.
-   * @param string Text to process
-   * @param bool Ignored for backwards compatibility
-   * @param string File to get variables for sidebar data from
-   * @return string
-   */
-  
-  function tplWikiFormat($message, $filter_links = false, $filename = 'elements.tpl')
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $START = microtime_float();
-    
-    // localize the whole string first
-    preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $message, $matches);
-    foreach ( $matches[1] as $i => $string_id )
-    {
-      $string = $lang->get($string_id);
-      $string = str_replace('\\', '\\\\', $string);
-      $string = str_replace('\'', '\\\'', $string);
-      $message = str_replace_once($matches[0][$i], $string, $message);
-    }
-    
-    // first: the hackish optimization -
-    // if it's only a bunch of letters, numbers and spaces, just skip this sh*t.
-    
-    if ( preg_match('/^[\w\s\.]*$/i', $message) )
-    {
-      return $message;
-    }
-    
-    $filter_links = false;
-    $tplvars = $this->extract_vars($filename);
-    if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super;
-    else $as = '';
-    error_reporting(E_ALL);
-    $random_id = sha1(microtime().''); // A temp value
-    
-    /*
-     * PREPROCESSOR
-     */
-    
-    // Variables
-    
-    preg_match_all('#\$([A-Z_-]+)\$#', $message, $links);
-    $links = $links[1];
-    
-    for($i=0;$i<sizeof($links);$i++)
-    {
-      if ( isset($this->tpl_strings[$links[$i]]) )
-      {
-        $message = str_replace('$'.$links[$i].'$', $this->tpl_strings[$links[$i]], $message);
-      }
-    }
-    
-    // Conditionals
-    
-    $message = $this->twf_parse_conditionals($message);
-    
-    /*
-     * HTML RENDERER
-     */
-     
-    // Images
-    $message = RenderMan::process_image_tags($message, $taglist);
-    $message = RenderMan::process_imgtags_stage2($message, $taglist);
-    
-    // Internal links
-    $message = RenderMan::parse_internal_links($message, $tplvars['sidebar_button'], false, $this->page_id, $this->namespace);
-    
-    // External links
-    
-    $url_regexp = <<<EOF
+			// This is really just a normal PHP file that sets a variable or two and exits.
+			// $tpl_text actually will contain the compiled code
+			fwrite($h, $file_contents);
+			fclose($h);
+		}
+		
+		return $this->compile_template_text_post($text); //('<pre>'.htmlspecialchars($text).'</pre>');
+	}
+	
+	
+	/**
+ 	* Compiles (parses) some template code with the current master set of variables and booleans.
+ 	* @param string Text to process
+ 	* @return string
+ 	*/
+	
+	function compile_template_text($text)
+	{
+		// this might do something else in the future, possibly cache large templates
+		return $this->compile_template_text_post($this->compile_tpl_code($text));
+	}
+	
+	/**
+ 	* For convenience - compiles AND parses some template code.
+ 	* @param string Text to process
+ 	* @return string
+ 	*/
+	
+	function parse($text)
+	{
+		$text = $this->compile_template_text($text);
+		$text = $this->compile_template_text_post($text);
+		return eval($text);
+	}
+	
+	/**
+ 	* Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks.
+ 	* @param string Mostly-processed TPL code
+ 	* @param bool Post-eval switch. If true, does not escape code.
+ 	* @return string
+ 	*/
+	
+	function compile_template_text_post($text, $post_eval = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		// Language strings
+		preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches);
+		foreach ( $matches[1] as $i => $string_id )
+		{
+			$string = $lang->get($string_id);
+			if ( !$post_eval )
+			{
+				$string = str_replace('\\', '\\\\', $string);
+				$string = str_replace('\'', '\\\'', $string);
+			}
+			$text = str_replace_once($matches[0][$i], $string, $text);
+		}
+		
+		// URLs
+		preg_match_all('/\{url:([A-z0-9]+):([^\}]+?)(?:;([^\s\}]+?))?(?:\|(escape))?\}/i', $text, $matches);
+		foreach ( $matches[1] as $i => $string_id )
+		{
+			$namespace =& $matches[1][$i];
+			$page_id =& $matches[2][$i];
+			$params =& $matches[3][$i];
+			$escape =& $matches[4][$i];
+			
+			if ( !$params )
+				$params = false;
+			$escape = !empty($escape);
+			
+			$result = makeUrlNS($namespace, sanitize_page_id($page_id), $params, $escape);
+			
+			if ( !$post_eval )
+			{
+				$result = str_replace('\\', '\\\\', $result);
+				$result = str_replace('\'', '\\\'', $result);
+			}
+			
+			$text = str_replace_once($matches[0][$i], $result, $text);
+		}
+		
+		$code = $plugins->setHook('compie_template_text_post');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		return $text;
+	}
+	
+	/**
+ 	* Returns the output of a theme hook
+ 	* @param string Hook name
+ 	* @return string
+ 	*/
+	
+	function get_theme_hook($hook)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		ob_start();
+		$code = $plugins->setHook($hook);
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		$out = ob_get_contents();
+		ob_end_clean();
+		
+		return $out;
+	}
+	
+	// n00bish comments removed from here. 2008-03-13 @ 12:02AM when I had nothing else to do.
+	
+	/**
+ 	* Takes a blob of HTML with the specially formatted template-oriented wikitext and formats it. Does not use eval().
+ 	* This function butchers every coding standard in Enano and should eventually be rewritten. The fact is that the
+ 	* code _works_ and does a good job of checking for errors and cleanly complaining about them.
+ 	* @param string Text to process
+ 	* @param bool Ignored for backwards compatibility
+ 	* @param string File to get variables for sidebar data from
+ 	* @return string
+ 	*/
+	
+	function tplWikiFormat($message, $filter_links = false, $filename = 'elements.tpl')
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$START = microtime_float();
+		
+		// localize the whole string first
+		preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $message, $matches);
+		foreach ( $matches[1] as $i => $string_id )
+		{
+			$string = $lang->get($string_id);
+			$string = str_replace('\\', '\\\\', $string);
+			$string = str_replace('\'', '\\\'', $string);
+			$message = str_replace_once($matches[0][$i], $string, $message);
+		}
+		
+		// first: the hackish optimization -
+		// if it's only a bunch of letters, numbers and spaces, just skip this sh*t.
+		
+		if ( preg_match('/^[\w\s\.]*$/i', $message) )
+		{
+			return $message;
+		}
+		
+		$filter_links = false;
+		$tplvars = $this->extract_vars($filename);
+		if($session->sid_super) $as = htmlspecialchars(urlSeparator).'auth='.$session->sid_super;
+		else $as = '';
+		error_reporting(E_ALL);
+		$random_id = sha1(microtime().''); // A temp value
+		
+		/*
+ 		* PREPROCESSOR
+ 		*/
+		
+		// Variables
+		
+		preg_match_all('#\$([A-Z_-]+)\$#', $message, $links);
+		$links = $links[1];
+		
+		for($i=0;$i<sizeof($links);$i++)
+		{
+			if ( isset($this->tpl_strings[$links[$i]]) )
+			{
+				$message = str_replace('$'.$links[$i].'$', $this->tpl_strings[$links[$i]], $message);
+			}
+		}
+		
+		// Conditionals
+		
+		$message = $this->twf_parse_conditionals($message);
+		
+		/*
+ 		* HTML RENDERER
+ 		*/
+ 		
+		// Images
+		$message = RenderMan::process_image_tags($message, $taglist);
+		$message = RenderMan::process_imgtags_stage2($message, $taglist);
+		
+		// Internal links
+		$message = RenderMan::parse_internal_links($message, $tplvars['sidebar_button'], false, $this->page_id, $this->namespace);
+		
+		// External links
+		
+		$url_regexp = <<<EOF
 (
-  (?:https?|ftp|irc):\/\/                            # protocol
-  (?:[^@\s\]"\':]+@)?                                # username (FTP only but whatever)
-  (?:(?:(?:[a-z0-9-]+\.)*)[a-z0-9\[\]:]+)            # hostname
-  (?::[0-9]+)?                                       # port number
-  (?:\/[A-z0-9_%\|~`!\!@#\$\^&?=\*\(\):;\.,\/-]*)?   # path
+	(?:https?|ftp|irc):\/\/                            # protocol
+	(?:[^@\s\]"\':]+@)?                                # username (FTP only but whatever)
+	(?:(?:(?:[a-z0-9-]+\.)*)[a-z0-9\[\]:]+)            # hostname
+	(?::[0-9]+)?                                       # port number
+	(?:\/[A-z0-9_%\|~`!\!@#\$\^&?=\*\(\):;\.,\/-]*)?   # path
 )
 EOF;
 
-    $text_parser = $this->makeParserText($tplvars['sidebar_button']);
+		$text_parser = $this->makeParserText($tplvars['sidebar_button']);
 
-    preg_match_all('/\[' . $url_regexp . '[ ]([^\]]+)\]/isx', $message, $ext_link);
-    
-    for ( $i = 0; $i < count($ext_link[0]); $i++ )
-    {
-      $text_parser->assign_vars(Array(  
-          'HREF'  => $ext_link[1][$i],
-          'FLAGS' => '',
-          'TEXT'  => $ext_link[2][$i]
-        ));
-      $message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
-    }
-    
-    preg_match_all('/\[' . $url_regexp . '\]/is', $message, $ext_link);
-    
-    for ( $i = 0; $i < count($ext_link[0]); $i++ )
-    {
-      $text_parser->assign_vars(Array(  
-          'HREF'  => $ext_link[1][$i],
-          'FLAGS' => '',
-          'TEXT'  => htmlspecialchars($ext_link[1][$i])
-        ));
-      $message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
-    }
-    
-    $TIME = microtime_float() - $START;
-    
-    /*
-    if ( $TIME > 0.02 )
-    {
-      echo 'template: tplWikiFormat took a while for this one. string dump:<pre>';
-      echo htmlspecialchars($message);
-      echo '</pre>';
-    }
-    */
-    
-    return $message;
-  }
-  
-  /**
-   * Parses conditional {if} blocks in sidebars and other tplWikiFormatted things
-   * @param string A string potentially containing conditional blocks
-   * @return string Processed string
-   */
-  
-  function twf_parse_conditionals($message)
-  {
-    if ( !preg_match_all('/\{(!?)if ([a-z0-9_\(\)\|&! ]+)\}(.*?)(?:\{else\}(.*?))?\{\/if\}/is', $message, $matches) )
-    {
-      return $message;
-    }
-    foreach ( $matches[0] as $match_id => $full_block )
-    {
-      // 1 = "not" flag
-      // 2 = condition
-      // 3 = if true
-      // 4 = else
-      $condresult = $this->process_condition($matches[2][$match_id]);
-      if ( !empty($matches[1][$match_id]) )
-      {
-        if ( $condresult == 1 )
-          $condresult = 2;
-        else if ( $condresult == 2 )
-          $condresult = 1;
-      }
-      switch($condresult)
-      {
-        case 1:
-          // evaluated to false
-          $message = str_replace_once($full_block, $matches[4][$match_id], $message);
-          break;
-        case 2:
-          // evaluated to true
-          $message = str_replace_once($full_block, $matches[3][$match_id], $message);
-          break;
-        case 3:
-          $message = str_replace_once($full_block, "Syntax error: mismatched parentheses (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
-          break;
-        case 4:
-          $message = str_replace_once($full_block, "Syntax error: illegal character (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
-          break;
-        case 5:
-          $message = str_replace_once($full_block, "Syntax error: illegal sequence (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
-          break;
-      }
-    }
-    return $message;
-  }
-  
-  /**
-   * Inner-loop parser for a conditional block. Verifies a string condition to make sure it's syntactically correct, then returns what it evaluates to.
-   * Return values:
-   *   1 - string evaluates to true
-   *   2 - string evaluates to false
-   *   3 - Syntax error - mismatched parentheses
-   *   4 - Syntax error - unknown token
-   *   5 - Syntax error - invalid sequence
-   * @param string
-   * @return int
-   * 
-   */
-  
-  function process_condition($condition)
-  {
-    // make sure parentheses are matched
-    $parentheses = preg_replace('/[^\(\)]/', '', $condition);
-    if ( !empty($parentheses) )
-    {
-      $i = 0;
-      $parentheses = enano_str_split($parentheses);
-      foreach ( $parentheses as $chr )
-      {
-        $inc = ( $chr == '(' ) ? 1 : -1;
-        $i += $inc;
-      }
-      if ( $i != 0 )
-      {
-        // mismatched parentheses
-        return 3;
-      }
-    }
-    // sequencer check
-    // first, pad all sequences of characters with spaces
-    $seqcheck = preg_replace('/([a-z0-9_]+)/i', '\\1 ', $condition);
-    $seqcheck = preg_replace('/([&|()!])/i', '\\1 ', $seqcheck);
-    // now shrink all spaces to one space each
-    $seqcheck = preg_replace('/[ ]+/', ' ', $seqcheck);
-    
-    // explode it. the allowed sequences are:
-    //   - TOKEN_NOT + TOKEN_VARIABLE
-    //   - TOKEN_NOT + TOKEN_PARENTHLEFT
-    //   - TOKEN_BOOLOP + TOKEN_NOT
-    //   - TOKEN_PARENTHRIGHT + TOKEN_NOT
-    //   - TOKEN_VARIABLE + TOKEN_BOOLOP
-    //   - TOKEN_BOOLOP + TOKEN_PARENTHLEFT
-    //   - TOKEN_PARENTHLEFT + TOKEN_VARIABLE
-    //   - TOKEN_BOOLOP + TOKEN_VARIABLE
-    //   - TOKEN_VARIABLE + TOKEN_PARENTHRIGHT
-    //   - TOKEN_PARENTHRIGHT + TOKEN_BOOLOP
-    $seqcheck = explode(' ', trim($seqcheck));
-    $last_item = TOKEN_BOOLOP;
-    foreach ( $seqcheck as $i => $token )
-    {
-      // determine type
-      if ( $token == '(' )
-      {
-        $type = TOKEN_PARENTHLEFT;
-      }
-      else if ( $token == ')' )
-      {
-        $type = TOKEN_PARENTHRIGHT;
-      }
-      else if ( $token == '!' )
-      {
-        $type = TOKEN_NOT;
-      }
-      else if ( strtolower($token) == 'and' || strtolower($token) == 'or' || $token == '&&' || $token == '||' )
-      {
-        $type = TOKEN_BOOLOP;
-      }
-      else if ( preg_match('/^[a-z0-9_]+$/i', $token) )
-      {
-        $type = TOKEN_VARIABLE;
-        // at this point it's considered safe to wrap it
-        $seqcheck[$i] = "( isset(\$this->tpl_bool['$token']) && \$this->tpl_bool['$token'] )";
-      }
-      else
-      {
-        // syntax error - doesn't match known token types
-        return 4;
-      }
-      // inner sequence check
-      if (
-           ( $last_item == TOKEN_BOOLOP && $type == TOKEN_NOT ) ||
-           ( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_NOT ) ||
-           ( $last_item == TOKEN_NOT && $type == TOKEN_VARIABLE ) ||
-           ( $last_item == TOKEN_NOT && $type == TOKEN_PARENTHLEFT ) ||
-           ( $last_item == TOKEN_VARIABLE && $type == TOKEN_BOOLOP ) ||
-           ( $last_item == TOKEN_BOOLOP && $type == TOKEN_PARENTHLEFT ) ||
-           ( $last_item == TOKEN_PARENTHLEFT && $type == TOKEN_VARIABLE ) ||
-           ( $last_item == TOKEN_BOOLOP && $type == TOKEN_VARIABLE ) ||
-           ( $last_item == TOKEN_VARIABLE && $type == TOKEN_PARENTHRIGHT ) ||
-           ( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_BOOLOP )
-         )
-      {
-        // sequence is good, continue
-      }
-      else
-      {
-        // sequence is invalid, break out
-        return 5;
-      }
-      $last_item = $type;
-    }
-    // passed all checks
-    $seqcheck = implode(' ', $seqcheck);
-    $result = eval("return ( $seqcheck ) ? true : false;");
-    return ( $result ) ? 2 : 1;
-  }
-  
-  /**
-   * Print a text field that auto-completes a username entered into it.
-   * @param string $name - the name of the form field
-   * @return string
-   */
-   
-  function username_field($name, $value = false)
-  {
-    $randomid = md5( time() . microtime() . mt_rand() );
-    $text = '<input name="'.$name.'" class="autofill username" onkeyup="new AutofillUsername(this);" type="text" size="30" id="userfield_'.$randomid.'" autocomplete="off"';
-    if($value) $text .= ' value="'.$value.'"';
-    $text .= ' />';
-    return $text;
-  }
-  
-  /**
-   * Print a text field that auto-completes a page name entered into it.
-   * @param string $name - the name of the form field
-   * @return string
-   */
-   
-  function pagename_field($name, $value = false)
-  {
-    $randomid = md5( time() . microtime() . mt_rand() );
-    $text = '<input name="'.$name.'" class="autofill page" onkeyup="new AutofillPage(this);" type="text" size="30" id="pagefield_'.$randomid.'" autocomplete="off"';
-    if($value) $text .= ' value="'.$value.'"';
-    $text .= ' />';
-    return $text;
-  }
-  
-  /**
-   * Sends a textarea that can be converted to and from a TinyMCE widget on the fly.
-   * @param string The name of the form element
-   * @param string The initial content. Optional, defaults to blank
-   * @param int Rows in textarea
-   * @param int Columns in textarea
-   * @return string HTML and Javascript code.
-   */
-  
-  function tinymce_textarea($name, $content = '', $rows = 20, $cols = 60)
-  {
-    global $lang;
-    $randomid = md5(microtime() . mt_rand());
-    $html = '';
-    $html .= '<textarea name="' . $name . '" rows="'.$rows.'" cols="'.$cols.'" style="width: 100%;" id="toggleMCEroot_'.$randomid.'">' . $content . '</textarea>';
-    $html .= '<div style="float: right; display: table;" id="mceSwitchAgent_' . $randomid . '">' . $lang->get('etc_tinymce_btn_text') . '&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">' . $lang->get('etc_tinymce_btn_graphical') . '</a></div>';
-    $html .= '<script type="text/javascript">
-                // <![CDATA[
-                function toggleMCE_'.$randomid.'()
-                {
-                  var the_obj = document.getElementById(\'toggleMCEroot_' . $randomid . '\');
-                  var panel = document.getElementById(\'mceSwitchAgent_' . $randomid . '\');
-                  var text_editor = $lang.get("etc_tinymce_btn_text");
-                  var graphical_editor = $lang.get("etc_tinymce_btn_graphical");
-                  if ( the_obj.dnIsMCE == "yes" )
-                  {
-                    $dynano(the_obj).destroyMCE();
-                    panel.innerHTML = text_editor + \'&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">\' + graphical_editor + \'</a>\';
-                  }
-                  else
-                  {
-                    $dynano(the_obj).switchToMCE();
-                    panel.innerHTML = \'<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">\' + text_editor + \'</a>&nbsp;&nbsp;|&nbsp;&nbsp;\' + graphical_editor;
-                  }
-                }
-                // ]]>
-              </script>';
-    return $html;
-  }
-  
-  /**
-   * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;)
-   * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class.
-   * @param $filename the filename of the template to be parsed
-   * @return object
-   */
-   
-  function makeParser($filename)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $filename = ENANO_ROOT.'/themes/'.$template->theme.'/'.$filename;
-    if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist');
-    $code = file_get_contents($filename);
-    $parser = new templateIndividual($code);
-    return $parser;
-  }
-  
-  /**
-   * Same as $template->makeParser(), but takes a string instead of a filename.
-   * @param $text the text to parse
-   * @return object
-   */
-   
-  function makeParserText($code)
-  {
-    $parser = new templateIndividual($code);
-    return $parser;
-  }
-  
-  /**
-   * Fetch the HTML for a plugin-added sidebar block
-   * @param $name the plugin name
-   * @return string
-   */
-   
-  function fetch_block($id, $just_the_innards_maam = false)
-  {
-    if ( $just_the_innards_maam )
-    {
-      $source =& $this->plugin_blocks_content;
-    }
-    else
-    {
-      $source =& $this->plugin_blocks;
-    }
-    return isset($source[$id]) ? $source[$id] : false;
-  }
-  
-  /**
-   * Fetches the contents of both sidebars.
-   * @return array - key 0 is left, key 1 is right, key 2 is the HTML that makes up an empty sidebar
-   * @example list($left, $right) = $template->fetch_sidebar();
-   */
-  
-  function fetch_sidebar()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // first, check the cache
-    if ( $result = $this->fetch_cached_sidebar() )
-    {
-      return $result;
-    }
-    
-    require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg");
-    
-    profiler_log('Started sidebar parsing');
-    
-    // init our block contents
-    $left = '';
-    $right = '';
-    $min = '';
-    
-    // also might want the links block
-    if ( !$this->fetch_block('Links') )
-      $this->initLinksWidget();
-    
-    // templates to work with
-    $vars = $this->extract_vars('elements.tpl');
-    
-    // sidebar_top and sidebar_bottom are HTML that is prepended/appended to sidebar content. Themes can
-    // choose whether or not to display a sidebar depending on whether the sidebar is empty ( $left == $min )
-    // or not using the sidebar_left and sidebar_right template booleans (the oxygen theme does this).
-    if ( isset($vars['sidebar_top']) ) 
-    {
-      $top = $this->parse($vars['sidebar_top']);
-      $left  .= $top;
-      $right .= $top;
-      $min .= $top;
-    }
-    
-    // grab the blocks from the DB
-    $q = $db->sql_query('SELECT item_id,sidebar_id,block_name,block_type,block_content FROM '.table_prefix.'sidebar' . "\n"
-                           . '  WHERE item_enabled=1 ORDER BY sidebar_id ASC, item_order ASC;');
-    if ( !$q )
-      $db->_die('The sidebar text data could not be selected.');
-    
-    // explicitly specify $q in case a plugin or PHP block makes a query
-    while ( $row = $db->fetchrow($q) )
-    {
-      // should we skip this block?
-      if (
-          ( $row['item_id'] === 2 && !empty($theme['sb_hide_tools']) ) ||
-          ( $row['item_id'] === 3 && !empty($theme['sb_hide_user']) ) ||
-          ( $row['item_id'] === 4 && !empty($theme['sb_hide_search']) )
-        )
-        continue;
-      
-      // format the block
-      $block_content = $this->format_sidebar_block($row, $vars, $parser);
-      
-      // is there a {restrict} or {hideif} block?
-      if ( preg_match('/\{(restrict|hideif) ([a-z0-9_\(\)\|&! ]+)\}/', $block_content, $match) )
-      {
-        // we have one, check the condition
-        $type =& $match[1];
-        $cond =& $match[2];
-        $result = $this->process_condition($cond);
-        if ( ( $type == 'restrict' && $result == 1 ) || ( $type == 'hideif' && $result == 2 ) )
-        {
-          // throw out this block, it's hidden for whatever reason by the sidebar script
-          continue;
-        }
-        // didn't get a match, so hide the conditional logic
-        // FIXME: this needs to be verbose about syntax errors
-        $block_content = str_replace_once($match[0], '', $block_content);
-      }
-      
-      // if we made it here, this block definitely needs to be displayed. send it to the
-      // parser (set by format_sidebar_block) and decide where to append it (but not in that order ;))
-      $appender = false;
-      
-      if ( $row['sidebar_id'] == SIDEBAR_LEFT )
-      {
-        $appender =& $left;
-      }
-      else if ( $row['sidebar_id'] == SIDEBAR_RIGHT )
-      {
-        $appender =& $right;
-      }
-      else
-      {
-        // uhoh, block_id isn't valid. maybe a plugin wants to put this block somewhere else?
-        $code = $plugins->setHook('sidebar_block_id');
-        foreach ( $code as $cmd )
-        {
-          eval($cmd);
-        }
-        // if $appender wasn't set by a plugin, don't parse this block to save some CPU cycles
-        if ( !$appender )
-        {
-          continue;
-        }
-      }
-      
-      // assign variables to parser
-      $block_name = $this->tplWikiFormat($row['block_name']);
-      $parser->assign_vars(array(
-          // be nice and format the title (FIXME, does this use a lot of CPU time? still needs l10n in some cases though)
-          'TITLE' => $block_name,
-          'CONTENT' => $block_content
-        ));
-      $parsed = $parser->run();
-      
-      // plugins are parsed earlier due to the way disabled/missing plugins that add sidebar blocks are
-      // handled, so the {TITLE} var won't be replaced until now. this allows completely eliminating a
-      // block if it's not available
-      if ( $row['block_type'] == BLOCK_PLUGIN )
-      {
-        $parsed = str_replace('{TITLE}', $block_name, $parsed);
-      }
-      
-      // done parsing - append and continue
-      $appender .= $parsed;
-      
-      // we're done with this - unset it because it's a reference and we don't want it overwritten.
-      // also free the parser to get some RAM back
-      unset($appender, $parser);
-    }
-    
-    // lastly, append any footer HTML
-    if(isset($vars['sidebar_bottom'])) 
-    {
-      $bottom = $this->parse($vars['sidebar_bottom']);
-      $left  .= $bottom;
-      $right .= $bottom;
-      $min   .= $bottom;
-    }
-    
-    $return = array($left, $right, $min);
-    
-    // allow any plugins to append what they want to the return
-    $code = $plugins->setHook('sidebar_fetch_return');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    // cache the result if appropriate
-    $this->cache_compiled_sidebar($return);
-    
-    profiler_log('Finished sidebar parsing');
-    
-    return $return;
-  }
-  
-  /**
-   * Runs the appropriate formatting routine on a sidebar row.
-   * @param array Row in sidebar table
-   * @param array Template variable set from elements.tpl
-   * @param null Pass by reference, will be filled with the parser according to the block's type (sidebar_section or sidebar_section_raw)
-   * @return string HTML + directives like {restrict} or {hideif}
-   */
-  
-  function format_sidebar_block($row, $vars, &$parser)
-  {
-    // import globals in case a PHP block wants to call the Enano API
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    $parser = null;
-    
-    switch($row['block_type'])
-    {
-      case BLOCK_WIKIFORMAT:
-        $parser = $this->makeParserText($vars['sidebar_section_raw']);
-        $c = RenderMan::render($row['block_content']);
-        break;
-        
-      case BLOCK_TEMPLATEFORMAT:
-        $parser = $this->makeParserText($vars['sidebar_section']);
-        $c = $this->tplWikiFormat($row['block_content']);
-        break;
-        
-      case BLOCK_HTML:
-        $parser = $this->makeParserText($vars['sidebar_section_raw']);
-        $c = $row['block_content'];
-        break;
-        
-      case BLOCK_PHP:
-        // PHP blocks need to be sent directly to eval()
-        $parser = $this->makeParserText($vars['sidebar_section_raw']);
-        ob_start();
-        @eval($row['block_content']);
-        $c = ob_get_contents();
-        ob_end_clean();
-        break;
-        
-      case BLOCK_PLUGIN:
-        $parser = $this->makeParserText('{CONTENT}');
-        $c = '<!-- PLUGIN -->' . (gettype($this->fetch_block($row['block_content'])) == 'string') ?
-                $this->fetch_block($row['block_content']) :
-                // This used to say "can't find plugin block" but I think it's more friendly to just silently hide it.
-                '';
-        break;
-      default:
-        // unknown block type - can a plugin handle it?
-        $code = $plugins->setHook('format_sidebar_block');
-        foreach ( $code as $cmd )
-        {
-          eval($cmd);
-        }
-        if ( !isset($c) )
-        {
-          // default to wiki formatting (this was going to be straight HTML but this is done for backwards compatibility reasons)
-          $c = RenderMan::render($row['block_content']);
-        }
-        if ( !$parser )
-        {
-          // no parser defined, use the "raw" section by default (plugins are more likely to want raw content
-          // rather than a list of links, and they can set the parser to use sidebar_section if they want)
-          $parser = $this->makeParserText($vars['sidebar_section_raw']);
-        }
-        
-        break;
-    }
-    
-    return $c;
-  }
-  
-  /**
-   * Returns the list of things that should not be cached (sorry, I was listening to "The Thing That Should Not Be" by Metallica when I
-   * wrote this routine. You should get a copy of Master of Puppets, it's a damn good album.)
-   * @return array
-   */
-  
-  function get_cache_replacements()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    return array(
-          '$LOGIN_LINK$' => $this->tpl_strings['LOGIN_LINK'],
-          '$MAIN_PAGE$' => $this->tpl_strings['MAIN_PAGE'],
-          '$USERNAME$' => $session->username
-        );
-  }
-  
-  /**
-   * Attempts to load a cached compiled sidebar.
-   * @return array Same format as fetch_sidebar()
-   */
-  
-  function fetch_cached_sidebar()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $cache;
-    
-    $cached = false;
-    if ( ($result = $cache->fetch('anon_sidebar')) && !$session->user_logged_in )
-    {
-      if ( isset($result[$this->theme]) )
-      {
-        $cached = $result[$this->theme];
-      }
-    }
-    
-    // if we haven't been able to fetch yet, see if a plugin wants to give us something
-    if ( !$cached )
-    {
-      $code = $plugins->setHook('fetch_cached_sidebar');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-    } 
-    
-    if ( is_array($cached) )
-    {
-      // fetch certain variables that can't be stored in the cache and quickly substitute
-      $replace = $this->get_cache_replacements();
-      foreach ( $cached as &$val )
-      {
-        $val = strtr($val, $replace);
-      }
-      
-      // done processing, send it back
-      return $cached;
-    }
-    
-    return false;
-  }
-  
-  /**
-   * Caches a completely compiled sidebar, if appropriate
-   * @param array Effectively, return from fetch_sidebar()
-   */
-  
-  function cache_compiled_sidebar($sidebar)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $cache;
-    
-    // check if conditions are right
-    if ( !$session->user_logged_in && getConfig('cache_thumbs') === '1' )
-    {
-      // load any existing cache to make sure other themes' cached sidebars aren't discarded
-      $cached = ( $_ = $cache->fetch('anon_sidebar') ) ? $_ : array();
-      
-      // replace variables
-      $replace = array_flip($this->get_cache_replacements());
-      
-      foreach ( $sidebar as &$section )
-      {
-        $section = strtr($section, $replace);
-      }
-      
-      // compile
-      $cached[$this->theme] = $sidebar;
-      
-      // store
-      $cache->store('anon_sidebar', $cached, 15);
-    }
-  }
-  
-  function initLinksWidget()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    
-    // SourceForge/W3C buttons
-    $ob = Array();
-    if(getConfig('sflogo_enabled')=='1')
-    {
-      $sflogo_secure = ( isset($_SERVER['HTTPS']) ) ? 'https' : 'http';
-      $ob[] = '<a style="text-align: center;" href="http://sourceforge.net/" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border-width: 0px;" alt="SourceForge.net Logo" src="' . $sflogo_secure . '://sflogo.sourceforge.net/sflogo.php?group_id='.getConfig('sflogo_groupid').'&amp;type='.getConfig('sflogo_type').'" /></a>';
-    }
-    if(getConfig('w3c_v32')     =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 3.2"  src="http://www.w3.org/Icons/valid-html32" /></a>';
-    if(getConfig('w3c_v40')     =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 4.0"  src="http://www.w3.org/Icons/valid-html40" /></a>';
-    if(getConfig('w3c_v401')    =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 4.01" src="http://www.w3.org/Icons/valid-html401" /></a>';
-    if(getConfig('w3c_vxhtml10')=='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid XHTML 1.0" src="http://www.w3.org/Icons/valid-xhtml10" /></a>';
-    if(getConfig('w3c_vxhtml11')=='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid XHTML 1.1" src="http://www.w3.org/Icons/valid-xhtml11" /></a>';
-    if(getConfig('w3c_vcss')    =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid CSS"       src="http://www.w3.org/Icons/valid-css" /></a>';
-    if(getConfig('dbd_button')  =='1') $ob[] = '<a style="text-align: center;" href="http://www.defectivebydesign.org/join/button" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="DRM technology restricts what you can do with your computer" src="' . cdnPath . '/images/defectivebydesign.png" /><br /><small>Protect your freedom >></small></a>';
-    
-    $code = $plugins->setHook('links_widget');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    if(count($ob) > 0 || getConfig('powered_btn', '1') == '1') $sb_links = '<div style="text-align: center; padding: 5px 0;">'. ( ( getConfig('powered_btn', '1') == '1' ) ? $this->fading_button : '' ) . implode('<br />', $ob).'</div>';
-    else $sb_links = '';
-    
-    $this->sidebar_widget('Links', $sb_links);
-  }
-  
-  /**
-   * Builds a box showing unread private messages.
-   */
-  
-  function notify_unread_pms()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if ( ( $paths->page_id == 'PrivateMessages' || $paths->page_id == 'Preferences' ) && $paths->namespace == 'Special' )
-    {
-      return '';
-    }
-    $ob = '<div class="usermessage">'."\n";
-    $s = ( $session->unread_pms == 1 ) ? '' : 's';
-    $ob .= "  <b>You have $session->unread_pms <a href=" . '"' . makeUrlNS('Special', 'PrivateMessages' ) . '"' . ">unread private message$s</a>.</b><br />\n  Messages: ";
-    $q = $db->sql_query('SELECT message_id,message_from,subject,date FROM '.table_prefix.'privmsgs WHERE message_to=\'' . $session->username . '\' AND message_read=0 ORDER BY date DESC;');
-    if ( !$q )
-      $db->_die();
-    $messages = array();
-    while ( $row = $db->fetchrow() )
-    {
-      $messages[] = '<a href="' . makeUrlNS('Special', 'PrivateMessages/View/' . $row['message_id']) . '" title="Sent ' . enano_date(ED_DATE | ED_TIME, $row['date']) . ' by ' . $row['message_from'] . '">' . $row['subject'] . '</a>';
-    }
-    $ob .= implode(",\n    " , $messages)."\n";
-    $ob .= '</div>'."\n";
-    return $ob;
-  }
-  
-  /**
-   * Parse a system message.
-   * @param string message
-   * @return string
-   */
-  
-  function parse_system_message($text)
-  {
-    ob_start();
-    eval( '?>' . $text );
-    $result = ob_get_contents();
-    ob_end_clean();
-    return $this->parse($result);
-  }
-  
-  /**
-   * Return the wiki mode edit notice, rendered and addslashes()'ed.
-   * @return string
-   */
-  
-  function get_wiki_edit_notice()
-  {
-    global $cache;
-    
-    if ( getConfig('wiki_edit_notice', 0) != 1 )
-      return '';
-    
-    $notice = RenderMan::render(getConfig('wiki_edit_notice_text'));
-    return $notice;
-  }
-  
+		preg_match_all('/\[' . $url_regexp . '[ ]([^\]]+)\]/isx', $message, $ext_link);
+		
+		for ( $i = 0; $i < count($ext_link[0]); $i++ )
+		{
+			$text_parser->assign_vars(Array(  
+					'HREF'  => $ext_link[1][$i],
+					'FLAGS' => '',
+					'TEXT'  => $ext_link[2][$i]
+				));
+			$message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
+		}
+		
+		preg_match_all('/\[' . $url_regexp . '\]/is', $message, $ext_link);
+		
+		for ( $i = 0; $i < count($ext_link[0]); $i++ )
+		{
+			$text_parser->assign_vars(Array(  
+					'HREF'  => $ext_link[1][$i],
+					'FLAGS' => '',
+					'TEXT'  => htmlspecialchars($ext_link[1][$i])
+				));
+			$message = str_replace($ext_link[0][$i], $text_parser->run(), $message);
+		}
+		
+		$TIME = microtime_float() - $START;
+		
+		/*
+		if ( $TIME > 0.02 )
+		{
+			echo 'template: tplWikiFormat took a while for this one. string dump:<pre>';
+			echo htmlspecialchars($message);
+			echo '</pre>';
+		}
+		*/
+		
+		return $message;
+	}
+	
+	/**
+ 	* Parses conditional {if} blocks in sidebars and other tplWikiFormatted things
+ 	* @param string A string potentially containing conditional blocks
+ 	* @return string Processed string
+ 	*/
+	
+	function twf_parse_conditionals($message)
+	{
+		if ( !preg_match_all('/\{(!?)if ([a-z0-9_\(\)\|&! ]+)\}(.*?)(?:\{else\}(.*?))?\{\/if\}/is', $message, $matches) )
+		{
+			return $message;
+		}
+		foreach ( $matches[0] as $match_id => $full_block )
+		{
+			// 1 = "not" flag
+			// 2 = condition
+			// 3 = if true
+			// 4 = else
+			$condresult = $this->process_condition($matches[2][$match_id]);
+			if ( !empty($matches[1][$match_id]) )
+			{
+				if ( $condresult == 1 )
+					$condresult = 2;
+				else if ( $condresult == 2 )
+					$condresult = 1;
+			}
+			switch($condresult)
+			{
+				case 1:
+					// evaluated to false
+					$message = str_replace_once($full_block, $matches[4][$match_id], $message);
+					break;
+				case 2:
+					// evaluated to true
+					$message = str_replace_once($full_block, $matches[3][$match_id], $message);
+					break;
+				case 3:
+					$message = str_replace_once($full_block, "Syntax error: mismatched parentheses (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
+					break;
+				case 4:
+					$message = str_replace_once($full_block, "Syntax error: illegal character (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
+					break;
+				case 5:
+					$message = str_replace_once($full_block, "Syntax error: illegal sequence (" . htmlspecialchars($matches[2][$match_id]) . ")<br />\n", $message);
+					break;
+			}
+		}
+		return $message;
+	}
+	
+	/**
+ 	* Inner-loop parser for a conditional block. Verifies a string condition to make sure it's syntactically correct, then returns what it evaluates to.
+ 	* Return values:
+ 	*   1 - string evaluates to true
+ 	*   2 - string evaluates to false
+ 	*   3 - Syntax error - mismatched parentheses
+ 	*   4 - Syntax error - unknown token
+ 	*   5 - Syntax error - invalid sequence
+ 	* @param string
+ 	* @return int
+ 	* 
+ 	*/
+	
+	function process_condition($condition)
+	{
+		// make sure parentheses are matched
+		$parentheses = preg_replace('/[^\(\)]/', '', $condition);
+		if ( !empty($parentheses) )
+		{
+			$i = 0;
+			$parentheses = enano_str_split($parentheses);
+			foreach ( $parentheses as $chr )
+			{
+				$inc = ( $chr == '(' ) ? 1 : -1;
+				$i += $inc;
+			}
+			if ( $i != 0 )
+			{
+				// mismatched parentheses
+				return 3;
+			}
+		}
+		// sequencer check
+		// first, pad all sequences of characters with spaces
+		$seqcheck = preg_replace('/([a-z0-9_]+)/i', '\\1 ', $condition);
+		$seqcheck = preg_replace('/([&|()!])/i', '\\1 ', $seqcheck);
+		// now shrink all spaces to one space each
+		$seqcheck = preg_replace('/[ ]+/', ' ', $seqcheck);
+		
+		// explode it. the allowed sequences are:
+		//   - TOKEN_NOT + TOKEN_VARIABLE
+		//   - TOKEN_NOT + TOKEN_PARENTHLEFT
+		//   - TOKEN_BOOLOP + TOKEN_NOT
+		//   - TOKEN_PARENTHRIGHT + TOKEN_NOT
+		//   - TOKEN_VARIABLE + TOKEN_BOOLOP
+		//   - TOKEN_BOOLOP + TOKEN_PARENTHLEFT
+		//   - TOKEN_PARENTHLEFT + TOKEN_VARIABLE
+		//   - TOKEN_BOOLOP + TOKEN_VARIABLE
+		//   - TOKEN_VARIABLE + TOKEN_PARENTHRIGHT
+		//   - TOKEN_PARENTHRIGHT + TOKEN_BOOLOP
+		$seqcheck = explode(' ', trim($seqcheck));
+		$last_item = TOKEN_BOOLOP;
+		foreach ( $seqcheck as $i => $token )
+		{
+			// determine type
+			if ( $token == '(' )
+			{
+				$type = TOKEN_PARENTHLEFT;
+			}
+			else if ( $token == ')' )
+			{
+				$type = TOKEN_PARENTHRIGHT;
+			}
+			else if ( $token == '!' )
+			{
+				$type = TOKEN_NOT;
+			}
+			else if ( strtolower($token) == 'and' || strtolower($token) == 'or' || $token == '&&' || $token == '||' )
+			{
+				$type = TOKEN_BOOLOP;
+			}
+			else if ( preg_match('/^[a-z0-9_]+$/i', $token) )
+			{
+				$type = TOKEN_VARIABLE;
+				// at this point it's considered safe to wrap it
+				$seqcheck[$i] = "( isset(\$this->tpl_bool['$token']) && \$this->tpl_bool['$token'] )";
+			}
+			else
+			{
+				// syntax error - doesn't match known token types
+				return 4;
+			}
+			// inner sequence check
+			if (
+ 					( $last_item == TOKEN_BOOLOP && $type == TOKEN_NOT ) ||
+ 					( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_NOT ) ||
+ 					( $last_item == TOKEN_NOT && $type == TOKEN_VARIABLE ) ||
+ 					( $last_item == TOKEN_NOT && $type == TOKEN_PARENTHLEFT ) ||
+ 					( $last_item == TOKEN_VARIABLE && $type == TOKEN_BOOLOP ) ||
+ 					( $last_item == TOKEN_BOOLOP && $type == TOKEN_PARENTHLEFT ) ||
+ 					( $last_item == TOKEN_PARENTHLEFT && $type == TOKEN_VARIABLE ) ||
+ 					( $last_item == TOKEN_BOOLOP && $type == TOKEN_VARIABLE ) ||
+ 					( $last_item == TOKEN_VARIABLE && $type == TOKEN_PARENTHRIGHT ) ||
+ 					( $last_item == TOKEN_PARENTHRIGHT && $type == TOKEN_BOOLOP )
+ 				)
+			{
+				// sequence is good, continue
+			}
+			else
+			{
+				// sequence is invalid, break out
+				return 5;
+			}
+			$last_item = $type;
+		}
+		// passed all checks
+		$seqcheck = implode(' ', $seqcheck);
+		$result = eval("return ( $seqcheck ) ? true : false;");
+		return ( $result ) ? 2 : 1;
+	}
+	
+	/**
+ 	* Print a text field that auto-completes a username entered into it.
+ 	* @param string $name - the name of the form field
+ 	* @return string
+ 	*/
+ 	
+	function username_field($name, $value = false)
+	{
+		$randomid = md5( time() . microtime() . mt_rand() );
+		$text = '<input name="'.$name.'" class="autofill username" onkeyup="new AutofillUsername(this);" type="text" size="30" id="userfield_'.$randomid.'" autocomplete="off"';
+		if($value) $text .= ' value="'.$value.'"';
+		$text .= ' />';
+		return $text;
+	}
+	
+	/**
+ 	* Print a text field that auto-completes a page name entered into it.
+ 	* @param string $name - the name of the form field
+ 	* @return string
+ 	*/
+ 	
+	function pagename_field($name, $value = false)
+	{
+		$randomid = md5( time() . microtime() . mt_rand() );
+		$text = '<input name="'.$name.'" class="autofill page" onkeyup="new AutofillPage(this);" type="text" size="30" id="pagefield_'.$randomid.'" autocomplete="off"';
+		if($value) $text .= ' value="'.$value.'"';
+		$text .= ' />';
+		return $text;
+	}
+	
+	/**
+ 	* Sends a textarea that can be converted to and from a TinyMCE widget on the fly.
+ 	* @param string The name of the form element
+ 	* @param string The initial content. Optional, defaults to blank
+ 	* @param int Rows in textarea
+ 	* @param int Columns in textarea
+ 	* @return string HTML and Javascript code.
+ 	*/
+	
+	function tinymce_textarea($name, $content = '', $rows = 20, $cols = 60)
+	{
+		global $lang;
+		$randomid = md5(microtime() . mt_rand());
+		$html = '';
+		$html .= '<textarea name="' . $name . '" rows="'.$rows.'" cols="'.$cols.'" style="width: 100%;" id="toggleMCEroot_'.$randomid.'">' . $content . '</textarea>';
+		$html .= '<div style="float: right; display: table;" id="mceSwitchAgent_' . $randomid . '">' . $lang->get('etc_tinymce_btn_text') . '&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">' . $lang->get('etc_tinymce_btn_graphical') . '</a></div>';
+		$html .= '<script type="text/javascript">
+								// <![CDATA[
+								function toggleMCE_'.$randomid.'()
+								{
+									var the_obj = document.getElementById(\'toggleMCEroot_' . $randomid . '\');
+									var panel = document.getElementById(\'mceSwitchAgent_' . $randomid . '\');
+									var text_editor = $lang.get("etc_tinymce_btn_text");
+									var graphical_editor = $lang.get("etc_tinymce_btn_graphical");
+									if ( the_obj.dnIsMCE == "yes" )
+									{
+										$dynano(the_obj).destroyMCE();
+										panel.innerHTML = text_editor + \'&nbsp;&nbsp;|&nbsp;&nbsp;<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">\' + graphical_editor + \'</a>\';
+									}
+									else
+									{
+										$dynano(the_obj).switchToMCE();
+										panel.innerHTML = \'<a href="#" onclick="if ( !KILL_SWITCH ) { toggleMCE_'.$randomid.'(); return false; }">\' + text_editor + \'</a>&nbsp;&nbsp;|&nbsp;&nbsp;\' + graphical_editor;
+									}
+								}
+								// ]]>
+							</script>';
+		return $html;
+	}
+	
+	/**
+ 	* Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;)
+ 	* Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class.
+ 	* @param $filename the filename of the template to be parsed
+ 	* @return object
+ 	*/
+ 	
+	function makeParser($filename)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$filename = ENANO_ROOT.'/themes/'.$template->theme.'/'.$filename;
+		if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist');
+		$code = file_get_contents($filename);
+		$parser = new templateIndividual($code);
+		return $parser;
+	}
+	
+	/**
+ 	* Same as $template->makeParser(), but takes a string instead of a filename.
+ 	* @param $text the text to parse
+ 	* @return object
+ 	*/
+ 	
+	function makeParserText($code)
+	{
+		$parser = new templateIndividual($code);
+		return $parser;
+	}
+	
+	/**
+ 	* Fetch the HTML for a plugin-added sidebar block
+ 	* @param $name the plugin name
+ 	* @return string
+ 	*/
+ 	
+	function fetch_block($id, $just_the_innards_maam = false)
+	{
+		if ( $just_the_innards_maam )
+		{
+			$source =& $this->plugin_blocks_content;
+		}
+		else
+		{
+			$source =& $this->plugin_blocks;
+		}
+		return isset($source[$id]) ? $source[$id] : false;
+	}
+	
+	/**
+ 	* Fetches the contents of both sidebars.
+ 	* @return array - key 0 is left, key 1 is right, key 2 is the HTML that makes up an empty sidebar
+ 	* @example list($left, $right) = $template->fetch_sidebar();
+ 	*/
+	
+	function fetch_sidebar()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// first, check the cache
+		if ( $result = $this->fetch_cached_sidebar() )
+		{
+			return $result;
+		}
+		
+		require(ENANO_ROOT . "/themes/{$this->theme}/theme.cfg");
+		
+		profiler_log('Started sidebar parsing');
+		
+		// init our block contents
+		$left = '';
+		$right = '';
+		$min = '';
+		
+		// also might want the links block
+		if ( !$this->fetch_block('Links') )
+			$this->initLinksWidget();
+		
+		// templates to work with
+		$vars = $this->extract_vars('elements.tpl');
+		
+		// sidebar_top and sidebar_bottom are HTML that is prepended/appended to sidebar content. Themes can
+		// choose whether or not to display a sidebar depending on whether the sidebar is empty ( $left == $min )
+		// or not using the sidebar_left and sidebar_right template booleans (the oxygen theme does this).
+		if ( isset($vars['sidebar_top']) ) 
+		{
+			$top = $this->parse($vars['sidebar_top']);
+			$left  .= $top;
+			$right .= $top;
+			$min .= $top;
+		}
+		
+		// grab the blocks from the DB
+		$q = $db->sql_query('SELECT item_id,sidebar_id,block_name,block_type,block_content FROM '.table_prefix.'sidebar' . "\n"
+ 													. '  WHERE item_enabled=1 ORDER BY sidebar_id ASC, item_order ASC;');
+		if ( !$q )
+			$db->_die('The sidebar text data could not be selected.');
+		
+		// explicitly specify $q in case a plugin or PHP block makes a query
+		while ( $row = $db->fetchrow($q) )
+		{
+			// should we skip this block?
+			if (
+					( $row['item_id'] === 2 && !empty($theme['sb_hide_tools']) ) ||
+					( $row['item_id'] === 3 && !empty($theme['sb_hide_user']) ) ||
+					( $row['item_id'] === 4 && !empty($theme['sb_hide_search']) )
+				)
+				continue;
+			
+			// format the block
+			$block_content = $this->format_sidebar_block($row, $vars, $parser);
+			
+			// is there a {restrict} or {hideif} block?
+			if ( preg_match('/\{(restrict|hideif) ([a-z0-9_\(\)\|&! ]+)\}/', $block_content, $match) )
+			{
+				// we have one, check the condition
+				$type =& $match[1];
+				$cond =& $match[2];
+				$result = $this->process_condition($cond);
+				if ( ( $type == 'restrict' && $result == 1 ) || ( $type == 'hideif' && $result == 2 ) )
+				{
+					// throw out this block, it's hidden for whatever reason by the sidebar script
+					continue;
+				}
+				// didn't get a match, so hide the conditional logic
+				// FIXME: this needs to be verbose about syntax errors
+				$block_content = str_replace_once($match[0], '', $block_content);
+			}
+			
+			// if we made it here, this block definitely needs to be displayed. send it to the
+			// parser (set by format_sidebar_block) and decide where to append it (but not in that order ;))
+			$appender = false;
+			
+			if ( $row['sidebar_id'] == SIDEBAR_LEFT )
+			{
+				$appender =& $left;
+			}
+			else if ( $row['sidebar_id'] == SIDEBAR_RIGHT )
+			{
+				$appender =& $right;
+			}
+			else
+			{
+				// uhoh, block_id isn't valid. maybe a plugin wants to put this block somewhere else?
+				$code = $plugins->setHook('sidebar_block_id');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				// if $appender wasn't set by a plugin, don't parse this block to save some CPU cycles
+				if ( !$appender )
+				{
+					continue;
+				}
+			}
+			
+			// assign variables to parser
+			$block_name = $this->tplWikiFormat($row['block_name']);
+			$parser->assign_vars(array(
+					// be nice and format the title (FIXME, does this use a lot of CPU time? still needs l10n in some cases though)
+					'TITLE' => $block_name,
+					'CONTENT' => $block_content
+				));
+			$parsed = $parser->run();
+			
+			// plugins are parsed earlier due to the way disabled/missing plugins that add sidebar blocks are
+			// handled, so the {TITLE} var won't be replaced until now. this allows completely eliminating a
+			// block if it's not available
+			if ( $row['block_type'] == BLOCK_PLUGIN )
+			{
+				$parsed = str_replace('{TITLE}', $block_name, $parsed);
+			}
+			
+			// done parsing - append and continue
+			$appender .= $parsed;
+			
+			// we're done with this - unset it because it's a reference and we don't want it overwritten.
+			// also free the parser to get some RAM back
+			unset($appender, $parser);
+		}
+		
+		// lastly, append any footer HTML
+		if(isset($vars['sidebar_bottom'])) 
+		{
+			$bottom = $this->parse($vars['sidebar_bottom']);
+			$left  .= $bottom;
+			$right .= $bottom;
+			$min   .= $bottom;
+		}
+		
+		$return = array($left, $right, $min);
+		
+		// allow any plugins to append what they want to the return
+		$code = $plugins->setHook('sidebar_fetch_return');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		// cache the result if appropriate
+		$this->cache_compiled_sidebar($return);
+		
+		profiler_log('Finished sidebar parsing');
+		
+		return $return;
+	}
+	
+	/**
+ 	* Runs the appropriate formatting routine on a sidebar row.
+ 	* @param array Row in sidebar table
+ 	* @param array Template variable set from elements.tpl
+ 	* @param null Pass by reference, will be filled with the parser according to the block's type (sidebar_section or sidebar_section_raw)
+ 	* @return string HTML + directives like {restrict} or {hideif}
+ 	*/
+	
+	function format_sidebar_block($row, $vars, &$parser)
+	{
+		// import globals in case a PHP block wants to call the Enano API
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		$parser = null;
+		
+		switch($row['block_type'])
+		{
+			case BLOCK_WIKIFORMAT:
+				$parser = $this->makeParserText($vars['sidebar_section_raw']);
+				$c = RenderMan::render($row['block_content']);
+				break;
+				
+			case BLOCK_TEMPLATEFORMAT:
+				$parser = $this->makeParserText($vars['sidebar_section']);
+				$c = $this->tplWikiFormat($row['block_content']);
+				break;
+				
+			case BLOCK_HTML:
+				$parser = $this->makeParserText($vars['sidebar_section_raw']);
+				$c = $row['block_content'];
+				break;
+				
+			case BLOCK_PHP:
+				// PHP blocks need to be sent directly to eval()
+				$parser = $this->makeParserText($vars['sidebar_section_raw']);
+				ob_start();
+				@eval($row['block_content']);
+				$c = ob_get_contents();
+				ob_end_clean();
+				break;
+				
+			case BLOCK_PLUGIN:
+				$parser = $this->makeParserText('{CONTENT}');
+				$c = '<!-- PLUGIN -->' . (gettype($this->fetch_block($row['block_content'])) == 'string') ?
+								$this->fetch_block($row['block_content']) :
+								// This used to say "can't find plugin block" but I think it's more friendly to just silently hide it.
+								'';
+				break;
+			default:
+				// unknown block type - can a plugin handle it?
+				$code = $plugins->setHook('format_sidebar_block');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				if ( !isset($c) )
+				{
+					// default to wiki formatting (this was going to be straight HTML but this is done for backwards compatibility reasons)
+					$c = RenderMan::render($row['block_content']);
+				}
+				if ( !$parser )
+				{
+					// no parser defined, use the "raw" section by default (plugins are more likely to want raw content
+					// rather than a list of links, and they can set the parser to use sidebar_section if they want)
+					$parser = $this->makeParserText($vars['sidebar_section_raw']);
+				}
+				
+				break;
+		}
+		
+		return $c;
+	}
+	
+	/**
+ 	* Returns the list of things that should not be cached (sorry, I was listening to "The Thing That Should Not Be" by Metallica when I
+ 	* wrote this routine. You should get a copy of Master of Puppets, it's a damn good album.)
+ 	* @return array
+ 	*/
+	
+	function get_cache_replacements()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		return array(
+					'$LOGIN_LINK$' => $this->tpl_strings['LOGIN_LINK'],
+					'$MAIN_PAGE$' => $this->tpl_strings['MAIN_PAGE'],
+					'$USERNAME$' => $session->username
+				);
+	}
+	
+	/**
+ 	* Attempts to load a cached compiled sidebar.
+ 	* @return array Same format as fetch_sidebar()
+ 	*/
+	
+	function fetch_cached_sidebar()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $cache;
+		
+		$cached = false;
+		if ( ($result = $cache->fetch('anon_sidebar')) && !$session->user_logged_in )
+		{
+			if ( isset($result[$this->theme]) )
+			{
+				$cached = $result[$this->theme];
+			}
+		}
+		
+		// if we haven't been able to fetch yet, see if a plugin wants to give us something
+		if ( !$cached )
+		{
+			$code = $plugins->setHook('fetch_cached_sidebar');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+		} 
+		
+		if ( is_array($cached) )
+		{
+			// fetch certain variables that can't be stored in the cache and quickly substitute
+			$replace = $this->get_cache_replacements();
+			foreach ( $cached as &$val )
+			{
+				$val = strtr($val, $replace);
+			}
+			
+			// done processing, send it back
+			return $cached;
+		}
+		
+		return false;
+	}
+	
+	/**
+ 	* Caches a completely compiled sidebar, if appropriate
+ 	* @param array Effectively, return from fetch_sidebar()
+ 	*/
+	
+	function cache_compiled_sidebar($sidebar)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $cache;
+		
+		// check if conditions are right
+		if ( !$session->user_logged_in && getConfig('cache_thumbs') === '1' )
+		{
+			// load any existing cache to make sure other themes' cached sidebars aren't discarded
+			$cached = ( $_ = $cache->fetch('anon_sidebar') ) ? $_ : array();
+			
+			// replace variables
+			$replace = array_flip($this->get_cache_replacements());
+			
+			foreach ( $sidebar as &$section )
+			{
+				$section = strtr($section, $replace);
+			}
+			
+			// compile
+			$cached[$this->theme] = $sidebar;
+			
+			// store
+			$cache->store('anon_sidebar', $cached, 15);
+		}
+	}
+	
+	function initLinksWidget()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		
+		// SourceForge/W3C buttons
+		$ob = Array();
+		if(getConfig('sflogo_enabled')=='1')
+		{
+			$sflogo_secure = ( isset($_SERVER['HTTPS']) ) ? 'https' : 'http';
+			$ob[] = '<a style="text-align: center;" href="http://sourceforge.net/" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border-width: 0px;" alt="SourceForge.net Logo" src="' . $sflogo_secure . '://sflogo.sourceforge.net/sflogo.php?group_id='.getConfig('sflogo_groupid').'&amp;type='.getConfig('sflogo_type').'" /></a>';
+		}
+		if(getConfig('w3c_v32')     =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 3.2"  src="http://www.w3.org/Icons/valid-html32" /></a>';
+		if(getConfig('w3c_v40')     =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 4.0"  src="http://www.w3.org/Icons/valid-html40" /></a>';
+		if(getConfig('w3c_v401')    =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid HTML 4.01" src="http://www.w3.org/Icons/valid-html401" /></a>';
+		if(getConfig('w3c_vxhtml10')=='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid XHTML 1.0" src="http://www.w3.org/Icons/valid-xhtml10" /></a>';
+		if(getConfig('w3c_vxhtml11')=='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid XHTML 1.1" src="http://www.w3.org/Icons/valid-xhtml11" /></a>';
+		if(getConfig('w3c_vcss')    =='1') $ob[] = '<a style="text-align: center;" href="http://validator.w3.org/check?uri=referer" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="Valid CSS"       src="http://www.w3.org/Icons/valid-css" /></a>';
+		if(getConfig('dbd_button')  =='1') $ob[] = '<a style="text-align: center;" href="http://www.defectivebydesign.org/join/button" onclick="if ( !KILL_SWITCH ) { window.open(this.href);return false; }"><img style="border: 0px solid #FFFFFF;" alt="DRM technology restricts what you can do with your computer" src="' . cdnPath . '/images/defectivebydesign.png" /><br /><small>Protect your freedom >></small></a>';
+		
+		$code = $plugins->setHook('links_widget');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		if(count($ob) > 0 || getConfig('powered_btn', '1') == '1') $sb_links = '<div style="text-align: center; padding: 5px 0;">'. ( ( getConfig('powered_btn', '1') == '1' ) ? $this->fading_button : '' ) . implode('<br />', $ob).'</div>';
+		else $sb_links = '';
+		
+		$this->sidebar_widget('Links', $sb_links);
+	}
+	
+	/**
+ 	* Builds a box showing unread private messages.
+ 	*/
+	
+	function notify_unread_pms()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if ( ( $paths->page_id == 'PrivateMessages' || $paths->page_id == 'Preferences' ) && $paths->namespace == 'Special' )
+		{
+			return '';
+		}
+		$ob = '<div class="usermessage">'."\n";
+		$s = ( $session->unread_pms == 1 ) ? '' : 's';
+		$ob .= "  <b>You have $session->unread_pms <a href=" . '"' . makeUrlNS('Special', 'PrivateMessages' ) . '"' . ">unread private message$s</a>.</b><br />\n  Messages: ";
+		$q = $db->sql_query('SELECT message_id,message_from,subject,date FROM '.table_prefix.'privmsgs WHERE message_to=\'' . $session->username . '\' AND message_read=0 ORDER BY date DESC;');
+		if ( !$q )
+			$db->_die();
+		$messages = array();
+		while ( $row = $db->fetchrow() )
+		{
+			$messages[] = '<a href="' . makeUrlNS('Special', 'PrivateMessages/View/' . $row['message_id']) . '" title="Sent ' . enano_date(ED_DATE | ED_TIME, $row['date']) . ' by ' . $row['message_from'] . '">' . $row['subject'] . '</a>';
+		}
+		$ob .= implode(",\n    " , $messages)."\n";
+		$ob .= '</div>'."\n";
+		return $ob;
+	}
+	
+	/**
+ 	* Parse a system message.
+ 	* @param string message
+ 	* @return string
+ 	*/
+	
+	function parse_system_message($text)
+	{
+		ob_start();
+		eval( '?>' . $text );
+		$result = ob_get_contents();
+		ob_end_clean();
+		return $this->parse($result);
+	}
+	
+	/**
+ 	* Return the wiki mode edit notice, rendered and addslashes()'ed.
+ 	* @return string
+ 	*/
+	
+	function get_wiki_edit_notice()
+	{
+		global $cache;
+		
+		if ( getConfig('wiki_edit_notice', 0) != 1 )
+			return '';
+		
+		$notice = RenderMan::render(getConfig('wiki_edit_notice_text'));
+		return $notice;
+	}
+	
 } // class template
 
 /**
@@ -2652,160 +2652,160 @@
 
 function template_compiler_core($text)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  // A random seed used to salt tags
-  $seed = md5 ( microtime() . mt_rand() );
-  
-  // Strip out PHP sections
-  preg_match_all('/<\?php(.+?)\?>/is', $text, $php_matches);
-  
-  foreach ( $php_matches[0] as $i => $match )
-  {
-    // Substitute the PHP section with a random tag
-    $tag = "{PHP:$i:$seed}";
-    $text = str_replace_once($match, $tag, $text);
-  }
-  
-  // Escape slashes and single quotes in template code
-  $text = str_replace('\\', '\\\\', $text);
-  $text = str_replace('\'', '\\\'', $text);
-  
-  // Initialize the PHP compiled code
-  $text = 'ob_start(); global $paths, $template; echo \''.$text.'\'; $tpl_code = ob_get_contents(); ob_end_clean(); return $tpl_code;';
-  
-  ##
-  ## Main rules
-  ##
-  
-  //
-  // Conditionals
-  //
-  
-  $keywords = array('BEGIN', 'BEGINNOT', 'IFSET', 'IFPLUGIN');
-  
-  // only do this if the plugins API is loaded
-  if ( is_object(@$plugins) )
-  {
-    $code = $plugins->setHook('template_compile_logic_keyword');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-  }
-  
-  $keywords = implode('|', $keywords);
-  
-  // Matches
-  //          1     2                 3                 4   56                       7     8        9
-  $regexp = '/(<!-- ?(' . $keywords . ') ([A-z0-9_-]+) ?-->)([\w\W]*)((<!-- ?BEGINELSE \\3 ?-->)([\w\W]*))?(<!-- ?END(IF)? \\3 ?-->)/isU';
-  
-  /*
-  The way this works is: match all blocks using the standard form with a different keyword in the block each time,
-  and replace them with appropriate PHP logic. Plugin-extensible now. :-)
-  */
-  
-  // This is a workaround for what seems like a PCRE bug
-  while ( preg_match_all($regexp, $text, $matches) )
-  {
-  
-  for ( $i = 0; $i < count($matches[0]); $i++ )
-  {
-    $start_tag =& $matches[1][$i];
-    $type =& $matches[2][$i];
-    $test =& $matches[3][$i];
-    $particle_true  =& $matches[4][$i];
-    $else_tag =& $matches[6][$i];
-    $particle_else =& $matches[7][$i];
-    $end_tag =& $matches[8][$i];
-    
-    switch($type)
-    {
-      case 'BEGIN':
-        $cond = "isset(\$this->tpl_bool['$test']) && \$this->tpl_bool['$test']";
-        break;
-      case 'BEGINNOT':
-        $cond = "!isset(\$this->tpl_bool['$test']) || ( isset(\$this->tpl_bool['$test']) && !\$this->tpl_bool['$test'] )";
-        break;
-      case 'IFPLUGIN':
-        $cond = "getConfig('plugin_$test') == '1'";
-        break;
-      case 'IFSET':
-        $cond = "isset(\$this->tpl_strings['$test'])";
-        break;
-      default:
-        // only do this if the plugins API is loaded
-        if ( is_object(@$plugins) )
-        {
-          $code = $plugins->setHook('template_compile_logic_cond');
-          foreach ( $code as $cmd )
-          {
-            eval($cmd);
-          }
-        }
-        break;
-    }
-    
-    if ( !isset($cond) || ( isset($cond) && !is_string($cond) ) )
-      continue;
-    
-    $tag_complete = <<<TPLCODE
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	// A random seed used to salt tags
+	$seed = md5 ( microtime() . mt_rand() );
+	
+	// Strip out PHP sections
+	preg_match_all('/<\?php(.+?)\?>/is', $text, $php_matches);
+	
+	foreach ( $php_matches[0] as $i => $match )
+	{
+		// Substitute the PHP section with a random tag
+		$tag = "{PHP:$i:$seed}";
+		$text = str_replace_once($match, $tag, $text);
+	}
+	
+	// Escape slashes and single quotes in template code
+	$text = str_replace('\\', '\\\\', $text);
+	$text = str_replace('\'', '\\\'', $text);
+	
+	// Initialize the PHP compiled code
+	$text = 'ob_start(); global $paths, $template; echo \''.$text.'\'; $tpl_code = ob_get_contents(); ob_end_clean(); return $tpl_code;';
+	
+	##
+	## Main rules
+	##
+	
+	//
+	// Conditionals
+	//
+	
+	$keywords = array('BEGIN', 'BEGINNOT', 'IFSET', 'IFPLUGIN');
+	
+	// only do this if the plugins API is loaded
+	if ( is_object(@$plugins) )
+	{
+		$code = $plugins->setHook('template_compile_logic_keyword');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+	}
+	
+	$keywords = implode('|', $keywords);
+	
+	// Matches
+	//          1     2                 3                 4   56                       7     8        9
+	$regexp = '/(<!-- ?(' . $keywords . ') ([A-z0-9_-]+) ?-->)([\w\W]*)((<!-- ?BEGINELSE \\3 ?-->)([\w\W]*))?(<!-- ?END(IF)? \\3 ?-->)/isU';
+	
+	/*
+	The way this works is: match all blocks using the standard form with a different keyword in the block each time,
+	and replace them with appropriate PHP logic. Plugin-extensible now. :-)
+	*/
+	
+	// This is a workaround for what seems like a PCRE bug
+	while ( preg_match_all($regexp, $text, $matches) )
+	{
+	
+	for ( $i = 0; $i < count($matches[0]); $i++ )
+	{
+		$start_tag =& $matches[1][$i];
+		$type =& $matches[2][$i];
+		$test =& $matches[3][$i];
+		$particle_true  =& $matches[4][$i];
+		$else_tag =& $matches[6][$i];
+		$particle_else =& $matches[7][$i];
+		$end_tag =& $matches[8][$i];
+		
+		switch($type)
+		{
+			case 'BEGIN':
+				$cond = "isset(\$this->tpl_bool['$test']) && \$this->tpl_bool['$test']";
+				break;
+			case 'BEGINNOT':
+				$cond = "!isset(\$this->tpl_bool['$test']) || ( isset(\$this->tpl_bool['$test']) && !\$this->tpl_bool['$test'] )";
+				break;
+			case 'IFPLUGIN':
+				$cond = "getConfig('plugin_$test') == '1'";
+				break;
+			case 'IFSET':
+				$cond = "isset(\$this->tpl_strings['$test'])";
+				break;
+			default:
+				// only do this if the plugins API is loaded
+				if ( is_object(@$plugins) )
+				{
+					$code = $plugins->setHook('template_compile_logic_cond');
+					foreach ( $code as $cmd )
+					{
+						eval($cmd);
+					}
+				}
+				break;
+		}
+		
+		if ( !isset($cond) || ( isset($cond) && !is_string($cond) ) )
+			continue;
+		
+		$tag_complete = <<<TPLCODE
 ';
-    /* START OF CONDITION: $type ($test) */
-    if ( $cond )
-    {
-      echo '$particle_true';
-    /* ELSE OF CONDITION: $type ($test) */
-    }
-    else
-    {
-      echo '$particle_else';
-    /* END OF CONDITION: $type ($test) */
-    }
-    echo '
+		/* START OF CONDITION: $type ($test) */
+		if ( $cond )
+		{
+			echo '$particle_true';
+		/* ELSE OF CONDITION: $type ($test) */
+		}
+		else
+		{
+			echo '$particle_else';
+		/* END OF CONDITION: $type ($test) */
+		}
+		echo '
 TPLCODE;
-    
-    $text = str_replace_once($matches[0][$i], $tag_complete, $text);
-  }
-  }
-  
-  // For debugging ;-)
-  // die("<pre>&lt;?php\n" . htmlspecialchars($text."\n\n".print_r($matches,true)) . "\n\n?&gt;</pre>");
-  
-  //
-  // Data substitution/variables
-  //
-  
-  // System messages
-  $text = preg_replace('/<!-- SYSMSG ([A-z0-9\._-]+?) -->/is', '\' . $this->parse_system_message($paths->sysMsg(\'\\1\')) . \'', $text);
-  
-  // Hooks
-  $text = preg_replace('/<!-- HOOK ([A-z0-9_]+) -->/', '\' . $this->get_theme_hook(\'\\1\') . \'', $text);
-  
-  // only do this if the plugins API is loaded
-  if ( is_object(@$plugins) )
-  {
-    $code = $plugins->setHook('template_compile_subst');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-  }
-  
-  // Template variables
-  $text = preg_replace('/\{([A-z0-9_-]+?)\}/is', '\' . $this->tpl_strings[\'\\1\'] . \'', $text);
-  
-  // Reinsert PHP
-  
-  foreach ( $php_matches[1] as $i => $match )
-  {
-    // Substitute the random tag with the "real" PHP code
-    $tag = "{PHP:$i:$seed}";
-    $text = str_replace_once($tag, "'; $match echo '", $text);
-  }
-  
-  // echo('<pre>' . htmlspecialchars($text) . '</pre>');
-  
-  return $text;
+		
+		$text = str_replace_once($matches[0][$i], $tag_complete, $text);
+	}
+	}
+	
+	// For debugging ;-)
+	// die("<pre>&lt;?php\n" . htmlspecialchars($text."\n\n".print_r($matches,true)) . "\n\n?&gt;</pre>");
+	
+	//
+	// Data substitution/variables
+	//
+	
+	// System messages
+	$text = preg_replace('/<!-- SYSMSG ([A-z0-9\._-]+?) -->/is', '\' . $this->parse_system_message($paths->sysMsg(\'\\1\')) . \'', $text);
+	
+	// Hooks
+	$text = preg_replace('/<!-- HOOK ([A-z0-9_]+) -->/', '\' . $this->get_theme_hook(\'\\1\') . \'', $text);
+	
+	// only do this if the plugins API is loaded
+	if ( is_object(@$plugins) )
+	{
+		$code = $plugins->setHook('template_compile_subst');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+	}
+	
+	// Template variables
+	$text = preg_replace('/\{([A-z0-9_-]+?)\}/is', '\' . $this->tpl_strings[\'\\1\'] . \'', $text);
+	
+	// Reinsert PHP
+	
+	foreach ( $php_matches[1] as $i => $match )
+	{
+		// Substitute the random tag with the "real" PHP code
+		$tag = "{PHP:$i:$seed}";
+		$text = str_replace_once($tag, "'; $match echo '", $text);
+	}
+	
+	// echo('<pre>' . htmlspecialchars($text) . '</pre>');
+	
+	return $text;
 }
 
 /**
@@ -2820,64 +2820,64 @@
 
 class templateIndividual extends template
 {
-  var $tpl_strings, $tpl_bool, $tpl_code;
-  var $compiled = false;
-  /**
-   * Constructor.
-   */
-  function __construct($text)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $this->tpl_code = $text;
-    $this->tpl_strings = $template->tpl_strings;
-    $this->tpl_bool = $template->tpl_bool;
-  }
-  /**
-   * PHP 4 constructor. Deprecated in 1.1.x.
-   */
-  /*
-  function templateIndividual($text)
-  {
-    $this->__construct($text);
-  }
-  */
-  
-  /**
-   * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
-   * @param $vars array
-   */
-  
-  // We add the unused variable $from_internal here to silence "declaration should be compatible" errors
-  function assign_vars($vars, $from_internal = false)
-  {
-    $this->tpl_strings = array_merge($this->tpl_strings, $vars);
-  }
-  
-  /**
-   * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
-   * @param $vars array
-   */
-  
-  // We add the unused variable $from_internal here to silence "declaration should be compatible" errors
-  function assign_bool($vars, $from_internal = false)
-  {
-    $this->tpl_bool = array_merge($this->tpl_bool, $vars);
-  }
-  
-  /**
-   * Compiles and executes the template code.
-   * @return string
-   */
-  function run()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if(!$this->compiled)
-    {
-      $this->tpl_code = $this->compile_template_text($this->tpl_code);
-      $this->compiled = true;
-    }
-    return eval($this->tpl_code);
-  }
+	var $tpl_strings, $tpl_bool, $tpl_code;
+	var $compiled = false;
+	/**
+ 	* Constructor.
+ 	*/
+	function __construct($text)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$this->tpl_code = $text;
+		$this->tpl_strings = $template->tpl_strings;
+		$this->tpl_bool = $template->tpl_bool;
+	}
+	/**
+ 	* PHP 4 constructor. Deprecated in 1.1.x.
+ 	*/
+	/*
+	function templateIndividual($text)
+	{
+		$this->__construct($text);
+	}
+	*/
+	
+	/**
+ 	* Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
+ 	* @param $vars array
+ 	*/
+	
+	// We add the unused variable $from_internal here to silence "declaration should be compatible" errors
+	function assign_vars($vars, $from_internal = false)
+	{
+		$this->tpl_strings = array_merge($this->tpl_strings, $vars);
+	}
+	
+	/**
+ 	* Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
+ 	* @param $vars array
+ 	*/
+	
+	// We add the unused variable $from_internal here to silence "declaration should be compatible" errors
+	function assign_bool($vars, $from_internal = false)
+	{
+		$this->tpl_bool = array_merge($this->tpl_bool, $vars);
+	}
+	
+	/**
+ 	* Compiles and executes the template code.
+ 	* @return string
+ 	*/
+	function run()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if(!$this->compiled)
+		{
+			$this->tpl_code = $this->compile_template_text($this->tpl_code);
+			$this->compiled = true;
+		}
+		return eval($this->tpl_code);
+	}
 }
 
 /**
@@ -2887,443 +2887,443 @@
 
 class template_nodb
 {
-  var $fading_button, $tpl_strings, $tpl_bool, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list;
-  function __construct()
-  {
-    $this->tpl_bool    = Array();
-    $this->tpl_strings = Array();
-    $this->sidebar_extra = '';
-    $this->sidebar_widgets = '';
-    $this->toolbar_menu = '';
-    $this->additional_headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>';
-    
-    $this->fading_button = '<div style="background-image: url('.scriptPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
-                              <a href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.scriptPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
-                            </div>';
-    
-    // get list of themes
-    $this->theme_list = array();
-    $this->named_theme_list = array();
-    $order = 0;
-    
-    if ( $dir = @opendir( ENANO_ROOT . '/themes' ) )
-    {
-      while ( $dh = @readdir($dir) )
-      {
-        if ( $dh == '.' || $dh == '..' || !is_dir( ENANO_ROOT . "/themes/$dh" ) )
-          continue;
-        $theme_dir = ENANO_ROOT . "/themes/$dh";
-        if ( !file_exists("$theme_dir/theme.cfg") )
-          continue;
-        $data = array(
-            'theme_id' => $dh,
-            'theme_name' => ucwords($dh),
-            'enabled' => 1,
-            'theme_order' => ++$order,
-            'default_style' => $this->get_default_style($dh)
-          );
-        $this->named_theme_list[$dh] = $data;
-        $this->theme_list[] =& $this->named_theme_list[$dh];
-      }
-      @closedir($dir);
-    }
-  }
-  function template() {
-    $this->__construct();
-  }
-  function get_default_style($theme_id)
-  {
-    if ( !is_dir( ENANO_ROOT . "/themes/$theme_id/css" ) )
-      return false;
-    $ds = false;
-    if ( $dh = @opendir( ENANO_ROOT . "/themes/$theme_id/css" ) )
-    {
-      while ( $dir = @readdir($dh) )
-      {
-        if ( !preg_match('/\.css$/', $dir) )
-          continue;
-        if ( $dir == '_printable.css' )
-          continue;
-        $ds = preg_replace('/\.css$/', '', $dir);
-        break;
-      }
-      closedir($dh);
-    }
-    else
-    {
-      return false;
-    }
-    return $ds;
-  }
-  function get_css($s = false)
-  {
-    if ( $s )
-      return $this->process_template('css/'.$s);
-    else
-      return $this->process_template('css/'.$this->style.'.css');
-  }
-  function load_theme($name, $css, $auto_init = true)
-  {
-    if ( !isset($this->named_theme_list[$name]) )
-      $name = $this->theme_list[0]['theme_id'];
-    
-    if ( !file_exists(ENANO_ROOT . "/themes/$name/css/$css.css") )
-      $css = $this->named_theme_list[$name]['default_style'];
-    
-    $this->theme = $name;
-    $this->style = $css;
-    
-    $this->tpl_strings['SCRIPTPATH'] = scriptPath;
-    if ( $auto_init )
-      $this->init_vars();
-  }
-  function add_header($html)
-  {
-    $this->additional_headers .= "\n<!-- ----------------------------------------------------------- -->\n\n    " . $html;
-  }
-  function init_vars()
-  {
-    global $sideinfo;
-    global $this_page;
-    global $lang;
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $tplvars = $this->extract_vars('elements.tpl');
-    $tb = '';
-    // Get the "article" button text (depends on namespace)
-    if(defined('IN_ENANO_INSTALL') && is_object($lang)) $ns = $lang->get('meta_btn_article');
-    else if ( isset($GLOBALS['article_btn']) ) $ns = $GLOBALS['article_btn'];
-    else $ns = 'system error page';
-    $t = str_replace('{FLAGS}', 'onclick="return false;" class="current" title="Hey! A button that doesn\'t do anything. Clever..." accesskey="a"', $tplvars['toolbar_button']);
-    $t = str_replace('{HREF}', '#', $t);
-    $t = str_replace('{TEXT}', $ns, $t);
-    $tb .= $t;
-    
-    // Page toolbar
-    
-    $this->tpl_bool = Array(
-      'auth_admin'=>true,
-      'user_logged_in'=>true,
-      'right_sidebar'=>false,
-      );
-    $this->tpl_bool['in_sidebar_admin'] = false;
-    
-    $this->tpl_bool['auth_rename'] = false;
-    
-    $asq = $asa = '';
-    
-    $this->tpl_bool['fixed_menus'] = false;
-    $slink = defined('IN_ENANO_INSTALL') ? scriptPath.'/install.php?mode=css' : makeUrlNS('Special', 'CSS');
-    
-    $title = ( is_object($paths) ) ? $paths->page : 'Critical error';
-    
-    $headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>';
-    
-    $js_dynamic = '';
-    if ( defined('IN_ENANO_INSTALL') )
-    {
-      $js_dynamic .= '<script type="text/javascript" src="install.php?mode=langjs"></script>';
-    }
-    $js_dynamic .= '<script type="text/javascript">var title="'. $title .'"; var scriptPath="'.scriptPath.'"; var cdnPath="'.scriptPath.'"; var ENANO_SID=""; var AES_BITS='.AES_BITS.'; var AES_BLOCKSIZE=' . AES_BLOCKSIZE . '; var pagepass=\'\'; var ENANO_LANG_ID = 1; var enano_version = \'' . enano_version() . '\'; var msg_loading_component = \'Loading %component%...\';</script>';
-    
-    global $site_name, $site_desc;
-    $site_default_name = ( !empty($site_name) ) ? $site_name : 'Critical error';
-    $site_default_desc = ( !empty($site_desc) ) ? $site_desc : 'This site is experiencing a problem and cannot load.';
-    
-    $site_name_final = ( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_site_name') : $site_default_name;
-    $site_desc_final = ( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_site_desc') : $site_default_desc;
-    
-    // The rewritten template engine will process all required vars during the load_template stage instead of (cough) re-processing everything each time around.
-    $tpl_strings = Array(
-      'PAGE_NAME'=>$this_page,
-      'PAGE_URLNAME'=>'Null',
-      'SITE_NAME' => $site_name_final,
-      'USERNAME'=>'admin',
-      'SITE_DESC' => $site_desc_final,
-      'TOOLBAR'=>$tb,
-      'SCRIPTPATH'=>scriptPath,
-      'CONTENTPATH'=>contentPath,
-      'CDNPATH' => scriptPath,
-      'JS_HEADER' => '<script type="text/javascript" src="' . scriptPath . '/includes/clientside/static/enano-lib-basic.js"></script>',
-      'JS_FOOTER' => '',
-      'ADMIN_SID_QUES'=>$asq,
-      'ADMIN_SID_AMP'=>$asa,
-      'ADMIN_SID_AMP_HTML'=>'',
-      'ADDITIONAL_HEADERS'=>$this->additional_headers,
-      'SIDEBAR_EXTRA'=>'',
-      'COPYRIGHT'=>( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_enano_copyright') : ( defined('ENANO_CONFIG_FETCHED') ? getConfig('copyright_notice') : '' ),
-      'TOOLBAR_EXTRAS'=>'',
-      'REQUEST_URI'=>( isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '' ).$_SERVER['REQUEST_URI'],
-      'STYLE_LINK'=>$slink,
-      'LOGOUT_LINK'=>'',
-      'THEME_LINK'=>'',
-      'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme,
-      'THEME_ID'=>$this->theme,
-      'STYLE_ID'=>$this->style,
-      'JS_DYNAMIC_VARS'=>$js_dynamic,
-      'SIDEBAR_RIGHT'=>'',
-      'REPORT_URI' => '',
-      'URL_ABOUT_ENANO' => 'http://enanocms.org/',
-      'ENANO_VERSION' => enano_version()
-      );
-    $this->tpl_strings = array_merge($tpl_strings, $this->tpl_strings);
-    
-    $sidebar = ( is_array(@$sideinfo) ) ? $sideinfo : '';
-    if ( $sidebar != '' )
-    {
-      if ( isset($tplvars['sidebar_top']) )
-      {
-        $text = $this->makeParserText($tplvars['sidebar_top']);
-        $top = $text->run();
-      }
-      else
-      {
-        $top = '';
-      }
-      
-      $p = $this->makeParserText($tplvars['sidebar_section']);
-      $b = $this->makeParserText($tplvars['sidebar_button']);
-      $sidebar_text = '';
-      
-      foreach ( $sidebar as $title => $links )
-      {
-        $p->assign_vars(array(
-          'TITLE' => $title
-        ));
-        // build content
-        $content = '';
-        foreach ( $links as $link_text => $url )
-        {
-          $b->assign_vars(array(
-            'HREF' => htmlspecialchars($url),
-            'FLAGS' => '',
-            'TEXT' => $link_text
-          ));
-          $content .= $b->run();
-        }
-        $p->assign_vars(array(
-          'CONTENT' => $content
-        ));
-        $sidebar_text .= $p->run();
-      }
-      
-      if ( isset($tplvars['sidebar_bottom']) )
-      {
-        $text = $this->makeParserText($tplvars['sidebar_bottom']);
-        $bottom = $text->run();
-      }
-      else
-      {
-        $bottom = '';
-      }
-      $sidebar = $top . $sidebar_text . $bottom;
-    }
-    $this->tpl_strings['SIDEBAR_LEFT'] = $sidebar;
-    
-    $this->tpl_bool['sidebar_left']  = ( $this->tpl_strings['SIDEBAR_LEFT']  != '') ? true : false;
-    $this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != '') ? true : false;
-    $this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility
-    $this->tpl_bool['stupid_mode'] = true;
-  }
-  function header($simple = false) 
-  {
-    $filename = ( $simple ) ? 'simple-header.tpl' : 'header.tpl';
-    if ( !$this->no_headers )
-    {
-      echo $this->process_template($filename);
-    }
-  }
-  function footer($simple = false)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    if(!$this->no_headers) {
-      global $_starttime;
-      
-      $filename = ( $simple ) ? 'simple-footer.tpl' : 'footer.tpl';
-      $t = $this->process_template($filename);
-      
-      $f = microtime_float();
-      $f = $f - $_starttime;
-      $f = round($f, 4);
-      
-      if ( is_object($lang) )
-      {
-        $t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f));
-        $t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f));
-        $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => ( is_object($db) ? $db->num_queries : 'N/A' ))) . '</a>';
-        $dbg = $t_loc;
-        $dbg_long = $t_loc_long;
-        if ( $session->user_level >= USER_LEVEL_ADMIN )
-        {
-          $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-          $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-        }
-        $t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
-        $t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
-      }
-      else
-      {
-        $t_loc = "Time: {$f}s";
-        $t_loc_long = "Generated in {$f}sec";
-        $q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . ( is_object($db) ? "{$db->num_queries} SQL" : 'Queries: N/A' ) . '</a>';
-        $dbg = $t_loc;
-        $dbg_long = $t_loc_long;
-        if ( is_object($session) )
-        {
-          if ( $session->user_level >= USER_LEVEL_ADMIN )
-          {
-            $dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-            $dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
-          }
-        }
-        $t = str_replace('[[EnanoPoweredLink]]', 'Powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t);
-        $t = str_replace('[[EnanoPoweredLinkLong]]', 'Website engine powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t);
-      }
-      
-      $t = str_replace('[[Stats]]', $dbg, $t);
-      $t = str_replace('[[StatsLong]]', $dbg_long, $t);
-      $t = str_replace('[[NumQueries]]', ( is_object($db) ? (string)$db->num_queries : '0' ), $t);
-      $t = str_replace('[[GenTime]]', (string)$f, $t);
-      $t = str_replace('[[NumQueriesLoc]]', $q_loc, $t);
-      $t = str_replace('[[GenTimeLoc]]', $t_loc, $t);
-      
-      if ( defined('ENANO_PROFILE') )
-      {
-        $t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
-      }
-      
-      echo $t;
-    }
-    else return '';
-  }
-  function getHeader()
-  {
-    if(!$this->no_headers) return $this->process_template('header.tpl');
-    else return '';
-  }
-  function getFooter()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if(!$this->no_headers) {
-      global $_starttime;
-      $f = microtime(true);
-      $f = $f - $_starttime;
-      $f = round($f, 4);
-      if(defined('IN_ENANO_INSTALL')) $nq = 'N/A';
-      else $nq = $db->num_queries;
-      if($nq == 0) $nq = 'N/A';
-      $dbg = 'Time: '.$f.'s  |  Queries: '.$nq;
-      if($nq == 0) $nq = 'N/A';
-      $t = $this->process_template('footer.tpl');
-      $t = str_replace('[[Stats]]', $dbg, $t);
-      return $t;
-    }
-    else return '';
-  }
-  
-  function process_template($file)
-  {
-    $compiled = $this->compile_template($file);
-    $result = eval($compiled);
-    return $result;
-  }
-  
-  function extract_vars($file) {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting');
-    $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file);
-    preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches);
-    $tplvars = Array();
-    for($i=0;$i<sizeof($matches[1]);$i++)
-    {
-      $tplvars[$matches[1][$i]] = $matches[2][$i];
-    }
-    return $tplvars;
-  }
-  function compile_template($text)
-  {
-    $text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text);
-    return $this->compile_template_text_post(template_compiler_core($text));
-  }
-  
-  function compile_template_text($text)
-  {
-    return $this->compile_template_text_post(template_compiler_core($text));
-  }
-  
-  /**
-   * Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks.
-   * @param string Mostly-processed TPL code
-   * @return string
-   */
-  
-  function compile_template_text_post($text)
-  {
-    global $lang;
-    preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches);
-    foreach ( $matches[1] as $i => $string_id )
-    {
-      if ( is_object(@$lang) )
-      {
-        $string = $lang->get($string_id);
-      }
-      else
-      {
-        $string = '[language not loaded]';
-      }
-      $string = str_replace('\\', '\\\\', $string);
-      $string = str_replace('\'', '\\\'', $string);
-      $text = str_replace_once($matches[0][$i], $string, $text);
-    }
-    return $text;
-  }
-  
-  /**
-   * Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;)
-   * Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class.
-   * @param $filename the filename of the template to be parsed
-   * @return object
-   */
-   
-  function makeParser($filename)
-  {
-    $filename = ENANO_ROOT.'/themes/'.$this->theme.'/'.$filename;
-    if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist');
-    $code = file_get_contents($filename);
-    $parser = new templateIndividualSafe($code, $this);
-    return $parser;
-  }
-  
-  /**
-   * Same as $template->makeParser(), but takes a string instead of a filename.
-   * @param $text the text to parse
-   * @return object
-   */
-   
-  function makeParserText($code)
-  {
-    $parser = new templateIndividualSafe($code, $this);
-    return $parser;
-  }
-  
-  /**
-   * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
-   * @param $vars array
-   */
-  function assign_vars($vars, $_ignored = false)
-  {
-    if(is_array($this->tpl_strings))
-      $this->tpl_strings = array_merge($this->tpl_strings, $vars);
-    else
-      $this->tpl_strings = $vars;
-  }
-  
-  function get_theme_hook()
-  {
-    return '';
-  }
-   
+	var $fading_button, $tpl_strings, $tpl_bool, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list;
+	function __construct()
+	{
+		$this->tpl_bool    = Array();
+		$this->tpl_strings = Array();
+		$this->sidebar_extra = '';
+		$this->sidebar_widgets = '';
+		$this->toolbar_menu = '';
+		$this->additional_headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>';
+		
+		$this->fading_button = '<div style="background-image: url('.scriptPath.'/images/about-powered-enano-hover.png); background-repeat: no-repeat; width: 88px; height: 31px; margin: 0 auto 5px auto;">
+															<a href="http://enanocms.org/" onclick="window.open(this.href); return false;"><img style="border-width: 0;" alt=" " src="'.scriptPath.'/images/about-powered-enano.png" onmouseover="domOpacity(this, 100, 0, 500);" onmouseout="domOpacity(this, 0, 100, 500);" /></a>
+														</div>';
+		
+		// get list of themes
+		$this->theme_list = array();
+		$this->named_theme_list = array();
+		$order = 0;
+		
+		if ( $dir = @opendir( ENANO_ROOT . '/themes' ) )
+		{
+			while ( $dh = @readdir($dir) )
+			{
+				if ( $dh == '.' || $dh == '..' || !is_dir( ENANO_ROOT . "/themes/$dh" ) )
+					continue;
+				$theme_dir = ENANO_ROOT . "/themes/$dh";
+				if ( !file_exists("$theme_dir/theme.cfg") )
+					continue;
+				$data = array(
+						'theme_id' => $dh,
+						'theme_name' => ucwords($dh),
+						'enabled' => 1,
+						'theme_order' => ++$order,
+						'default_style' => $this->get_default_style($dh)
+					);
+				$this->named_theme_list[$dh] = $data;
+				$this->theme_list[] =& $this->named_theme_list[$dh];
+			}
+			@closedir($dir);
+		}
+	}
+	function template() {
+		$this->__construct();
+	}
+	function get_default_style($theme_id)
+	{
+		if ( !is_dir( ENANO_ROOT . "/themes/$theme_id/css" ) )
+			return false;
+		$ds = false;
+		if ( $dh = @opendir( ENANO_ROOT . "/themes/$theme_id/css" ) )
+		{
+			while ( $dir = @readdir($dh) )
+			{
+				if ( !preg_match('/\.css$/', $dir) )
+					continue;
+				if ( $dir == '_printable.css' )
+					continue;
+				$ds = preg_replace('/\.css$/', '', $dir);
+				break;
+			}
+			closedir($dh);
+		}
+		else
+		{
+			return false;
+		}
+		return $ds;
+	}
+	function get_css($s = false)
+	{
+		if ( $s )
+			return $this->process_template('css/'.$s);
+		else
+			return $this->process_template('css/'.$this->style.'.css');
+	}
+	function load_theme($name, $css, $auto_init = true)
+	{
+		if ( !isset($this->named_theme_list[$name]) )
+			$name = $this->theme_list[0]['theme_id'];
+		
+		if ( !file_exists(ENANO_ROOT . "/themes/$name/css/$css.css") )
+			$css = $this->named_theme_list[$name]['default_style'];
+		
+		$this->theme = $name;
+		$this->style = $css;
+		
+		$this->tpl_strings['SCRIPTPATH'] = scriptPath;
+		if ( $auto_init )
+			$this->init_vars();
+	}
+	function add_header($html)
+	{
+		$this->additional_headers .= "\n<!-- ----------------------------------------------------------- -->\n\n    " . $html;
+	}
+	function init_vars()
+	{
+		global $sideinfo;
+		global $this_page;
+		global $lang;
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$tplvars = $this->extract_vars('elements.tpl');
+		$tb = '';
+		// Get the "article" button text (depends on namespace)
+		if(defined('IN_ENANO_INSTALL') && is_object($lang)) $ns = $lang->get('meta_btn_article');
+		else if ( isset($GLOBALS['article_btn']) ) $ns = $GLOBALS['article_btn'];
+		else $ns = 'system error page';
+		$t = str_replace('{FLAGS}', 'onclick="return false;" class="current" title="Hey! A button that doesn\'t do anything. Clever..." accesskey="a"', $tplvars['toolbar_button']);
+		$t = str_replace('{HREF}', '#', $t);
+		$t = str_replace('{TEXT}', $ns, $t);
+		$tb .= $t;
+		
+		// Page toolbar
+		
+		$this->tpl_bool = Array(
+			'auth_admin'=>true,
+			'user_logged_in'=>true,
+			'right_sidebar'=>false,
+			);
+		$this->tpl_bool['in_sidebar_admin'] = false;
+		
+		$this->tpl_bool['auth_rename'] = false;
+		
+		$asq = $asa = '';
+		
+		$this->tpl_bool['fixed_menus'] = false;
+		$slink = defined('IN_ENANO_INSTALL') ? scriptPath.'/install.php?mode=css' : makeUrlNS('Special', 'CSS');
+		
+		$title = ( is_object($paths) ) ? $paths->page : 'Critical error';
+		
+		$headers = '<style type="text/css">div.pagenav { border-top: 1px solid #CCC; padding-top: 7px; margin-top: 10px; }</style>';
+		
+		$js_dynamic = '';
+		if ( defined('IN_ENANO_INSTALL') )
+		{
+			$js_dynamic .= '<script type="text/javascript" src="install.php?mode=langjs"></script>';
+		}
+		$js_dynamic .= '<script type="text/javascript">var title="'. $title .'"; var scriptPath="'.scriptPath.'"; var cdnPath="'.scriptPath.'"; var ENANO_SID=""; var AES_BITS='.AES_BITS.'; var AES_BLOCKSIZE=' . AES_BLOCKSIZE . '; var pagepass=\'\'; var ENANO_LANG_ID = 1; var enano_version = \'' . enano_version() . '\'; var msg_loading_component = \'Loading %component%...\';</script>';
+		
+		global $site_name, $site_desc;
+		$site_default_name = ( !empty($site_name) ) ? $site_name : 'Critical error';
+		$site_default_desc = ( !empty($site_desc) ) ? $site_desc : 'This site is experiencing a problem and cannot load.';
+		
+		$site_name_final = ( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_site_name') : $site_default_name;
+		$site_desc_final = ( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_site_desc') : $site_default_desc;
+		
+		// The rewritten template engine will process all required vars during the load_template stage instead of (cough) re-processing everything each time around.
+		$tpl_strings = Array(
+			'PAGE_NAME'=>$this_page,
+			'PAGE_URLNAME'=>'Null',
+			'SITE_NAME' => $site_name_final,
+			'USERNAME'=>'admin',
+			'SITE_DESC' => $site_desc_final,
+			'TOOLBAR'=>$tb,
+			'SCRIPTPATH'=>scriptPath,
+			'CONTENTPATH'=>contentPath,
+			'CDNPATH' => scriptPath,
+			'JS_HEADER' => '<script type="text/javascript" src="' . scriptPath . '/includes/clientside/static/enano-lib-basic.js"></script>',
+			'JS_FOOTER' => '',
+			'ADMIN_SID_QUES'=>$asq,
+			'ADMIN_SID_AMP'=>$asa,
+			'ADMIN_SID_AMP_HTML'=>'',
+			'ADDITIONAL_HEADERS'=>$this->additional_headers,
+			'SIDEBAR_EXTRA'=>'',
+			'COPYRIGHT'=>( defined('IN_ENANO_INSTALL') && is_object($lang) ) ? $lang->get('meta_enano_copyright') : ( defined('ENANO_CONFIG_FETCHED') ? getConfig('copyright_notice') : '' ),
+			'TOOLBAR_EXTRAS'=>'',
+			'REQUEST_URI'=>( isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '' ).$_SERVER['REQUEST_URI'],
+			'STYLE_LINK'=>$slink,
+			'LOGOUT_LINK'=>'',
+			'THEME_LINK'=>'',
+			'TEMPLATE_DIR'=>scriptPath.'/themes/'.$this->theme,
+			'THEME_ID'=>$this->theme,
+			'STYLE_ID'=>$this->style,
+			'JS_DYNAMIC_VARS'=>$js_dynamic,
+			'SIDEBAR_RIGHT'=>'',
+			'REPORT_URI' => '',
+			'URL_ABOUT_ENANO' => 'http://enanocms.org/',
+			'ENANO_VERSION' => enano_version()
+			);
+		$this->tpl_strings = array_merge($tpl_strings, $this->tpl_strings);
+		
+		$sidebar = ( is_array(@$sideinfo) ) ? $sideinfo : '';
+		if ( $sidebar != '' )
+		{
+			if ( isset($tplvars['sidebar_top']) )
+			{
+				$text = $this->makeParserText($tplvars['sidebar_top']);
+				$top = $text->run();
+			}
+			else
+			{
+				$top = '';
+			}
+			
+			$p = $this->makeParserText($tplvars['sidebar_section']);
+			$b = $this->makeParserText($tplvars['sidebar_button']);
+			$sidebar_text = '';
+			
+			foreach ( $sidebar as $title => $links )
+			{
+				$p->assign_vars(array(
+					'TITLE' => $title
+				));
+				// build content
+				$content = '';
+				foreach ( $links as $link_text => $url )
+				{
+					$b->assign_vars(array(
+						'HREF' => htmlspecialchars($url),
+						'FLAGS' => '',
+						'TEXT' => $link_text
+					));
+					$content .= $b->run();
+				}
+				$p->assign_vars(array(
+					'CONTENT' => $content
+				));
+				$sidebar_text .= $p->run();
+			}
+			
+			if ( isset($tplvars['sidebar_bottom']) )
+			{
+				$text = $this->makeParserText($tplvars['sidebar_bottom']);
+				$bottom = $text->run();
+			}
+			else
+			{
+				$bottom = '';
+			}
+			$sidebar = $top . $sidebar_text . $bottom;
+		}
+		$this->tpl_strings['SIDEBAR_LEFT'] = $sidebar;
+		
+		$this->tpl_bool['sidebar_left']  = ( $this->tpl_strings['SIDEBAR_LEFT']  != '') ? true : false;
+		$this->tpl_bool['sidebar_right'] = ( $this->tpl_strings['SIDEBAR_RIGHT'] != '') ? true : false;
+		$this->tpl_bool['right_sidebar'] = $this->tpl_bool['sidebar_right']; // backward compatibility
+		$this->tpl_bool['stupid_mode'] = true;
+	}
+	function header($simple = false) 
+	{
+		$filename = ( $simple ) ? 'simple-header.tpl' : 'header.tpl';
+		if ( !$this->no_headers )
+		{
+			echo $this->process_template($filename);
+		}
+	}
+	function footer($simple = false)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		if(!$this->no_headers) {
+			global $_starttime;
+			
+			$filename = ( $simple ) ? 'simple-footer.tpl' : 'footer.tpl';
+			$t = $this->process_template($filename);
+			
+			$f = microtime_float();
+			$f = $f - $_starttime;
+			$f = round($f, 4);
+			
+			if ( is_object($lang) )
+			{
+				$t_loc = $lang->get('page_msg_stats_gentime_short', array('time' => $f));
+				$t_loc_long = $lang->get('page_msg_stats_gentime_long', array('time' => $f));
+				$q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . $lang->get('page_msg_stats_sql', array('nq' => ( is_object($db) ? $db->num_queries : 'N/A' ))) . '</a>';
+				$dbg = $t_loc;
+				$dbg_long = $t_loc_long;
+				if ( $session->user_level >= USER_LEVEL_ADMIN )
+				{
+					$dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+					$dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+				}
+				$t = str_replace('[[EnanoPoweredLink]]', $lang->get('page_enano_powered', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
+				$t = str_replace('[[EnanoPoweredLinkLong]]', $lang->get('page_enano_powered_long', array('about_uri' => $this->tpl_strings['URL_ABOUT_ENANO'])), $t);
+			}
+			else
+			{
+				$t_loc = "Time: {$f}s";
+				$t_loc_long = "Generated in {$f}sec";
+				$q_loc = '<a href="' . $this->tpl_strings['REPORT_URI'] . '">' . ( is_object($db) ? "{$db->num_queries} SQL" : 'Queries: N/A' ) . '</a>';
+				$dbg = $t_loc;
+				$dbg_long = $t_loc_long;
+				if ( is_object($session) )
+				{
+					if ( $session->user_level >= USER_LEVEL_ADMIN )
+					{
+						$dbg .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+						$dbg_long .= "&nbsp;&nbsp;|&nbsp;&nbsp;$q_loc";
+					}
+				}
+				$t = str_replace('[[EnanoPoweredLink]]', 'Powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t);
+				$t = str_replace('[[EnanoPoweredLinkLong]]', 'Website engine powered by <a href="http://enanocms.org/" onclick="window.open(this.href); return false;">Enano</a>', $t);
+			}
+			
+			$t = str_replace('[[Stats]]', $dbg, $t);
+			$t = str_replace('[[StatsLong]]', $dbg_long, $t);
+			$t = str_replace('[[NumQueries]]', ( is_object($db) ? (string)$db->num_queries : '0' ), $t);
+			$t = str_replace('[[GenTime]]', (string)$f, $t);
+			$t = str_replace('[[NumQueriesLoc]]', $q_loc, $t);
+			$t = str_replace('[[GenTimeLoc]]', $t_loc, $t);
+			
+			if ( defined('ENANO_PROFILE') )
+			{
+				$t = str_replace('</body>', '<div id="profile" style="margin: 10px;">' . profiler_make_html() . '</div></body>', $t);
+			}
+			
+			echo $t;
+		}
+		else return '';
+	}
+	function getHeader()
+	{
+		if(!$this->no_headers) return $this->process_template('header.tpl');
+		else return '';
+	}
+	function getFooter()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if(!$this->no_headers) {
+			global $_starttime;
+			$f = microtime(true);
+			$f = $f - $_starttime;
+			$f = round($f, 4);
+			if(defined('IN_ENANO_INSTALL')) $nq = 'N/A';
+			else $nq = $db->num_queries;
+			if($nq == 0) $nq = 'N/A';
+			$dbg = 'Time: '.$f.'s  |  Queries: '.$nq;
+			if($nq == 0) $nq = 'N/A';
+			$t = $this->process_template('footer.tpl');
+			$t = str_replace('[[Stats]]', $dbg, $t);
+			return $t;
+		}
+		else return '';
+	}
+	
+	function process_template($file)
+	{
+		$compiled = $this->compile_template($file);
+		$result = eval($compiled);
+		return $result;
+	}
+	
+	function extract_vars($file) {
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if(!is_file(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file)) die('Cannot find '.$file.' file for style "'.$this->theme.'", exiting');
+		$text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$file);
+		preg_match_all('#<\!-- VAR ([A-z0-9_-]*) -->(.*?)<\!-- ENDVAR \\1 -->#is', $text, $matches);
+		$tplvars = Array();
+		for($i=0;$i<sizeof($matches[1]);$i++)
+		{
+			$tplvars[$matches[1][$i]] = $matches[2][$i];
+		}
+		return $tplvars;
+	}
+	function compile_template($text)
+	{
+		$text = file_get_contents(ENANO_ROOT . '/themes/'.$this->theme.'/'.$text);
+		return $this->compile_template_text_post(template_compiler_core($text));
+	}
+	
+	function compile_template_text($text)
+	{
+		return $this->compile_template_text_post(template_compiler_core($text));
+	}
+	
+	/**
+ 	* Post-processor for template code. Basically what this does is it localizes {lang:foo} blocks.
+ 	* @param string Mostly-processed TPL code
+ 	* @return string
+ 	*/
+	
+	function compile_template_text_post($text)
+	{
+		global $lang;
+		preg_match_all('/\{lang:([a-z0-9]+_[a-z0-9_]+)\}/', $text, $matches);
+		foreach ( $matches[1] as $i => $string_id )
+		{
+			if ( is_object(@$lang) )
+			{
+				$string = $lang->get($string_id);
+			}
+			else
+			{
+				$string = '[language not loaded]';
+			}
+			$string = str_replace('\\', '\\\\', $string);
+			$string = str_replace('\'', '\\\'', $string);
+			$text = str_replace_once($matches[0][$i], $string, $text);
+		}
+		return $text;
+	}
+	
+	/**
+ 	* Allows individual parsing of template files. Similar to phpBB but follows the spirit of object-oriented programming ;)
+ 	* Returns on object of class templateIndividual. Usage instructions can be found in the inline docs for that class.
+ 	* @param $filename the filename of the template to be parsed
+ 	* @return object
+ 	*/
+ 	
+	function makeParser($filename)
+	{
+		$filename = ENANO_ROOT.'/themes/'.$this->theme.'/'.$filename;
+		if(!file_exists($filename)) die('templateIndividual: file '.$filename.' does not exist');
+		$code = file_get_contents($filename);
+		$parser = new templateIndividualSafe($code, $this);
+		return $parser;
+	}
+	
+	/**
+ 	* Same as $template->makeParser(), but takes a string instead of a filename.
+ 	* @param $text the text to parse
+ 	* @return object
+ 	*/
+ 	
+	function makeParserText($code)
+	{
+		$parser = new templateIndividualSafe($code, $this);
+		return $parser;
+	}
+	
+	/**
+ 	* Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
+ 	* @param $vars array
+ 	*/
+	function assign_vars($vars, $_ignored = false)
+	{
+		if(is_array($this->tpl_strings))
+			$this->tpl_strings = array_merge($this->tpl_strings, $vars);
+		else
+			$this->tpl_strings = $vars;
+	}
+	
+	function get_theme_hook()
+	{
+		return '';
+	}
+ 	
 } // class template_nodb
 
 /**
@@ -3333,58 +3333,58 @@
  
 class templateIndividualSafe extends template_nodb
 {
-  var $tpl_strings, $tpl_bool, $tpl_code;
-  var $compiled = false;
-  /**
-   * Constructor.
-   */
-  function __construct($text, $parent)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    $this->tpl_code = $text;
-    $this->tpl_strings = $parent->tpl_strings;
-    $this->tpl_bool = $parent->tpl_bool;
-  }
-  /**
-   * PHP 4 constructor.
-   */
-  function templateIndividual($text)
-  {
-    $this->__construct($text);
-  }
-  /**
-   * Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
-   * @param $vars array
-   */
-  function assign_vars($vars, $_ignored = false)
-  {
-    if(is_array($this->tpl_strings))
-      $this->tpl_strings = array_merge($this->tpl_strings, $vars);
-    else
-      $this->tpl_strings = $vars;
-  }
-  /**
-   * Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
-   * @param $vars array
-   */
-  function assign_bool($vars)
-  {
-    $this->tpl_bool = array_merge($this->tpl_bool, $vars);
-  }
-  /**
-   * Compiles and executes the template code.
-   * @return string
-   */
-  function run()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    if(!$this->compiled)
-    {
-      $this->tpl_code = $this->compile_template_text($this->tpl_code);
-      $this->compiled = true;
-    }
-    return eval($this->tpl_code);
-  }
+	var $tpl_strings, $tpl_bool, $tpl_code;
+	var $compiled = false;
+	/**
+ 	* Constructor.
+ 	*/
+	function __construct($text, $parent)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		$this->tpl_code = $text;
+		$this->tpl_strings = $parent->tpl_strings;
+		$this->tpl_bool = $parent->tpl_bool;
+	}
+	/**
+ 	* PHP 4 constructor.
+ 	*/
+	function templateIndividual($text)
+	{
+		$this->__construct($text);
+	}
+	/**
+ 	* Assigns an array of string values to the template. Strings can be accessed from the template by inserting {KEY_NAME} in the template file.
+ 	* @param $vars array
+ 	*/
+	function assign_vars($vars, $_ignored = false)
+	{
+		if(is_array($this->tpl_strings))
+			$this->tpl_strings = array_merge($this->tpl_strings, $vars);
+		else
+			$this->tpl_strings = $vars;
+	}
+	/**
+ 	* Assigns an array of boolean values to the template. These can be used for <!-- IF ... --> statements.
+ 	* @param $vars array
+ 	*/
+	function assign_bool($vars)
+	{
+		$this->tpl_bool = array_merge($this->tpl_bool, $vars);
+	}
+	/**
+ 	* Compiles and executes the template code.
+ 	* @return string
+ 	*/
+	function run()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		if(!$this->compiled)
+		{
+			$this->tpl_code = $this->compile_template_text($this->tpl_code);
+			$this->compiled = true;
+		}
+		return eval($this->tpl_code);
+	}
 }
 
 ?>
--- a/includes/wikiengine/Tables.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/wikiengine/Tables.php	Sun Mar 28 23:10:46 2010 -0400
@@ -30,14 +30,14 @@
  
 function process_tables( $text )
 {
-  // include some globals, do some parser stuff that would normally be done in the parent parser function
-  global $mStripState;
-  $x =& $mStripState;
-  
-  // parse the text
-  $text = doTableStuff($text);
+	// include some globals, do some parser stuff that would normally be done in the parent parser function
+	global $mStripState;
+	$x =& $mStripState;
+	
+	// parse the text
+	$text = doTableStuff($text);
 
-  return $text;
+	return $text;
 }
 
 /**
@@ -48,131 +48,131 @@
  * @access private
  */
 function doTableStuff( $t ) {
-  
-  $t = explode ( "\n" , $t ) ;
-  $td = array () ; # Is currently a td tag open?
-  $ltd = array () ; # Was it TD or TH?
-  $tr = array () ; # Is currently a tr tag open?
-  $ltr = array () ; # tr attributes
-  $has_opened_tr = array(); # Did this table open a <tr> element?
-  $indent_level = 0; # indent level of the table
-  foreach ( $t AS $k => $x )
-  {
-    $x = trim ( $x ) ;
-    $fc = substr ( $x , 0 , 1 ) ;
-    if ( preg_match( '/^(:*)\{\|(.*)$/', $x, $matches ) ) {
-      $indent_level = strlen( $matches[1] );
+	
+	$t = explode ( "\n" , $t ) ;
+	$td = array () ; # Is currently a td tag open?
+	$ltd = array () ; # Was it TD or TH?
+	$tr = array () ; # Is currently a tr tag open?
+	$ltr = array () ; # tr attributes
+	$has_opened_tr = array(); # Did this table open a <tr> element?
+	$indent_level = 0; # indent level of the table
+	foreach ( $t AS $k => $x )
+	{
+		$x = trim ( $x ) ;
+		$fc = substr ( $x , 0 , 1 ) ;
+		if ( preg_match( '/^(:*)\{\|(.*)$/', $x, $matches ) ) {
+			$indent_level = strlen( $matches[1] );
 
-      $attributes = unstripForHTML( $matches[2] );
+			$attributes = unstripForHTML( $matches[2] );
 
-      $t[$k] = str_repeat( '<dl><dd>', $indent_level ) .
-        '<table' . fixTagAttributes( $attributes, 'table' ) . '>' ;
-      array_push ( $td , false ) ;
-      array_push ( $ltd , '' ) ;
-      array_push ( $tr , false ) ;
-      array_push ( $ltr , '' ) ;
-      array_push ( $has_opened_tr, false );
-    }
-    else if ( count ( $td ) == 0 ) { } # Don't do any of the following
-    else if ( '|}' == substr ( $x , 0 , 2 ) ) {
-      $z = "</table>" . substr ( $x , 2);
-      $l = array_pop ( $ltd ) ;
-      if ( !array_pop ( $has_opened_tr ) ) $z = "<tr><td></td></tr>" . $z ;
-      if ( array_pop ( $tr ) ) $z = '</tr>' . $z ;
-      if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
-      array_pop ( $ltr ) ;
-      $t[$k] = $z . str_repeat( '</dd></dl>', $indent_level );
-    }
-    else if ( '|-' == substr ( $x , 0 , 2 ) ) { # Allows for |---------------
-      $x = substr ( $x , 1 ) ;
-      while ( $x != '' && substr ( $x , 0 , 1 ) == '-' ) $x = substr ( $x , 1 ) ;
-      $z = '' ;
-      $l = array_pop ( $ltd ) ;
-      array_pop ( $has_opened_tr );
-      array_push ( $has_opened_tr , true ) ;
-      if ( array_pop ( $tr ) ) $z = '</tr>' . $z ;
-      if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
-      array_pop ( $ltr ) ;
-      $t[$k] = $z ;
-      array_push ( $tr , false ) ;
-      array_push ( $td , false ) ;
-      array_push ( $ltd , '' ) ;
-      $attributes = unstripForHTML( $x );
-      array_push ( $ltr , fixTagAttributes( $attributes, 'tr' ) ) ;
-    }
-    else if ( '|' == $fc || '!' == $fc || '|+' == substr ( $x , 0 , 2 ) ) { # Caption
-      # $x is a table row
-      if ( '|+' == substr ( $x , 0 , 2 ) ) {
-        $fc = '+' ;
-        $x = substr ( $x , 1 ) ;
-      }
-      $after = substr ( $x , 1 ) ;
-      if ( $fc == '!' ) $after = str_replace ( '!!' , '||' , $after ) ;
+			$t[$k] = str_repeat( '<dl><dd>', $indent_level ) .
+				'<table' . fixTagAttributes( $attributes, 'table' ) . '>' ;
+			array_push ( $td , false ) ;
+			array_push ( $ltd , '' ) ;
+			array_push ( $tr , false ) ;
+			array_push ( $ltr , '' ) ;
+			array_push ( $has_opened_tr, false );
+		}
+		else if ( count ( $td ) == 0 ) { } # Don't do any of the following
+		else if ( '|}' == substr ( $x , 0 , 2 ) ) {
+			$z = "</table>" . substr ( $x , 2);
+			$l = array_pop ( $ltd ) ;
+			if ( !array_pop ( $has_opened_tr ) ) $z = "<tr><td></td></tr>" . $z ;
+			if ( array_pop ( $tr ) ) $z = '</tr>' . $z ;
+			if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
+			array_pop ( $ltr ) ;
+			$t[$k] = $z . str_repeat( '</dd></dl>', $indent_level );
+		}
+		else if ( '|-' == substr ( $x , 0 , 2 ) ) { # Allows for |---------------
+			$x = substr ( $x , 1 ) ;
+			while ( $x != '' && substr ( $x , 0 , 1 ) == '-' ) $x = substr ( $x , 1 ) ;
+			$z = '' ;
+			$l = array_pop ( $ltd ) ;
+			array_pop ( $has_opened_tr );
+			array_push ( $has_opened_tr , true ) ;
+			if ( array_pop ( $tr ) ) $z = '</tr>' . $z ;
+			if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
+			array_pop ( $ltr ) ;
+			$t[$k] = $z ;
+			array_push ( $tr , false ) ;
+			array_push ( $td , false ) ;
+			array_push ( $ltd , '' ) ;
+			$attributes = unstripForHTML( $x );
+			array_push ( $ltr , fixTagAttributes( $attributes, 'tr' ) ) ;
+		}
+		else if ( '|' == $fc || '!' == $fc || '|+' == substr ( $x , 0 , 2 ) ) { # Caption
+			# $x is a table row
+			if ( '|+' == substr ( $x , 0 , 2 ) ) {
+				$fc = '+' ;
+				$x = substr ( $x , 1 ) ;
+			}
+			$after = substr ( $x , 1 ) ;
+			if ( $fc == '!' ) $after = str_replace ( '!!' , '||' , $after ) ;
 
-      // Split up multiple cells on the same line.
-      // FIXME: This can result in improper nesting of tags processed
-      // by earlier parser steps, but should avoid splitting up eg
-      // attribute values containing literal "||".
-      $after = wfExplodeMarkup( '||', $after );
+			// Split up multiple cells on the same line.
+			// FIXME: This can result in improper nesting of tags processed
+			// by earlier parser steps, but should avoid splitting up eg
+			// attribute values containing literal "||".
+			$after = wfExplodeMarkup( '||', $after );
 
-      $t[$k] = '' ;
+			$t[$k] = '' ;
 
-      # Loop through each table cell
-      foreach ( $after AS $theline )
-      {
-        $z = '' ;
-        if ( $fc != '+' )
-        {
-          $tra = array_pop ( $ltr ) ;
-          if ( !array_pop ( $tr ) ) $z = '<tr'.$tra.">\n" ;
-          array_push ( $tr , true ) ;
-          array_push ( $ltr , '' ) ;
-          array_pop ( $has_opened_tr );
-          array_push ( $has_opened_tr , true ) ;
-        }
+			# Loop through each table cell
+			foreach ( $after AS $theline )
+			{
+				$z = '' ;
+				if ( $fc != '+' )
+				{
+					$tra = array_pop ( $ltr ) ;
+					if ( !array_pop ( $tr ) ) $z = '<tr'.$tra.">\n" ;
+					array_push ( $tr , true ) ;
+					array_push ( $ltr , '' ) ;
+					array_pop ( $has_opened_tr );
+					array_push ( $has_opened_tr , true ) ;
+				}
 
-        $l = array_pop ( $ltd ) ;
-        if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
-        if ( $fc == '|' ) $l = 'td' ;
-        else if ( $fc == '!' ) $l = 'th' ;
-        else if ( $fc == '+' ) $l = 'caption' ;
-        else $l = '' ;
-        array_push ( $ltd , $l ) ;
+				$l = array_pop ( $ltd ) ;
+				if ( array_pop ( $td ) ) $z = '</'.$l.'>' . $z ;
+				if ( $fc == '|' ) $l = 'td' ;
+				else if ( $fc == '!' ) $l = 'th' ;
+				else if ( $fc == '+' ) $l = 'caption' ;
+				else $l = '' ;
+				array_push ( $ltd , $l ) ;
 
-        # Cell parameters
-        $y = explode ( '|' , $theline , 2 ) ;
-        # Note that a '|' inside an invalid link should not
-        # be mistaken as delimiting cell parameters
-        if ( strpos( $y[0], '[[' ) !== false ) {
-          $y = array ($theline);
-        }
-        if ( count ( $y ) == 1 )
-          $y = "{$z}<{$l}>{$y[0]}" ;
-        else {
-          $attributes = unstripForHTML( $y[0] );
-          $y = "{$z}<{$l}".fixTagAttributes($attributes, $l).">{$y[1]}" ;
-        }
-        $t[$k] .= $y ;
-        array_push ( $td , true ) ;
-      }
-    }
-  }
+				# Cell parameters
+				$y = explode ( '|' , $theline , 2 ) ;
+				# Note that a '|' inside an invalid link should not
+				# be mistaken as delimiting cell parameters
+				if ( strpos( $y[0], '[[' ) !== false ) {
+					$y = array ($theline);
+				}
+				if ( count ( $y ) == 1 )
+					$y = "{$z}<{$l}>{$y[0]}" ;
+				else {
+					$attributes = unstripForHTML( $y[0] );
+					$y = "{$z}<{$l}".fixTagAttributes($attributes, $l).">{$y[1]}" ;
+				}
+				$t[$k] .= $y ;
+				array_push ( $td , true ) ;
+			}
+		}
+	}
 
-  # Closing open td, tr && table
-  while ( count ( $td ) > 0 )
-  {
-    $l = array_pop ( $ltd ) ;
-    if ( array_pop ( $td ) ) $t[] = '</td>' ;
-    if ( array_pop ( $tr ) ) $t[] = '</tr>' ;
-    if ( !array_pop ( $has_opened_tr ) ) $t[] = "<tr><td></td></tr>" ;
-    $t[] = '</table></_paragraph_bypass>' ;
-  }
+	# Closing open td, tr && table
+	while ( count ( $td ) > 0 )
+	{
+		$l = array_pop ( $ltd ) ;
+		if ( array_pop ( $td ) ) $t[] = '</td>' ;
+		if ( array_pop ( $tr ) ) $t[] = '</tr>' ;
+		if ( !array_pop ( $has_opened_tr ) ) $t[] = "<tr><td></td></tr>" ;
+		$t[] = '</table></_paragraph_bypass>' ;
+	}
 
-  $t = implode ( "\n" , $t ) ;
-  
-  # special case: don't return empty table
-  if($t == "<table>\n<tr><td></td></tr>\n</table>")
-    $t = '';
-  return $t ;
+	$t = implode ( "\n" , $t ) ;
+	
+	# special case: don't return empty table
+	if($t == "<table>\n<tr><td></td></tr>\n</table>")
+		$t = '';
+	return $t ;
 }
 
--- a/includes/wikiengine/TagSanitizer.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/wikiengine/TagSanitizer.php	Sun Mar 28 23:10:46 2010 -0400
@@ -25,18 +25,18 @@
  |(&)/x' );
 
 define( 'MW_ATTRIBS_REGEX',
-  "/(?:^|$space)($attrib+)
-    ($space*=$space*
-    (?:
-     # The attribute value: quoted or alone
-      \"([^<\"]*)\"
-     | '([^<']*)'
-     |  ([a-zA-Z0-9!#$%&()*,\\-.\\/:;<>?@[\\]^_`{|}~]+)
-     |  (\#[0-9a-fA-F]+) # Technically wrong, but lots of
-               # colors are specified like this.
-               # We'll be normalizing it.
-    )
-     )?(?=$space|\$)/sx" );
+	"/(?:^|$space)($attrib+)
+		($space*=$space*
+		(?:
+ 		# The attribute value: quoted or alone
+			\"([^<\"]*)\"
+ 		| '([^<']*)'
+ 		|  ([a-zA-Z0-9!#$%&()*,\\-.\\/:;<>?@[\\]^_`{|}~]+)
+ 		|  (\#[0-9a-fA-F]+) # Technically wrong, but lots of
+ 							# colors are specified like this.
+ 							# We'll be normalizing it.
+		)
+ 		)?(?=$space|\$)/sx" );
 
 /**
  * Take a tag soup fragment listing an HTML element's attributes
@@ -58,21 +58,21 @@
  * @return string
  */
 function fixTagAttributes( $text, $element ) {
-  if( trim( $text ) == '' ) {
-    return '';
-  }
-  
-  $stripped = validateTagAttributes(
-    decodeTagAttributes( $text ), $element );
-  
-  $attribs = array();
-  foreach( $stripped as $attribute => $value ) {
-    $encAttribute = htmlspecialchars( $attribute );
-    $encValue = safeEncodeAttribute( $value );
-    
-    $attribs[] = "$encAttribute=".'"'."$encValue".'"'.""; // "
-  }
-  return count( $attribs ) ? ' ' . implode( ' ', $attribs ) : '';
+	if( trim( $text ) == '' ) {
+		return '';
+	}
+	
+	$stripped = validateTagAttributes(
+		decodeTagAttributes( $text ), $element );
+	
+	$attribs = array();
+	foreach( $stripped as $attribute => $value ) {
+		$encAttribute = htmlspecialchars( $attribute );
+		$encValue = safeEncodeAttribute( $value );
+		
+		$attribs[] = "$encAttribute=".'"'."$encValue".'"'.""; // "
+	}
+	return count( $attribs ) ? ' ' . implode( ' ', $attribs ) : '';
 }
 
 /**
@@ -82,25 +82,25 @@
  * @return HTML-encoded text fragment
  */
 function safeEncodeAttribute( $text ) {
-  $encValue= encodeAttribute( $text );
-  
-  # Templates and links may be expanded in later parsing,
-  # creating invalid or dangerous output. Suppress this.
-  $encValue = strtr( $encValue, array(
-    '<'    => '&lt;',   // This should never happen,
-    '>'    => '&gt;',   // we've received invalid input
-    '"'    => '&quot;', // which should have been escaped.
-    '{'    => '&#123;',
-    '['    => '&#91;',
-    "''"   => '&#39;&#39;',
-    'ISBN' => '&#73;SBN',
-    'RFC'  => '&#82;FC',
-    'PMID' => '&#80;MID',
-    '|'    => '&#124;',
-    '__'   => '&#95;_',
-  ) );
+	$encValue= encodeAttribute( $text );
+	
+	# Templates and links may be expanded in later parsing,
+	# creating invalid or dangerous output. Suppress this.
+	$encValue = strtr( $encValue, array(
+		'<'    => '&lt;',   // This should never happen,
+		'>'    => '&gt;',   // we've received invalid input
+		'"'    => '&quot;', // which should have been escaped.
+		'{'    => '&#123;',
+		'['    => '&#91;',
+		"''"   => '&#39;&#39;',
+		'ISBN' => '&#73;SBN',
+		'RFC'  => '&#82;FC',
+		'PMID' => '&#80;MID',
+		'|'    => '&#124;',
+		'__'   => '&#95;_',
+	) );
 
-  return $encValue;
+	return $encValue;
 }
 
 /**
@@ -109,45 +109,45 @@
  * @return HTML-encoded text fragment
  */
 function encodeAttribute( $text ) {
-  
-  // In Enano 1.0.3, added this cheapo hack to keep ampersands
-  // from being double-sanitized. Thanks to markybob from #deluge.
-  
-  // htmlspecialchars() the "manual" way
-  $encValue = strtr( $text, array(
-    '&amp;'  => '&',
-    '&quot;' => '"',
-    '&lt;'   => '<',
-    '&gt;'   => '>',
-    '&#039;' => "'"
-  ) );
-  
-  $encValue = strtr( $text, array(
-    '&' => '&amp;',
-    '"' => '&quot;',
-    '<' => '&lt;',
-    '>' => '&gt;',
-    "'" => '&#039;'
-  ) );
-  
-  
-  // Whitespace is normalized during attribute decoding,
-  // so if we've been passed non-spaces we must encode them
-  // ahead of time or they won't be preserved.
-  $encValue = strtr( $encValue, array(
-    "\n" => '&#10;',
-    "\r" => '&#13;',
-    "\t" => '&#9;',
-  ) );
-  
-  return $encValue;
+	
+	// In Enano 1.0.3, added this cheapo hack to keep ampersands
+	// from being double-sanitized. Thanks to markybob from #deluge.
+	
+	// htmlspecialchars() the "manual" way
+	$encValue = strtr( $text, array(
+		'&amp;'  => '&',
+		'&quot;' => '"',
+		'&lt;'   => '<',
+		'&gt;'   => '>',
+		'&#039;' => "'"
+	) );
+	
+	$encValue = strtr( $text, array(
+		'&' => '&amp;',
+		'"' => '&quot;',
+		'<' => '&lt;',
+		'>' => '&gt;',
+		"'" => '&#039;'
+	) );
+	
+	
+	// Whitespace is normalized during attribute decoding,
+	// so if we've been passed non-spaces we must encode them
+	// ahead of time or they won't be preserved.
+	$encValue = strtr( $encValue, array(
+		"\n" => '&#10;',
+		"\r" => '&#13;',
+		"\t" => '&#9;',
+	) );
+	
+	return $encValue;
 }
 
 function unstripForHTML( $text ) {
-  global $mStripState;
-  $text = unstrip( $text, $mStripState );
-  $text = unstripNoWiki( $text, $mStripState );
-  return $text;
+	global $mStripState;
+	$text = unstrip( $text, $mStripState );
+	$text = unstripNoWiki( $text, $mStripState );
+	return $text;
 }
 
 /**
@@ -156,14 +156,14 @@
  * @private
  */
 function unstripNoWiki( $text, &$state ) {
-  if ( !isset( $state['nowiki'] ) ) {
-    return $text;
-  }
+	if ( !isset( $state['nowiki'] ) ) {
+		return $text;
+	}
 
-  # TODO: good candidate for FSS
-  $text = strtr( $text, $state['nowiki'] );
-  
-  return $text;
+	# TODO: good candidate for FSS
+	$text = strtr( $text, $state['nowiki'] );
+	
+	return $text;
 }
 
 /**
@@ -181,30 +181,30 @@
  * @todo Check for unique id attribute :P
  */
 function validateTagAttributes( $attribs, $element ) {
-  $whitelist = array_flip( attributeWhitelist( $element ) );
-  $out = array();
-  foreach( $attribs as $attribute => $value ) {
-    if( !isset( $whitelist[$attribute] ) ) {
-      continue;
-    }
-    # Strip javascript "expression" from stylesheets.
-    # http://msdn.microsoft.com/workshop/author/dhtml/overview/recalc.asp
-    if( $attribute == 'style' ) {
-      $value = checkCss( $value );
-      if( $value === false ) {
-        # haxx0r
-        continue;
-      }
-    }
+	$whitelist = array_flip( attributeWhitelist( $element ) );
+	$out = array();
+	foreach( $attribs as $attribute => $value ) {
+		if( !isset( $whitelist[$attribute] ) ) {
+			continue;
+		}
+		# Strip javascript "expression" from stylesheets.
+		# http://msdn.microsoft.com/workshop/author/dhtml/overview/recalc.asp
+		if( $attribute == 'style' ) {
+			$value = checkCss( $value );
+			if( $value === false ) {
+				# haxx0r
+				continue;
+			}
+		}
 
-    if ( $attribute === 'id' )
-      $value = escapeId( $value );
+		if ( $attribute === 'id' )
+			$value = escapeId( $value );
 
-    // If this attribute was previously set, override it.
-    // Output should only have one attribute of each name.
-    $out[$attribute] = $value;
-  }
-  return $out;
+		// If this attribute was previously set, override it.
+		// Output should only have one attribute of each name.
+		$out[$attribute] = $value;
+	}
+	return $out;
 }
 
 /**
@@ -217,23 +217,23 @@
  * @return mixed
  */
 function checkCss( $value ) {
-  $stripped = decodeCharReferences( $value );
+	$stripped = decodeCharReferences( $value );
 
-  // Remove any comments; IE gets token splitting wrong
-  $stripped = preg_replace( '!/\\*.*?\\*/!S', '', $stripped );
-  $value = $stripped;
+	// Remove any comments; IE gets token splitting wrong
+	$stripped = preg_replace( '!/\\*.*?\\*/!S', '', $stripped );
+	$value = $stripped;
 
-  // ... and continue checks
-  $stripped = preg_replace( '!\\\\([0-9A-Fa-f]{1,6})[ \\n\\r\\t\\f]?!e',
-    'codepointToUtf8(hexdec("$1"))', $stripped );
-  $stripped = str_replace( '\\', '', $stripped );
-  if( preg_match( '/(expression|tps*:\/\/|url\\s*\().*/is',
-      $stripped ) ) {
-    # haxx0r
-    return false;
-  }
-  
-  return $value;
+	// ... and continue checks
+	$stripped = preg_replace( '!\\\\([0-9A-Fa-f]{1,6})[ \\n\\r\\t\\f]?!e',
+		'codepointToUtf8(hexdec("$1"))', $stripped );
+	$stripped = str_replace( '\\', '', $stripped );
+	if( preg_match( '/(expression|tps*:\/\/|url\\s*\().*/is',
+			$stripped ) ) {
+		# haxx0r
+		return false;
+	}
+	
+	return $value;
 }
 
 /**
@@ -246,10 +246,10 @@
  * @static
  */
 function decodeCharReferences( $text ) {
-  return preg_replace_callback(
-    MW_CHAR_REFS_REGEX,
-    'decodeCharReferencesCallback',
-    $text );
+	return preg_replace_callback(
+		MW_CHAR_REFS_REGEX,
+		'decodeCharReferencesCallback',
+		$text );
 }
 
 /**
@@ -260,13 +260,13 @@
  * @return array
  */
 function attributeWhitelist( $element ) {
-  static $list;
-  if( !isset( $list ) ) {
-    $list = setupAttributeWhitelist();
-  }
-  return isset( $list[$element] )
-    ? $list[$element]
-    : array();
+	static $list;
+	if( !isset( $list ) ) {
+		$list = setupAttributeWhitelist();
+	}
+	return isset( $list[$element] )
+		? $list[$element]
+		: array();
 }
 
 /**
@@ -274,165 +274,165 @@
  * @return array
  */
 function setupAttributeWhitelist() {
-  global $db, $session, $paths, $template, $plugins;
-  $common = array( 'id', 'class', 'lang', 'dir', 'title', 'style' );
-  $block = array_merge( $common, array( 'align' ) );
-  $tablealign = array( 'align', 'char', 'charoff', 'valign' );
-  $tablecell = array( 'abbr',
-                      'axis',
-                      'headers',
-                      'scope',
-                      'rowspan',
-                      'colspan',
-                      'nowrap', # deprecated
-                      'width',  # deprecated
-                      'height', # deprecated
-                      'bgcolor' # deprecated
-                      );
+	global $db, $session, $paths, $template, $plugins;
+	$common = array( 'id', 'class', 'lang', 'dir', 'title', 'style' );
+	$block = array_merge( $common, array( 'align' ) );
+	$tablealign = array( 'align', 'char', 'charoff', 'valign' );
+	$tablecell = array( 'abbr',
+											'axis',
+											'headers',
+											'scope',
+											'rowspan',
+											'colspan',
+											'nowrap', # deprecated
+											'width',  # deprecated
+											'height', # deprecated
+											'bgcolor' # deprecated
+											);
 
-  # Numbers refer to sections in HTML 4.01 standard describing the element.
-  # See: http://www.w3.org/TR/html4/
-  $whitelist = array (
-    # 7.5.4
-    'div'        => $block,
-    'center'     => $common, # deprecated
-    'span'       => $block, # ??
+	# Numbers refer to sections in HTML 4.01 standard describing the element.
+	# See: http://www.w3.org/TR/html4/
+	$whitelist = array (
+		# 7.5.4
+		'div'        => $block,
+		'center'     => $common, # deprecated
+		'span'       => $block, # ??
 
-    # 7.5.5
-    'h1'         => $block,
-    'h2'         => $block,
-    'h3'         => $block,
-    'h4'         => $block,
-    'h5'         => $block,
-    'h6'         => $block,
+		# 7.5.5
+		'h1'         => $block,
+		'h2'         => $block,
+		'h3'         => $block,
+		'h4'         => $block,
+		'h5'         => $block,
+		'h6'         => $block,
 
-    # 7.5.6
-    # address
+		# 7.5.6
+		# address
 
-    # 8.2.4
-    # bdo
+		# 8.2.4
+		# bdo
 
-    # 9.2.1
-    'em'         => $common,
-    'strong'     => $common,
-    'cite'       => $common,
-    # dfn
-    'code'       => $common,
-    # samp
-    # kbd
-    'var'        => $common,
-    # abbr
-    # acronym
+		# 9.2.1
+		'em'         => $common,
+		'strong'     => $common,
+		'cite'       => $common,
+		# dfn
+		'code'       => $common,
+		# samp
+		# kbd
+		'var'        => $common,
+		# abbr
+		# acronym
 
-    # 9.2.2
-    'blockquote' => array_merge( $common, array( 'cite' ) ),
-    # q
+		# 9.2.2
+		'blockquote' => array_merge( $common, array( 'cite' ) ),
+		# q
 
-    # 9.2.3
-    'sub'        => $common,
-    'sup'        => $common,
+		# 9.2.3
+		'sub'        => $common,
+		'sup'        => $common,
 
-    # 9.3.1
-    'p'          => $block,
+		# 9.3.1
+		'p'          => $block,
 
-    # 9.3.2
-    'br'         => array( 'id', 'class', 'title', 'style', 'clear' ),
+		# 9.3.2
+		'br'         => array( 'id', 'class', 'title', 'style', 'clear' ),
 
-    # 9.3.4
-    'pre'        => array_merge( $common, array( 'width' ) ),
+		# 9.3.4
+		'pre'        => array_merge( $common, array( 'width' ) ),
 
-    # 9.4
-    'ins'        => array_merge( $common, array( 'cite', 'datetime' ) ),
-    'del'        => array_merge( $common, array( 'cite', 'datetime' ) ),
+		# 9.4
+		'ins'        => array_merge( $common, array( 'cite', 'datetime' ) ),
+		'del'        => array_merge( $common, array( 'cite', 'datetime' ) ),
 
-    # 10.2
-    'ul'         => array_merge( $common, array( 'type' ) ),
-    'ol'         => array_merge( $common, array( 'type', 'start' ) ),
-    'li'         => array_merge( $common, array( 'type', 'value' ) ),
+		# 10.2
+		'ul'         => array_merge( $common, array( 'type' ) ),
+		'ol'         => array_merge( $common, array( 'type', 'start' ) ),
+		'li'         => array_merge( $common, array( 'type', 'value' ) ),
 
-    # 10.3
-    'dl'         => $common,
-    'dd'         => $common,
-    'dt'         => $common,
+		# 10.3
+		'dl'         => $common,
+		'dd'         => $common,
+		'dt'         => $common,
 
-    # 11.2.1
-    'table'      => array_merge( $common,
-              array( 'summary', 'width', 'border', 'frame',
-                  'rules', 'cellspacing', 'cellpadding',
-                  'align', 'bgcolor',
-              ) ),
+		# 11.2.1
+		'table'      => array_merge( $common,
+							array( 'summary', 'width', 'border', 'frame',
+									'rules', 'cellspacing', 'cellpadding',
+									'align', 'bgcolor',
+							) ),
 
-    # 11.2.2
-    'caption'    => array_merge( $common, array( 'align' ) ),
+		# 11.2.2
+		'caption'    => array_merge( $common, array( 'align' ) ),
 
-    # 11.2.3
-    'thead'      => array_merge( $common, $tablealign ),
-    'tfoot'      => array_merge( $common, $tablealign ),
-    'tbody'      => array_merge( $common, $tablealign ),
+		# 11.2.3
+		'thead'      => array_merge( $common, $tablealign ),
+		'tfoot'      => array_merge( $common, $tablealign ),
+		'tbody'      => array_merge( $common, $tablealign ),
 
-    # 11.2.4
-    'colgroup'   => array_merge( $common, array( 'span', 'width' ), $tablealign ),
-    'col'        => array_merge( $common, array( 'span', 'width' ), $tablealign ),
+		# 11.2.4
+		'colgroup'   => array_merge( $common, array( 'span', 'width' ), $tablealign ),
+		'col'        => array_merge( $common, array( 'span', 'width' ), $tablealign ),
 
-    # 11.2.5
-    'tr'         => array_merge( $common, array( 'bgcolor' ), $tablealign ),
+		# 11.2.5
+		'tr'         => array_merge( $common, array( 'bgcolor' ), $tablealign ),
 
-    # 11.2.6
-    'td'         => array_merge( $common, $tablecell, $tablealign ),
-    'th'         => array_merge( $common, $tablecell, $tablealign ),
-    
-    # 12.2
-    # added by dan
-    'a'          => array_merge( $common, array( 'href', 'name' ) ),
-    
-    # 13.2
-    # added by dan
-    'img'        => array_merge( $common, array( 'src', 'width', 'height', 'alt' ) ),
+		# 11.2.6
+		'td'         => array_merge( $common, $tablecell, $tablealign ),
+		'th'         => array_merge( $common, $tablecell, $tablealign ),
+		
+		# 12.2
+		# added by dan
+		'a'          => array_merge( $common, array( 'href', 'name' ) ),
+		
+		# 13.2
+		# added by dan
+		'img'        => array_merge( $common, array( 'src', 'width', 'height', 'alt' ) ),
 
-    # 15.2.1
-    'tt'         => $common,
-    'b'          => $common,
-    'i'          => $common,
-    'big'        => $common,
-    'small'      => $common,
-    'strike'     => $common,
-    's'          => $common,
-    'u'          => $common,
+		# 15.2.1
+		'tt'         => $common,
+		'b'          => $common,
+		'i'          => $common,
+		'big'        => $common,
+		'small'      => $common,
+		'strike'     => $common,
+		's'          => $common,
+		'u'          => $common,
 
-    # 15.2.2
-    'font'       => array_merge( $common, array( 'size', 'color', 'face' ) ),
-    # basefont
+		# 15.2.2
+		'font'       => array_merge( $common, array( 'size', 'color', 'face' ) ),
+		# basefont
 
-    # 15.3
-    'hr'         => array_merge( $common, array( 'noshade', 'size', 'width' ) ),
+		# 15.3
+		'hr'         => array_merge( $common, array( 'noshade', 'size', 'width' ) ),
 
-    # XHTML Ruby annotation text module, simple ruby only.
-    # http://www.w3c.org/TR/ruby/
-    'ruby'       => $common,
-    # rbc
-    # rtc
-    'rb'         => $common,
-    'rt'         => $common, #array_merge( $common, array( 'rbspan' ) ),
-    'rp'         => $common,
-    
-    # For compatibility with the XHTML parser.
-    'nowiki'     => array(),
-    'noinclude'  => array(),
-    'nodisplay'  => array(),
-    'lang'       => array('code'),
-    
-    # XHTML stuff
-    'acronym'    => $common
-    );
-  
-  // custom tags can be added by plugins
-  $code = $plugins->setHook('html_attribute_whitelist');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  return $whitelist;
+		# XHTML Ruby annotation text module, simple ruby only.
+		# http://www.w3c.org/TR/ruby/
+		'ruby'       => $common,
+		# rbc
+		# rtc
+		'rb'         => $common,
+		'rt'         => $common, #array_merge( $common, array( 'rbspan' ) ),
+		'rp'         => $common,
+		
+		# For compatibility with the XHTML parser.
+		'nowiki'     => array(),
+		'noinclude'  => array(),
+		'nodisplay'  => array(),
+		'lang'       => array('code'),
+		
+		# XHTML stuff
+		'acronym'    => $common
+		);
+	
+	// custom tags can be added by plugins
+	$code = $plugins->setHook('html_attribute_whitelist');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	return $whitelist;
 }
 
 /**
@@ -452,14 +452,14 @@
  * @return string
  */
 function escapeId( $id ) {
-  static $replace = array(
-    '%3A' => ':',
-    '%' => '.'
-  );
+	static $replace = array(
+		'%3A' => ':',
+		'%' => '.'
+	);
 
-  $id = urlencode( decodeCharReferences( strtr( $id, ' ', '_' ) ) );
+	$id = urlencode( decodeCharReferences( strtr( $id, ' ', '_' ) ) );
 
-  return str_replace( array_keys( $replace ), array_values( $replace ), $id );
+	return str_replace( array_keys( $replace ), array_values( $replace ), $id );
 }
 
 /**
@@ -470,32 +470,32 @@
  * @return array
  */
 function wfExplodeMarkup( $separator, $text ) {
-  $placeholder = "\x00";
-  
-  // Just in case...
-  $text = str_replace( $placeholder, '', $text );
-  
-  // Trim stuff
-  $replacer = new ReplacerCallback( $separator, $placeholder );
-  $cleaned = preg_replace_callback( '/(<.*?>)/', array( $replacer, 'go' ), $text );
-  
-  $items = explode( $separator, $cleaned );
-  foreach( $items as $i => $str ) {
-    $items[$i] = str_replace( $placeholder, $separator, $str );
-  }
-  
-  return $items;
+	$placeholder = "\x00";
+	
+	// Just in case...
+	$text = str_replace( $placeholder, '', $text );
+	
+	// Trim stuff
+	$replacer = new ReplacerCallback( $separator, $placeholder );
+	$cleaned = preg_replace_callback( '/(<.*?>)/', array( $replacer, 'go' ), $text );
+	
+	$items = explode( $separator, $cleaned );
+	foreach( $items as $i => $str ) {
+		$items[$i] = str_replace( $placeholder, $separator, $str );
+	}
+	
+	return $items;
 }
 
 class ReplacerCallback {
-  function ReplacerCallback( $from, $to ) {
-    $this->from = $from;
-    $this->to = $to;
-  }
-  
-  function go( $matches ) {
-    return str_replace( $this->from, $this->to, $matches[1] );
-  }
+	function ReplacerCallback( $from, $to ) {
+		$this->from = $from;
+		$this->to = $to;
+	}
+	
+	function go( $matches ) {
+		return str_replace( $this->from, $this->to, $matches[1] );
+	}
 }
 
 /**
@@ -507,33 +507,33 @@
  * @return array
  */
 function decodeTagAttributes( $text ) {
-  $attribs = array();
+	$attribs = array();
 
-  if( trim( $text ) == '' ) {
-    return $attribs;
-  }
+	if( trim( $text ) == '' ) {
+		return $attribs;
+	}
 
-  $pairs = array();
-  if( !preg_match_all(
-    MW_ATTRIBS_REGEX,
-    $text,
-    $pairs,
-    PREG_SET_ORDER ) ) {
-    return $attribs;
-  }
+	$pairs = array();
+	if( !preg_match_all(
+		MW_ATTRIBS_REGEX,
+		$text,
+		$pairs,
+		PREG_SET_ORDER ) ) {
+		return $attribs;
+	}
 
-  foreach( $pairs as $set ) {
-    $attribute = strtolower( $set[1] );
-    $value = getTagAttributeCallback( $set );
-    
-    // Normalize whitespace
-    $value = preg_replace( '/[\t\r\n ]+/', ' ', $value );
-    $value = trim( $value );
-    
-    // Decode character references
-    $attribs[$attribute] = decodeCharReferences( $value );
-  }
-  return $attribs;
+	foreach( $pairs as $set ) {
+		$attribute = strtolower( $set[1] );
+		$value = getTagAttributeCallback( $set );
+		
+		// Normalize whitespace
+		$value = preg_replace( '/[\t\r\n ]+/', ' ', $value );
+		$value = trim( $value );
+		
+		// Decode character references
+		$attribs[$attribute] = decodeCharReferences( $value );
+	}
+	return $attribs;
 }
 
 /**
@@ -545,25 +545,25 @@
  * @access private
  */
 function getTagAttributeCallback( $set ) {
-  if( isset( $set[6] ) ) {
-    # Illegal #XXXXXX color with no quotes.
-    return $set[6];
-  } elseif( isset( $set[5] ) ) {
-    # No quotes.
-    return $set[5];
-  } elseif( isset( $set[4] ) ) {
-    # Single-quoted
-    return $set[4];
-  } elseif( isset( $set[3] ) ) {
-    # Double-quoted
-    return $set[3];
-  } elseif( !isset( $set[2] ) ) {
-    # In XHTML, attributes must have a value.
-    # For 'reduced' form, return explicitly the attribute name here.
-    return $set[1];
-  } else {
-    die_friendly('Parser error', "<p>Tag conditions not met. This should never happen and is a bug.</p>" );
-  }
+	if( isset( $set[6] ) ) {
+		# Illegal #XXXXXX color with no quotes.
+		return $set[6];
+	} elseif( isset( $set[5] ) ) {
+		# No quotes.
+		return $set[5];
+	} elseif( isset( $set[4] ) ) {
+		# Single-quoted
+		return $set[4];
+	} elseif( isset( $set[3] ) ) {
+		# Double-quoted
+		return $set[3];
+	} elseif( !isset( $set[2] ) ) {
+		# In XHTML, attributes must have a value.
+		# For 'reduced' form, return explicitly the attribute name here.
+		return $set[1];
+	} else {
+		die_friendly('Parser error', "<p>Tag conditions not met. This should never happen and is a bug.</p>" );
+	}
 }
 
 /**
@@ -583,78 +583,78 @@
  * @access private
  */
 function mwStrip( $text, &$state, $stripcomments = false , $dontstrip = array () ) {
-  global $wgRandomKey;
-  $render = true;
+	global $wgRandomKey;
+	$render = true;
 
-  $wgRandomKey = "\x07UNIQ" . dechex(mt_rand(0, 0x7fffffff)) . dechex(mt_rand(0, 0x7fffffff));
-  $uniq_prefix =& $wgRandomKey;
-  $commentState = array();
-  
-  $elements = array( 'nowiki', 'gallery' );
-  
-  # Removing $dontstrip tags from $elements list (currently only 'gallery', fixing bug 2700)
-  foreach ( $elements AS $k => $v ) {
-    if ( !in_array ( $v , $dontstrip ) ) continue;
-    unset ( $elements[$k] );
-  }
-  
-  $matches = array();
-  $text = extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
+	$wgRandomKey = "\x07UNIQ" . dechex(mt_rand(0, 0x7fffffff)) . dechex(mt_rand(0, 0x7fffffff));
+	$uniq_prefix =& $wgRandomKey;
+	$commentState = array();
+	
+	$elements = array( 'nowiki', 'gallery' );
+	
+	# Removing $dontstrip tags from $elements list (currently only 'gallery', fixing bug 2700)
+	foreach ( $elements AS $k => $v ) {
+		if ( !in_array ( $v , $dontstrip ) ) continue;
+		unset ( $elements[$k] );
+	}
+	
+	$matches = array();
+	$text = extractTagsAndParams( $elements, $text, $matches, $uniq_prefix );
 
-  foreach( $matches as $marker => $data ) {
-    list( $element, $content, $params, $tag ) = $data;
-    if( $render ) {
-      $tagName = strtolower( $element );
-      switch( $tagName ) {
-      case '!--':
-        // Comment
-        if( substr( $tag, -3 ) == '-->' ) {
-          $output = $tag;
-        } else {
-          // Unclosed comment in input.
-          // Close it so later stripping can remove it
-          $output = "$tag-->";
-        }
-        break;
-      case 'html':
-        if( $wgRawHtml ) {
-          $output = $content;
-          break;
-        }
-        // Shouldn't happen otherwise. :)
-      case 'nowiki':
-        $output = wfEscapeHTMLTagsOnly( $content );
-        break;
-      default:
-      }
-    } else {
-      // Just stripping tags; keep the source
-      $output = $tag;
-    }
+	foreach( $matches as $marker => $data ) {
+		list( $element, $content, $params, $tag ) = $data;
+		if( $render ) {
+			$tagName = strtolower( $element );
+			switch( $tagName ) {
+			case '!--':
+				// Comment
+				if( substr( $tag, -3 ) == '-->' ) {
+					$output = $tag;
+				} else {
+					// Unclosed comment in input.
+					// Close it so later stripping can remove it
+					$output = "$tag-->";
+				}
+				break;
+			case 'html':
+				if( $wgRawHtml ) {
+					$output = $content;
+					break;
+				}
+				// Shouldn't happen otherwise. :)
+			case 'nowiki':
+				$output = wfEscapeHTMLTagsOnly( $content );
+				break;
+			default:
+			}
+		} else {
+			// Just stripping tags; keep the source
+			$output = $tag;
+		}
 
-    // Unstrip the output, because unstrip() is no longer recursive so 
-    // it won't do it itself
-    $output = unstrip( $output, $state );
+		// Unstrip the output, because unstrip() is no longer recursive so 
+		// it won't do it itself
+		$output = unstrip( $output, $state );
 
-    if( !$stripcomments && $element == '!--' ) {
-      $commentState[$marker] = $output;
-    } elseif ( $element == 'html' || $element == 'nowiki' ) {
-      $state['nowiki'][$marker] = $output;
-    } else {
-      $state['general'][$marker] = $output;
-    }
-  }
+		if( !$stripcomments && $element == '!--' ) {
+			$commentState[$marker] = $output;
+		} elseif ( $element == 'html' || $element == 'nowiki' ) {
+			$state['nowiki'][$marker] = $output;
+		} else {
+			$state['general'][$marker] = $output;
+		}
+	}
 
-  # Unstrip comments unless explicitly told otherwise.
-  # (The comments are always stripped prior to this point, so as to
-  # not invoke any extension tags / parser hooks contained within
-  # a comment.)
-  if ( !$stripcomments ) {
-    // Put them all back and forget them
-    $text = strtr( $text, $commentState );
-  }
+	# Unstrip comments unless explicitly told otherwise.
+	# (The comments are always stripped prior to this point, so as to
+	# not invoke any extension tags / parser hooks contained within
+	# a comment.)
+	if ( !$stripcomments ) {
+		// Put them all back and forget them
+		$text = strtr( $text, $commentState );
+	}
 
-  return $text;
+	return $text;
 }
 
 /**
@@ -676,65 +676,65 @@
  * @static
  */
 function extractTagsAndParams($elements, $text, &$matches, $uniq_prefix = ''){
-  static $n = 1;
-  $stripped = '';
-  $matches = array();
+	static $n = 1;
+	$stripped = '';
+	$matches = array();
 
-  $taglist = implode( '|', $elements );
-  $start = "/<($taglist)(\\s+[^>]*?|\\s*?)(\/?>)|<(!--)/i";
+	$taglist = implode( '|', $elements );
+	$start = "/<($taglist)(\\s+[^>]*?|\\s*?)(\/?>)|<(!--)/i";
 
-  while ( '' != $text ) {
-    $p = preg_split( $start, $text, 2, PREG_SPLIT_DELIM_CAPTURE );
-    $stripped .= $p[0];
-    if( count( $p ) < 5 ) {
-      break;
-    }
-    if( count( $p ) > 5 ) {
-      // comment
-      $element    = $p[4];
-      $attributes = '';
-      $close      = '';
-      $inside     = $p[5];
-    } else {
-      // tag
-      $element    = $p[1];
-      $attributes = $p[2];
-      $close      = $p[3];
-      $inside     = $p[4];
-    }
+	while ( '' != $text ) {
+		$p = preg_split( $start, $text, 2, PREG_SPLIT_DELIM_CAPTURE );
+		$stripped .= $p[0];
+		if( count( $p ) < 5 ) {
+			break;
+		}
+		if( count( $p ) > 5 ) {
+			// comment
+			$element    = $p[4];
+			$attributes = '';
+			$close      = '';
+			$inside     = $p[5];
+		} else {
+			// tag
+			$element    = $p[1];
+			$attributes = $p[2];
+			$close      = $p[3];
+			$inside     = $p[4];
+		}
 
-    $marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . '-QINU';
-    $stripped .= $marker;
+		$marker = "$uniq_prefix-$element-" . sprintf('%08X', $n++) . '-QINU';
+		$stripped .= $marker;
 
-    if ( $close === '/>' ) {
-      // Empty element tag, <tag />
-      $content = null;
-      $text = $inside;
-      $tail = null;
-    } else {
-      if( $element == '!--' ) {
-        $end = '/(-->)/';
-      } else {
-        $end = "/(<\\/$element\\s*>)/i";
-      }
-      $q = preg_split( $end, $inside, 2, PREG_SPLIT_DELIM_CAPTURE );
-      $content = $q[0];
-      if( count( $q ) < 3 ) {
-        # No end tag -- let it run out to the end of the text.
-        $tail = '';
-        $text = '';
-      } else {
-        $tail = $q[1];
-        $text = $q[2];
-      }
-    }
-    
-    $matches[$marker] = array( $element,
-      $content,
-      decodeTagAttributes( $attributes ),
-      "<$element$attributes$close$content$tail" );
-  }
-  return $stripped;
+		if ( $close === '/>' ) {
+			// Empty element tag, <tag />
+			$content = null;
+			$text = $inside;
+			$tail = null;
+		} else {
+			if( $element == '!--' ) {
+				$end = '/(-->)/';
+			} else {
+				$end = "/(<\\/$element\\s*>)/i";
+			}
+			$q = preg_split( $end, $inside, 2, PREG_SPLIT_DELIM_CAPTURE );
+			$content = $q[0];
+			if( count( $q ) < 3 ) {
+				# No end tag -- let it run out to the end of the text.
+				$tail = '';
+				$text = '';
+			} else {
+				$tail = $q[1];
+				$text = $q[2];
+			}
+		}
+		
+		$matches[$marker] = array( $element,
+			$content,
+			decodeTagAttributes( $attributes ),
+			"<$element$attributes$close$content$tail" );
+	}
+	return $stripped;
 }
 
 /**
@@ -745,10 +745,10 @@
  * @return string Escaped string
  */
 function wfEscapeHTMLTagsOnly( $in ) {
-  return str_replace(
-    array( '"', '>', '<' ),
-    array( '&quot;', '&gt;', '&lt;' ),
-    $in );
+	return str_replace(
+		array( '"', '>', '<' ),
+		array( '&quot;', '&gt;', '&lt;' ),
+		$in );
 }
 
 /**
@@ -758,14 +758,14 @@
  * @private
  */
 function unstrip( $text, &$state ) {
-  if ( !isset( $state['general'] ) ) {
-    return $text;
-  }
+	if ( !isset( $state['general'] ) ) {
+		return $text;
+	}
 
-  # TODO: good candidate for FSS
-  $text = strtr( $text, $state['general'] );
-  
-  return $text;
+	# TODO: good candidate for FSS
+	$text = strtr( $text, $state['general'] );
+	
+	return $text;
 }
 
 /**
@@ -776,11 +776,11 @@
  * @private
  */
 function decodeChar( $codepoint ) {
-  if( validateCodepoint( $codepoint ) ) {
-    return codepointToUtf8( $codepoint );
-  } else {
-    return UTF8_REPLACEMENT;
-  }
+	if( validateCodepoint( $codepoint ) ) {
+		return codepointToUtf8( $codepoint );
+	} else {
+		return UTF8_REPLACEMENT;
+	}
 }
 
 /**
@@ -792,12 +792,12 @@
  * @return string
  */
 function decodeEntity( $name ) {
-  global $wgHtmlEntities;
-  if( isset( $wgHtmlEntities[$name] ) ) {
-    return codepointToUtf8( $wgHtmlEntities[$name] );
-  } else {
-    return "&$name;";
-  }
+	global $wgHtmlEntities;
+	if( isset( $wgHtmlEntities[$name] ) ) {
+		return codepointToUtf8( $wgHtmlEntities[$name] );
+	} else {
+		return "&$name;";
+	}
 }
 
 /**
@@ -806,14 +806,14 @@
  * @return bool
  */
 function validateCodepoint( $codepoint ) {
-  return ($codepoint ==    0x09)
-    || ($codepoint ==    0x0a)
-    || ($codepoint ==    0x0d)
-    || ($codepoint >=    0x20 && $codepoint <=   0xd7ff)
-    || ($codepoint >=  0xe000 && $codepoint <=   0xfffd)
-    || ($codepoint >= 0x10000 && $codepoint <= 0x10ffff);
+	return ($codepoint ==    0x09)
+		|| ($codepoint ==    0x0a)
+		|| ($codepoint ==    0x0d)
+		|| ($codepoint >=    0x20 && $codepoint <=   0xd7ff)
+		|| ($codepoint >=  0xe000 && $codepoint <=   0xfffd)
+		|| ($codepoint >= 0x10000 && $codepoint <= 0x10ffff);
 }
-  
+	
 /**
  * Return UTF-8 sequence for a given Unicode code point.
  * May die if fed out of range data.
@@ -843,16 +843,16 @@
  * @return string
  */
 function decodeCharReferencesCallback( $matches ) {
-  if( $matches[1] != '' ) {
-    return decodeEntity( $matches[1] );
-  } elseif( $matches[2] != '' ) {
-    return  decodeChar( intval( $matches[2] ) );
-  } elseif( $matches[3] != ''  ) {
-    return  decodeChar( hexdec( $matches[3] ) );
-  } elseif( $matches[4] != '' ) {
-    return  decodeChar( hexdec( $matches[4] ) );
-  }
-  # Last case should be an ampersand by itself
-  return $matches[0];
+	if( $matches[1] != '' ) {
+		return decodeEntity( $matches[1] );
+	} elseif( $matches[2] != '' ) {
+		return  decodeChar( intval( $matches[2] ) );
+	} elseif( $matches[3] != ''  ) {
+		return  decodeChar( hexdec( $matches[3] ) );
+	} elseif( $matches[4] != '' ) {
+		return  decodeChar( hexdec( $matches[4] ) );
+	}
+	# Last case should be an ampersand by itself
+	return $matches[0];
 }
 
--- a/includes/wikiengine/parse_mediawiki.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/wikiengine/parse_mediawiki.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,278 +13,278 @@
 
 class Carpenter_Parse_MediaWiki
 {
-  public $rules = array(
-    'bold'   => "/'''(.+?)'''/",
-    'italic' => "/''(.+?)''/",
-    'underline' => '/__(.+?)__/',
-    'externalwithtext' => '#\[((?:https?|irc|ftp)://.+?) (.+?)\]#',
-    'externalnotext' => '#\[((?:https?|irc|ftp)://.+?)\]#',
-    'mailtonotext' => '#\[mailto:([^ \]]+?)\]#',
-    'mailtowithtext' => '#\[mailto:([^ \]]+?) (.+?)\]#',
-    'hr' => '/^[-]{4,} *$/m',
-    'code' => '/^(?:<code>(?:\r?\n)?|<pre>)(.+?)(?:<\/pre>|(?:\r?\n)?<\/code>)$/mis'
-  );
-  
-  private $blockquote_rand_id;
-  
-  public function lang(&$text)
-  {
-    global $lang;
-    
-    preg_match_all('/<lang (?:code|id)="([a-z0-9_-]+)">([\w\W]+?)<\/lang>/', $text, $langmatch);
-    foreach ( $langmatch[0] as $i => $match )
-    {
-      if ( $langmatch[1][$i] == $lang->lang_code )
-      {
-        $text = str_replace_once($match, $langmatch[2][$i], $text);
-      }
-      else
-      {
-        $text = str_replace_once($match, '', $text);
-      }
-    }
-    
-    return array();
-  }
-  
-  public function templates(&$text)
-  {
-    $template_regex = "/\{\{(.+)((\n|\|[ ]*([A-z0-9]+)[ ]*=[ ]*(.+))*)\}\}/isU";
-    $i = 0;
-    while ( preg_match($template_regex, $text, $match) )
-    {
-      $i++;
-      if ( $i == 5 )
-        break;
-      $text = RenderMan::include_templates($text);
-    }
-    
-    return array();
-  }
-  
-  public function heading(&$text)
-  {
-    if ( !preg_match_all('/^(={1,6}) *(.+?) *\\1 *$/m', $text, $results) )
-      return array();
-    
-    $headings = array();
-    foreach ( $results[0] as $i => $match )
-    {
-      $headings[] = array(
-          'level' => strlen($results[1][$i]),
-          'text' => $results[2][$i]
-        );
-    }
-    
-    $text = Carpenter::tokenize($text, $results[0]);
-    
-    return $headings;
-  }
-  
-  public function multilist(&$text)
-  {
-    // Match entire lists
-    $regex = '/^
-                ([:#\*])+     # Initial list delimiter
-                [ ]*
-                .+?
-                (?:
-                  \r?\n
-                  (?:\\1|[ ]{2,})
-                  [ ]*
-                  .+?)*
-                $/mx';
-    
-    if ( !preg_match_all($regex, $text, $lists) )
-      return array();
-    
-    $types = array(
-        '*' => 'unordered',
-        '#' => 'ordered',
-        ':' => 'indent'
-      );
-    
-    $pieces = array();
-    foreach ( $lists[0] as $i => $list )
-    {
-      $token = $lists[1][$i];
-      $piece = array(
-          'type' => $types[$token],
-          'items' => array()
-        );
-      
-      // convert windows newlines to unix
-      $list = str_replace("\r\n", "\n", $list);
-      $items_pre = explode("\n", $list);
-      $items = array();
-      // first pass, go through and combine items that are newlined
-      foreach ( $items_pre as $item )
-      {
-        if ( substr($item, 0, 1) == $token )
-        {
-          $items[] = $item;
-        }
-        else
-        {
-          // it's a continuation of the previous LI. Don't need to worry about
-          // undefined indices here since the regex should filter out all invalid
-          // markup. Just append this line to the previous.
-          $items[ count($items) - 1 ] .= "\n" . trim($item);
-        }
-      }
-      
-      // second pass, separate items and tokens
-      unset($items_pre);
-      foreach ( $items as $item )
-      {
-        // get the depth
-        $itemtoken = preg_replace('/^([#:\*]+).*$/s', '$1', $item);
-        // get the text
-        $itemtext = trim(substr($item, strlen($itemtoken)));
-        $piece['items'][] = array(
-            // depth starts at 1
-            'depth' => strlen($itemtoken),
-            'text' => $itemtext
-          );
-      }
-      $pieces[] = $piece;
-    }
-    
-    $text = Carpenter::tokenize($text, $lists[0]);
-    
-    return $pieces;
-  }
-  
-  public function blockquote(&$text)
-  {
-    $rand_id = hexencode(AESCrypt::randkey(16), '', '');
-    
-    while ( preg_match_all('/^(?:(>+) *.+(?:\r?\n|$))+/m', $text, $quotes) )
-    {
-      foreach ( $quotes[0] as $quote )
-      {
-        $piece = trim(preg_replace('/^> */m', '', $quote));
-        $text = str_replace_once($quote, "{blockquote:$rand_id}\n$piece\n{/blockquote:$rand_id}\n", $text);
-      }
-    }
-    //die('<pre>' . htmlspecialchars($text) . '</pre>');
-    
-    $this->blockquote_rand_id = $rand_id;
-  }
-  
-  public function blockquotepost(&$text)
-  {
-    return $this->blockquote_rand_id;
-  }
-  
-  public function paragraph(&$text)
-  {
-    // The trick with paragraphs is to not turn things into them when a block level element already wraps the block of text.
-    // First we need a list of block level elements (http://htmlhelp.com/reference/html40/block.html + some Enano extensions)
-    $blocklevel = 'address|blockquote|center|code|div|dl|fieldset|form|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|table|ul|tr|td|th|tbody|thead|tfoot';
-    
-    // Wrap all block level tags
-    RenderMan::tag_strip('_paragraph_bypass', $text, $_nw);
-    
-    // Find all opening and closing tags
-    
-    $regex = ";(<(?:/(?:$blocklevel)|(?:$blocklevel)(?: [^>]*?)?)>);s";
-                
-    // oh. and we're using this tokens thing because for identical matches, the first match will
-    // get wrapped X number of times instead of all matches getting wrapped once; replacing each
-    // with a unique token id remedies this
-    
-    $tokens = array();
-    $rand_id = sha1(microtime() . mt_rand());
-    $tag_stack = array();
-    
-    if ( $text_split = preg_split($regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE) )
-    {
-      $text = '';
-      // go through the text, extract tag names, and push them to a stack.
-      foreach ( $text_split as $splitpart )
-      {
-        if ( preg_match(";^<(/)?($blocklevel)( |>);i", $splitpart, $match) )
-        {
-          $tagname = $match[2];
-          if ( $match[1] == '/' )
-          {
-            // closing tag
-            if ( $tagname != ($top = array_pop($tag_stack)) )
-            {
-              // invalid - push back
-              array_push($tag_stack, $top);
-            }
-            else
-            {
-              // valid - if stack's at zero, add a </_paragraph_bypass>
-              if ( count($tag_stack) == 0 )
-                $splitpart .= '</_paragraph_bypass>';
-            }
-          }
-          else
-          {
-            // push
-            array_push($tag_stack, $tagname);
-            if ( count($tag_stack) == 1 )
-              $splitpart = '<_paragraph_bypass>' . $splitpart;
-          }
-        }
-        $text .= $splitpart;
-      }
-      //echo '<pre>' . htmlspecialchars(print_r($text, true)) . '</pre>';
-    }
-    
-    // All things that should be para-bypassed now are surrounded by _paragraph_bypass tags.
-    
-    // die('<pre>' . htmlspecialchars($text) . '</pre>');
+	public $rules = array(
+		'bold'   => "/'''(.+?)'''/",
+		'italic' => "/''(.+?)''/",
+		'underline' => '/__(.+?)__/',
+		'externalwithtext' => '#\[((?:https?|irc|ftp)://.+?) (.+?)\]#',
+		'externalnotext' => '#\[((?:https?|irc|ftp)://.+?)\]#',
+		'mailtonotext' => '#\[mailto:([^ \]]+?)\]#',
+		'mailtowithtext' => '#\[mailto:([^ \]]+?) (.+?)\]#',
+		'hr' => '/^[-]{4,} *$/m',
+		'code' => '/^(?:<code>(?:\r?\n)?|<pre>)(.+?)(?:<\/pre>|(?:\r?\n)?<\/code>)$/mis'
+	);
+	
+	private $blockquote_rand_id;
+	
+	public function lang(&$text)
+	{
+		global $lang;
+		
+		preg_match_all('/<lang (?:code|id)="([a-z0-9_-]+)">([\w\W]+?)<\/lang>/', $text, $langmatch);
+		foreach ( $langmatch[0] as $i => $match )
+		{
+			if ( $langmatch[1][$i] == $lang->lang_code )
+			{
+				$text = str_replace_once($match, $langmatch[2][$i], $text);
+			}
+			else
+			{
+				$text = str_replace_once($match, '', $text);
+			}
+		}
+		
+		return array();
+	}
+	
+	public function templates(&$text)
+	{
+		$template_regex = "/\{\{(.+)((\n|\|[ ]*([A-z0-9]+)[ ]*=[ ]*(.+))*)\}\}/isU";
+		$i = 0;
+		while ( preg_match($template_regex, $text, $match) )
+		{
+			$i++;
+			if ( $i == 5 )
+				break;
+			$text = RenderMan::include_templates($text);
+		}
+		
+		return array();
+	}
+	
+	public function heading(&$text)
+	{
+		if ( !preg_match_all('/^(={1,6}) *(.+?) *\\1 *$/m', $text, $results) )
+			return array();
+		
+		$headings = array();
+		foreach ( $results[0] as $i => $match )
+		{
+			$headings[] = array(
+					'level' => strlen($results[1][$i]),
+					'text' => $results[2][$i]
+				);
+		}
+		
+		$text = Carpenter::tokenize($text, $results[0]);
+		
+		return $headings;
+	}
 	
-    RenderMan::tag_unstrip('_paragraph_bypass', $text, $_nw, true);
-    
-    // This is potentially a hack. It allows the parser to stick in <_paragraph_bypass> tags
-    // to prevent the paragraph parser from interfering with pretty HTML generated elsewhere.
-    RenderMan::tag_strip('_paragraph_bypass', $text, $_nw);
-    
-    $startcond = "(?!(?:[\\r\\n]|\{_paragraph_bypass:[a-f0-9]{32}:[0-9]+\}|[ ]*<\/?(?:$blocklevel)(?: .+>|>)))";
-    $regex = "/^
-                $startcond        # line start condition - do not match if the line starts with the condition above
-                .+?               # body text
-                (?:
-                  \\n             # additional lines
-                  $startcond      # make sure of only one newline in a row, and end the paragraph if a new line fails the start condition
-                  .*?
-                )*                # keep going until it fails
-              $
-              /mx";
-    
-    if ( !preg_match_all($regex, $text, $matches) )
-    {
-      RenderMan::tag_unstrip('_paragraph_bypass', $text, $_nw);
-      return array();
-    }
-    
-    // Debugging :)
-    // die('<pre>' . htmlspecialchars($text) . "\n-----------------------------------------------------------\n" . htmlspecialchars(print_r($matches, true)) . '</pre>');
-    
-    // restore stripped
-    RenderMan::tag_unstrip('_paragraph_bypass', $text, $_nw);
-    
-    // tokenize
-    $text = Carpenter::tokenize($text, $matches[0]);
-    
-    return $matches[0];
-  }
+	public function multilist(&$text)
+	{
+		// Match entire lists
+		$regex = '/^
+								([:#\*])+     # Initial list delimiter
+								[ ]*
+								.+?
+								(?:
+									\r?\n
+									(?:\\1|[ ]{2,})
+									[ ]*
+									.+?)*
+								$/mx';
+		
+		if ( !preg_match_all($regex, $text, $lists) )
+			return array();
+		
+		$types = array(
+				'*' => 'unordered',
+				'#' => 'ordered',
+				':' => 'indent'
+			);
+		
+		$pieces = array();
+		foreach ( $lists[0] as $i => $list )
+		{
+			$token = $lists[1][$i];
+			$piece = array(
+					'type' => $types[$token],
+					'items' => array()
+				);
+			
+			// convert windows newlines to unix
+			$list = str_replace("\r\n", "\n", $list);
+			$items_pre = explode("\n", $list);
+			$items = array();
+			// first pass, go through and combine items that are newlined
+			foreach ( $items_pre as $item )
+			{
+				if ( substr($item, 0, 1) == $token )
+				{
+					$items[] = $item;
+				}
+				else
+				{
+					// it's a continuation of the previous LI. Don't need to worry about
+					// undefined indices here since the regex should filter out all invalid
+					// markup. Just append this line to the previous.
+					$items[ count($items) - 1 ] .= "\n" . trim($item);
+				}
+			}
+			
+			// second pass, separate items and tokens
+			unset($items_pre);
+			foreach ( $items as $item )
+			{
+				// get the depth
+				$itemtoken = preg_replace('/^([#:\*]+).*$/s', '$1', $item);
+				// get the text
+				$itemtext = trim(substr($item, strlen($itemtoken)));
+				$piece['items'][] = array(
+						// depth starts at 1
+						'depth' => strlen($itemtoken),
+						'text' => $itemtext
+					);
+			}
+			$pieces[] = $piece;
+		}
+		
+		$text = Carpenter::tokenize($text, $lists[0]);
+		
+		return $pieces;
+	}
+	
+	public function blockquote(&$text)
+	{
+		$rand_id = hexencode(AESCrypt::randkey(16), '', '');
+		
+		while ( preg_match_all('/^(?:(>+) *.+(?:\r?\n|$))+/m', $text, $quotes) )
+		{
+			foreach ( $quotes[0] as $quote )
+			{
+				$piece = trim(preg_replace('/^> */m', '', $quote));
+				$text = str_replace_once($quote, "{blockquote:$rand_id}\n$piece\n{/blockquote:$rand_id}\n", $text);
+			}
+		}
+		//die('<pre>' . htmlspecialchars($text) . '</pre>');
+		
+		$this->blockquote_rand_id = $rand_id;
+	}
+	
+	public function blockquotepost(&$text)
+	{
+		return $this->blockquote_rand_id;
+	}
+	
+	public function paragraph(&$text)
+	{
+		// The trick with paragraphs is to not turn things into them when a block level element already wraps the block of text.
+		// First we need a list of block level elements (http://htmlhelp.com/reference/html40/block.html + some Enano extensions)
+		$blocklevel = 'address|blockquote|center|code|div|dl|fieldset|form|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|table|ul|tr|td|th|tbody|thead|tfoot';
+		
+		// Wrap all block level tags
+		RenderMan::tag_strip('_paragraph_bypass', $text, $_nw);
+		
+		// Find all opening and closing tags
+		
+		$regex = ";(<(?:/(?:$blocklevel)|(?:$blocklevel)(?: [^>]*?)?)>);s";
+								
+		// oh. and we're using this tokens thing because for identical matches, the first match will
+		// get wrapped X number of times instead of all matches getting wrapped once; replacing each
+		// with a unique token id remedies this
+		
+		$tokens = array();
+		$rand_id = sha1(microtime() . mt_rand());
+		$tag_stack = array();
+		
+		if ( $text_split = preg_split($regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE) )
+		{
+			$text = '';
+			// go through the text, extract tag names, and push them to a stack.
+			foreach ( $text_split as $splitpart )
+			{
+				if ( preg_match(";^<(/)?($blocklevel)( |>);i", $splitpart, $match) )
+				{
+					$tagname = $match[2];
+					if ( $match[1] == '/' )
+					{
+						// closing tag
+						if ( $tagname != ($top = array_pop($tag_stack)) )
+						{
+							// invalid - push back
+							array_push($tag_stack, $top);
+						}
+						else
+						{
+							// valid - if stack's at zero, add a </_paragraph_bypass>
+							if ( count($tag_stack) == 0 )
+								$splitpart .= '</_paragraph_bypass>';
+						}
+					}
+					else
+					{
+						// push
+						array_push($tag_stack, $tagname);
+						if ( count($tag_stack) == 1 )
+							$splitpart = '<_paragraph_bypass>' . $splitpart;
+					}
+				}
+				$text .= $splitpart;
+			}
+			//echo '<pre>' . htmlspecialchars(print_r($text, true)) . '</pre>';
+		}
+		
+		// All things that should be para-bypassed now are surrounded by _paragraph_bypass tags.
+		
+		// die('<pre>' . htmlspecialchars($text) . '</pre>');
+	
+		RenderMan::tag_unstrip('_paragraph_bypass', $text, $_nw, true);
+		
+		// This is potentially a hack. It allows the parser to stick in <_paragraph_bypass> tags
+		// to prevent the paragraph parser from interfering with pretty HTML generated elsewhere.
+		RenderMan::tag_strip('_paragraph_bypass', $text, $_nw);
+		
+		$startcond = "(?!(?:[\\r\\n]|\{_paragraph_bypass:[a-f0-9]{32}:[0-9]+\}|[ ]*<\/?(?:$blocklevel)(?: .+>|>)))";
+		$regex = "/^
+								$startcond        # line start condition - do not match if the line starts with the condition above
+								.+?               # body text
+								(?:
+									\\n             # additional lines
+									$startcond      # make sure of only one newline in a row, and end the paragraph if a new line fails the start condition
+									.*?
+								)*                # keep going until it fails
+							$
+							/mx";
+		
+		if ( !preg_match_all($regex, $text, $matches) )
+		{
+			RenderMan::tag_unstrip('_paragraph_bypass', $text, $_nw);
+			return array();
+		}
+		
+		// Debugging :)
+		// die('<pre>' . htmlspecialchars($text) . "\n-----------------------------------------------------------\n" . htmlspecialchars(print_r($matches, true)) . '</pre>');
+		
+		// restore stripped
+		RenderMan::tag_unstrip('_paragraph_bypass', $text, $_nw);
+		
+		// tokenize
+		$text = Carpenter::tokenize($text, $matches[0]);
+		
+		return $matches[0];
+	}
 }
 
 function parser_mediawiki_xhtml_image($text)
 {
-  $text = RenderMan::process_image_tags($text, $taglist);
-  $text = RenderMan::process_imgtags_stage2($text, $taglist);
-  return $text;
+	$text = RenderMan::process_image_tags($text, $taglist);
+	$text = RenderMan::process_imgtags_stage2($text, $taglist);
+	return $text;
 }
 
 function parser_mediawiki_xhtml_tables($text)
 {
-  return process_tables($text);
+	return process_tables($text);
 }
 
--- a/includes/wikiengine/render_xhtml.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/wikiengine/render_xhtml.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,159 +13,159 @@
 
 class Carpenter_Render_Xhtml
 {
-  public $rules = array(
-    'lang'   => '',
-    'templates' => '',
-    'bold'   => '<strong>\\1</strong>',
-    'italic' => '<em>\\1</em>',
-    'underline' => '<span style="text-decoration: underline;">\\1</span>',
-    'externalwithtext' => '<a href="\\1" onclick="window.open(this.href); return false;">\\2</a>',
-    'externalnotext' => '<a href="\\1" onclick="window.open(this.href); return false;">\\1</a>',
-    'hr' => '<hr />'
-  );
-  
-  public function heading($text, $pieces)
-  {
-    foreach ( $pieces as $i => $piece )
-    {
-      $tocid = sanitize_page_id(trim($piece['text']));
-      // (bad) workaround for links in headings
-      $tocid = str_replace(array('[', ']'), '', $tocid);
-      $tag = '<h' . $piece['level'] . ' id="head:' . $tocid . '">';
-      $tag .= trim($piece['text']);
-      $tag .= '</h' . $piece['level'] . '>';
-      
-      $text = str_replace(Carpenter::generate_token($i), $tag, $text);
-    }
-    
-    return $text;
-  }
-  
-  public function multilist($text, $pieces)
-  {
-    foreach ( $pieces as $i => $piece )
-    {
-      switch($piece['type'])
-      {
-        case 'unordered':
-        default:
-          $btag = 'ul';
-          $itag = 'li';
-          break;
-        case 'ordered':
-          $btag = 'ol';
-          $itag = 'li';
-          break;
-        case 'indent':
-          $btag = 'dl';
-          $itag = 'dd';
-          break;
-      }
-      $list = "<_paragraph_bypass><$btag>\n";
-      $spacing = '';
-      $depth = 1;
-      foreach ( $piece['items'] as $j => $item )
-      {
-        // most of this just goes into pretty formatting.
-        // everything else goes into meeting the PITA requirement that if you're going
-        // another level deep, HTML requires the next level to be INSIDE of the <dd>/<li> tag.
-        $itemdepth = $item['depth'];
-        if ( $itemdepth > $depth )
-        {
-          while ( $depth < $itemdepth )
-          {
-            $spacing .= '    ';
-            $list .= "{$spacing}<$btag>\n";
-            $depth++;
-          }
-        }
-        else if ( $itemdepth < $depth )
-        {
-          while ( $depth > $itemdepth )
-          {
-            $list .= "{$spacing}</$btag>\n";
-            $spacing = substr($spacing, 4);
-            $list .= "{$spacing}</$itag>\n";
-            $spacing = substr($spacing, 4);
-            $depth--;
-          }
-        }
-        $list .= "{$spacing}    <$itag>" . nl2br($item['text']);
-        if ( ( isset($piece['items'][ ++$j ]) && $piece['items'][ $j ]['depth'] <= $itemdepth ) || !isset($piece['items'][ $j ]) ) 
-        {
-          $list .= "</$itag>\n";
-        }
-        else
-        {
-          $list .= "\n";
-          $spacing .= "    ";
-        }
-      }
-      while ( $depth > 1 )
-      {
-        $list .= "{$spacing}</$btag>\n";
-        $spacing = substr($spacing, 4);
-        $list .= "{$spacing}</$itag>\n";
-        $spacing = substr($spacing, 4);
-        $depth--;
-      }
-      $list .= "</$btag></_paragraph_bypass>\n";
-      $text = str_replace(Carpenter::generate_token($i), $list, $text);
-    }
-    return $text;
-  }
-  
-  public function blockquote($text)
-  {
-    return $text;
-  }
-  
-  public function blockquotepost($text, $rand_id)
-  {
-    $text = strtr($text, array(
-        "<p>{blockquote:$rand_id}<br />"  => '<blockquote>',
-        "<br />\n{/blockquote:$rand_id}</p>" => '</blockquote>',
-        "{blockquote:$rand_id}"  => '<blockquote>',
-        "{/blockquote:$rand_id}" => '</blockquote>'
-      ));
-    $text = strtr($text, array(
-        "<blockquote><br />" => '<blockquote>',
-        "</blockquote><br />" => '</blockquote>'
-      ));
-    return $text;
-  }
-  
-  public function paragraph($text, $pieces)
-  {
-    foreach ( $pieces as $i => $piece )
-    {
-      $text = str_replace(Carpenter::generate_token($i), '<p>' . nl2br($piece) . '</p>', $text);
-    }
-    
-    return $text;
-  }
-  
-  public function mailtonotext($pieces)
-  {
-    $pieces[2] = $pieces[1];
-    return $this->mailtowithtext($pieces);
-  }
-  
-  public function mailtowithtext($pieces)
-  {
-    global $email;
-    return $email->encryptEmail($pieces[1], '', '', $pieces[2]);
-  }
-  
-  public function code($match)
-  {
-    return '<pre class="wikitext-code"><final>' . htmlspecialchars($match[1]) . '</final></pre>';
-  }
+	public $rules = array(
+		'lang'   => '',
+		'templates' => '',
+		'bold'   => '<strong>\\1</strong>',
+		'italic' => '<em>\\1</em>',
+		'underline' => '<span style="text-decoration: underline;">\\1</span>',
+		'externalwithtext' => '<a href="\\1" onclick="window.open(this.href); return false;">\\2</a>',
+		'externalnotext' => '<a href="\\1" onclick="window.open(this.href); return false;">\\1</a>',
+		'hr' => '<hr />'
+	);
+	
+	public function heading($text, $pieces)
+	{
+		foreach ( $pieces as $i => $piece )
+		{
+			$tocid = sanitize_page_id(trim($piece['text']));
+			// (bad) workaround for links in headings
+			$tocid = str_replace(array('[', ']'), '', $tocid);
+			$tag = '<h' . $piece['level'] . ' id="head:' . $tocid . '">';
+			$tag .= trim($piece['text']);
+			$tag .= '</h' . $piece['level'] . '>';
+			
+			$text = str_replace(Carpenter::generate_token($i), $tag, $text);
+		}
+		
+		return $text;
+	}
+	
+	public function multilist($text, $pieces)
+	{
+		foreach ( $pieces as $i => $piece )
+		{
+			switch($piece['type'])
+			{
+				case 'unordered':
+				default:
+					$btag = 'ul';
+					$itag = 'li';
+					break;
+				case 'ordered':
+					$btag = 'ol';
+					$itag = 'li';
+					break;
+				case 'indent':
+					$btag = 'dl';
+					$itag = 'dd';
+					break;
+			}
+			$list = "<_paragraph_bypass><$btag>\n";
+			$spacing = '';
+			$depth = 1;
+			foreach ( $piece['items'] as $j => $item )
+			{
+				// most of this just goes into pretty formatting.
+				// everything else goes into meeting the PITA requirement that if you're going
+				// another level deep, HTML requires the next level to be INSIDE of the <dd>/<li> tag.
+				$itemdepth = $item['depth'];
+				if ( $itemdepth > $depth )
+				{
+					while ( $depth < $itemdepth )
+					{
+						$spacing .= '    ';
+						$list .= "{$spacing}<$btag>\n";
+						$depth++;
+					}
+				}
+				else if ( $itemdepth < $depth )
+				{
+					while ( $depth > $itemdepth )
+					{
+						$list .= "{$spacing}</$btag>\n";
+						$spacing = substr($spacing, 4);
+						$list .= "{$spacing}</$itag>\n";
+						$spacing = substr($spacing, 4);
+						$depth--;
+					}
+				}
+				$list .= "{$spacing}    <$itag>" . nl2br($item['text']);
+				if ( ( isset($piece['items'][ ++$j ]) && $piece['items'][ $j ]['depth'] <= $itemdepth ) || !isset($piece['items'][ $j ]) ) 
+				{
+					$list .= "</$itag>\n";
+				}
+				else
+				{
+					$list .= "\n";
+					$spacing .= "    ";
+				}
+			}
+			while ( $depth > 1 )
+			{
+				$list .= "{$spacing}</$btag>\n";
+				$spacing = substr($spacing, 4);
+				$list .= "{$spacing}</$itag>\n";
+				$spacing = substr($spacing, 4);
+				$depth--;
+			}
+			$list .= "</$btag></_paragraph_bypass>\n";
+			$text = str_replace(Carpenter::generate_token($i), $list, $text);
+		}
+		return $text;
+	}
+	
+	public function blockquote($text)
+	{
+		return $text;
+	}
+	
+	public function blockquotepost($text, $rand_id)
+	{
+		$text = strtr($text, array(
+				"<p>{blockquote:$rand_id}<br />"  => '<blockquote>',
+				"<br />\n{/blockquote:$rand_id}</p>" => '</blockquote>',
+				"{blockquote:$rand_id}"  => '<blockquote>',
+				"{/blockquote:$rand_id}" => '</blockquote>'
+			));
+		$text = strtr($text, array(
+				"<blockquote><br />" => '<blockquote>',
+				"</blockquote><br />" => '</blockquote>'
+			));
+		return $text;
+	}
+	
+	public function paragraph($text, $pieces)
+	{
+		foreach ( $pieces as $i => $piece )
+		{
+			$text = str_replace(Carpenter::generate_token($i), '<p>' . nl2br($piece) . '</p>', $text);
+		}
+		
+		return $text;
+	}
+	
+	public function mailtonotext($pieces)
+	{
+		$pieces[2] = $pieces[1];
+		return $this->mailtowithtext($pieces);
+	}
+	
+	public function mailtowithtext($pieces)
+	{
+		global $email;
+		return $email->encryptEmail($pieces[1], '', '', $pieces[2]);
+	}
+	
+	public function code($match)
+	{
+		return '<pre class="wikitext-code"><final>' . htmlspecialchars($match[1]) . '</final></pre>';
+	}
 }
 
 // Alias internal link parsing to RenderMan's method
 function parser_mediawiki_xhtml_internallink($text)
 {
-  return RenderMan::parse_internal_links($text);
+	return RenderMan::parse_internal_links($text);
 }
 
--- a/includes/wikiformat.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/includes/wikiformat.php	Sun Mar 28 23:10:46 2010 -0400
@@ -24,377 +24,377 @@
 
 class Carpenter
 {
-  /**
-   * Parser token
-   * @const string
-   */
-  
-  const PARSER_TOKEN = "\xFF";
-  
-  /**
-   * Parsing engine
-   * @var string
-   */
-  
-  private $parser = 'mediawiki';
-  
-  /**
-   * Rendering engine
-   * @var string
-   */
-  
-  private $renderer = 'xhtml';
-  
-  /**
-   * Rendering flags
-   */
-  
-  public $flags = RENDER_WIKI_DEFAULT;
-  
-  /**
-   * List of rendering rules
-   * @var array
-   */
-  
-  private $rules = array(
-      'lang',
-      'templates',
-      'blockquote',
-      'code',
-      'tables',
-      'heading',
-      'hr',
-      // note: can't be named list ("list" is a PHP language construct)
-      'multilist',
-      'bold',
-      'italic',
-      'underline',
-      'externalwithtext',
-      'externalnotext',
-      'mailtowithtext',
-      'mailtonotext',
-      'image',
-      'internallink',
-      'paragraph',
-      'blockquotepost'
-    );
-  
-  /**
-   * List of render hooks
-   * @var array
-   */
-  
-  private $hooks = array();
-  
-  /* private $rules = array('prefilter', 'delimiter', 'code', 'function', 'html', 'raw', 'include', 'embed', 'anchor',
-           'heading', 'toc', 'horiz', 'break', 'blockquote', 'list', 'deflist', 'table', 'image',
-           'phplookup', 'center', 'newline', 'paragraph', 'url', 'freelink', 'interwiki',
-           'wikilink', 'colortext', 'strong', 'bold', 'emphasis', 'italic', 'underline', 'tt',
-           'superscript', 'subscript', 'revise', 'tighten'); */
-  
-  /**
-   * Render the text!
-   * @param string Text to render
-   * @return string
-   */
-  
-  public function render($text)
-  {
-    $parser_class = "Carpenter_Parse_" . ucwords($this->parser);
-    $renderer_class = "Carpenter_Render_" . ucwords($this->renderer);
-    
-    // empty? (don't remove this. the parser will shit bricks later about rules returning empty strings)
-    if ( trim($text) === '' )
-      return $text;
-    
-    // include files, if we haven't already
-    if ( !class_exists($parser_class) )
-    {
-      require_once( ENANO_ROOT . "/includes/wikiengine/parse_{$this->parser}.php");
-    }
-    
-    if ( !class_exists($renderer_class) )
-    {
-      require_once( ENANO_ROOT . "/includes/wikiengine/render_{$this->renderer}.php");
-    }
-    
-    $parser = new $parser_class;
-    $renderer = new $renderer_class;
-    
-    // run prehooks
-    foreach ( $this->hooks as $hook )
-    {
-      if ( $hook['when'] === PO_FIRST )
-      {
-        $text = call_user_func($hook['callback'], $text);
-        if ( !is_string($text) || empty($text) )
-        {
-          trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
-          // *sigh*
-          $text = '';
-        }
-      }
-    }
-    
-    // perform render
-    foreach ( $this->rules as $rule )
-    {
-      // run prehooks
-      foreach ( $this->hooks as $hook )
-      {
-        if ( $hook['when'] === PO_BEFORE && $hook['rule'] === $rule )
-        {
-          $text = call_user_func($hook['callback'], $text);
-          if ( !is_string($text) || empty($text) )
-          {
-            trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
-            // *sigh*
-            $text = '';
-          }
-        }
-      }
-      
-      // execute rule
-      $text_before = $text;
-      $text = $this->perform_render_step($text, $rule, $parser, $renderer);
-      if ( empty($text) )
-      {
-        trigger_error("Wikitext was completely empty after rule \"$rule\"; restoring backup", E_USER_WARNING);
-        $text = $text_before;
-      }
-      unset($text_before);
-      
-      // run posthooks
-      foreach ( $this->hooks as $hook )
-      {
-        if ( $hook['when'] === PO_AFTER && $hook['rule'] === $rule )
-        {
-          $text = call_user_func($hook['callback'], $text);
-          if ( !is_string($text) || empty($text) )
-          {
-            trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
-            // *sigh*
-            $text = '';
-          }
-        }
-      }
-      
-      RenderMan::tag_strip_push('final', $text, $final_stripdata);
-    }
-    
-    RenderMan::tag_unstrip('final', $text, $final_stripdata);
-    
-    // run posthooks
-    foreach ( $this->hooks as $hook )
-    {
-      if ( $hook['when'] === PO_LAST )
-      {
-        $text = call_user_func($hook['callback'], $text);
-        if ( !is_string($text) || empty($text) )
-        {
-          trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
-          // *sigh*
-          $text = '';
-        }
-      }
-    }
-    
-    return (( defined('ENANO_DEBUG') && isset($_GET['parserdebug']) ) ? '<pre>' . htmlspecialchars($text) . '</pre>' : $text) . "\n\n";
-  }
-  
-  /**
-   * Performs a step in the rendering process.
-   * @param string Text to render
-   * @param string Rule to execute
-   * @param object Parser instance
-   * @param object Renderer instance
-   * @return string
-   * @access private
-   */
-  
-  private function perform_render_step($text, $rule, $parser, $renderer)
-  {
-    // First look for a direct function
-    if ( function_exists("parser_{$this->parser}_{$this->renderer}_{$rule}") )
-    {
-      return call_user_func("parser_{$this->parser}_{$this->renderer}_{$rule}", $text, $this->flags);
-    }
-    
-    // We don't have that, so start looking for other ways or means of doing this
-    if ( method_exists($parser, $rule) && method_exists($renderer, $rule) )
-    {
-      // Both the parser and render have callbacks they want to use.
-      $pieces = $parser->$rule($text);
-      $text = call_user_func(array($renderer, $rule), $text, $pieces);
-    }
-    else if ( method_exists($parser, $rule) && !method_exists($renderer, $rule) && isset($renderer->rules[$rule]) )
-    {
-      // The parser has a callback, but the renderer does not
-      $pieces = $parser->$rule($text);
-      $text = $this->generic_render($text, $pieces, $renderer->rules[$rule]);
-    }
-    else if ( !method_exists($parser, $rule) && isset($parser->rules[$rule]) && method_exists($renderer, $rule) )
-    {
-      // The parser has no callback, but the renderer does
-      $text = preg_replace_callback($parser->rules[$rule], array($renderer, $rule), $text);
-    }
-    else if ( isset($parser->rules[$rule]) && isset($renderer->rules[$rule]) )
-    {
-      // This is a straight-up regex only rule
-      $text = preg_replace($parser->rules[$rule], $renderer->rules[$rule], $text);
-    }
-    else
-    {
-      // Either the renderer or parser does not support this rule, ignore it
-    }
-    
-    return $text;
-  }
-  
-  /**
-   * Generic renderer
-   * @access protected
-   */
-  
-  protected function generic_render($text, $pieces, $rule)
-  {
-    foreach ( $pieces as $i => $piece )
-    {
-      $replacement = $rule;
-      
-      // if the piece is an array, replace $1, $2, etc. in the rule with each value in the piece
-      if ( is_array($piece) )
-      {
-        $j = 0;
-        foreach ( $piece as $part )
-        {
-          $j++;
-          $replacement = str_replace(array("\\$j", "\${$j}"), $part, $replacement);
-        }
-      }
-      // else, just replace \\1 or $1 in the rule with the piece
-      else
-      {
-        $replacement = str_replace(array("\\1", "\$1"), $piece, $replacement);
-      }
-      
-      $text = str_replace(self::generate_token($i), $replacement, $text);
-    }
-    
-    return $text;
-  }
-  
-  /**
-   * Add a hook into the parser.
-   * @param callback Function to call
-   * @param int PO_* constant
-   * @param string If PO_{BEFORE,AFTER} used, rule
-   */
-  
-  public function hook($callback, $when, $rule = false)
-  {
-    if ( !is_int($when) )
-      return null;
-    if ( ($when == PO_BEFORE || $when == PO_AFTER) && !is_string($rule) )
-      return null;
-    if ( ( is_string($callback) && !function_exists($callback) ) || ( is_array($callback) && !method_exists($callback[0], $callback[1]) ) || ( !is_string($callback) && !is_array($callback) ) )
-    {
-      trigger_error("Attempt to hook with undefined function/method " . print_r($callback, true), E_USER_ERROR);
-      return null;
-    }
-    
-    $this->hooks[] = array(
-        'callback' => $callback,
-        'when'     => $when,
-        'rule'     => $rule
-      );
-  }
-  
-  /**
-   * Disable a render stage
-   * @param string stage
-   * @return null
-   */
-  
-  public function disable_rule($rule)
-  {
-    foreach ( $this->rules as $i => $current_rule )
-    {
-      if ( $current_rule === $rule )
-      {
-        unset($this->rules[$i]);
-        return null;
-      }
-    }
-    return null;
-  }
-  
-  /**
-   * Disables all rules.
-   * @return null
-   */
-  
-  public function disable_all_rules()
-  {
-    $this->rules = array();
-    return null;
-  }
-  
-  /**
-   * Enables a rule
-   * @param string rule
-   * @return null
-   */
-   
-  public function enable_rule($rule)
-  {
-    $this->rules[] = $rule;
-    return null;
-  }
-  
-  /**
-   * Make a rule exclusive (the only one called)
-   * @param string stage
-   * @return null
-   */
-  
-  public function exclusive_rule($rule)
-  {
-    if ( is_string($rule) )
-      $this->rules = array($rule);
-    
-    return null;
-  }
-  
-  /**
-   * Generate a token
-   * @param int Token index
-   * @return string
-   * @static
-   */
-  
-  public static function generate_token($i)
-  {
-    return self::PARSER_TOKEN . $i . self::PARSER_TOKEN;
-  }
-  
-  /**
-   * Tokenize string
-   * @param string
-   * @param array Matches
-   * @return string
-   * @static
-   */
-  
-  public static function tokenize($text, $matches)
-  {
-    $matches = array_values($matches);
-    foreach ( $matches as $i => $match )
-    {
-      $text = str_replace_once($match, self::generate_token($i), $text);
-    }
-    
-    return $text;
-  }
+	/**
+ 	* Parser token
+ 	* @const string
+ 	*/
+	
+	const PARSER_TOKEN = "\xFF";
+	
+	/**
+ 	* Parsing engine
+ 	* @var string
+ 	*/
+	
+	private $parser = 'mediawiki';
+	
+	/**
+ 	* Rendering engine
+ 	* @var string
+ 	*/
+	
+	private $renderer = 'xhtml';
+	
+	/**
+ 	* Rendering flags
+ 	*/
+	
+	public $flags = RENDER_WIKI_DEFAULT;
+	
+	/**
+ 	* List of rendering rules
+ 	* @var array
+ 	*/
+	
+	private $rules = array(
+			'lang',
+			'templates',
+			'blockquote',
+			'code',
+			'tables',
+			'heading',
+			'hr',
+			// note: can't be named list ("list" is a PHP language construct)
+			'multilist',
+			'bold',
+			'italic',
+			'underline',
+			'externalwithtext',
+			'externalnotext',
+			'mailtowithtext',
+			'mailtonotext',
+			'image',
+			'internallink',
+			'paragraph',
+			'blockquotepost'
+		);
+	
+	/**
+ 	* List of render hooks
+ 	* @var array
+ 	*/
+	
+	private $hooks = array();
+	
+	/* private $rules = array('prefilter', 'delimiter', 'code', 'function', 'html', 'raw', 'include', 'embed', 'anchor',
+ 					'heading', 'toc', 'horiz', 'break', 'blockquote', 'list', 'deflist', 'table', 'image',
+ 					'phplookup', 'center', 'newline', 'paragraph', 'url', 'freelink', 'interwiki',
+ 					'wikilink', 'colortext', 'strong', 'bold', 'emphasis', 'italic', 'underline', 'tt',
+ 					'superscript', 'subscript', 'revise', 'tighten'); */
+	
+	/**
+ 	* Render the text!
+ 	* @param string Text to render
+ 	* @return string
+ 	*/
+	
+	public function render($text)
+	{
+		$parser_class = "Carpenter_Parse_" . ucwords($this->parser);
+		$renderer_class = "Carpenter_Render_" . ucwords($this->renderer);
+		
+		// empty? (don't remove this. the parser will shit bricks later about rules returning empty strings)
+		if ( trim($text) === '' )
+			return $text;
+		
+		// include files, if we haven't already
+		if ( !class_exists($parser_class) )
+		{
+			require_once( ENANO_ROOT . "/includes/wikiengine/parse_{$this->parser}.php");
+		}
+		
+		if ( !class_exists($renderer_class) )
+		{
+			require_once( ENANO_ROOT . "/includes/wikiengine/render_{$this->renderer}.php");
+		}
+		
+		$parser = new $parser_class;
+		$renderer = new $renderer_class;
+		
+		// run prehooks
+		foreach ( $this->hooks as $hook )
+		{
+			if ( $hook['when'] === PO_FIRST )
+			{
+				$text = call_user_func($hook['callback'], $text);
+				if ( !is_string($text) || empty($text) )
+				{
+					trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
+					// *sigh*
+					$text = '';
+				}
+			}
+		}
+		
+		// perform render
+		foreach ( $this->rules as $rule )
+		{
+			// run prehooks
+			foreach ( $this->hooks as $hook )
+			{
+				if ( $hook['when'] === PO_BEFORE && $hook['rule'] === $rule )
+				{
+					$text = call_user_func($hook['callback'], $text);
+					if ( !is_string($text) || empty($text) )
+					{
+						trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
+						// *sigh*
+						$text = '';
+					}
+				}
+			}
+			
+			// execute rule
+			$text_before = $text;
+			$text = $this->perform_render_step($text, $rule, $parser, $renderer);
+			if ( empty($text) )
+			{
+				trigger_error("Wikitext was completely empty after rule \"$rule\"; restoring backup", E_USER_WARNING);
+				$text = $text_before;
+			}
+			unset($text_before);
+			
+			// run posthooks
+			foreach ( $this->hooks as $hook )
+			{
+				if ( $hook['when'] === PO_AFTER && $hook['rule'] === $rule )
+				{
+					$text = call_user_func($hook['callback'], $text);
+					if ( !is_string($text) || empty($text) )
+					{
+						trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
+						// *sigh*
+						$text = '';
+					}
+				}
+			}
+			
+			RenderMan::tag_strip_push('final', $text, $final_stripdata);
+		}
+		
+		RenderMan::tag_unstrip('final', $text, $final_stripdata);
+		
+		// run posthooks
+		foreach ( $this->hooks as $hook )
+		{
+			if ( $hook['when'] === PO_LAST )
+			{
+				$text = call_user_func($hook['callback'], $text);
+				if ( !is_string($text) || empty($text) )
+				{
+					trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
+					// *sigh*
+					$text = '';
+				}
+			}
+		}
+		
+		return (( defined('ENANO_DEBUG') && isset($_GET['parserdebug']) ) ? '<pre>' . htmlspecialchars($text) . '</pre>' : $text) . "\n\n";
+	}
+	
+	/**
+ 	* Performs a step in the rendering process.
+ 	* @param string Text to render
+ 	* @param string Rule to execute
+ 	* @param object Parser instance
+ 	* @param object Renderer instance
+ 	* @return string
+ 	* @access private
+ 	*/
+	
+	private function perform_render_step($text, $rule, $parser, $renderer)
+	{
+		// First look for a direct function
+		if ( function_exists("parser_{$this->parser}_{$this->renderer}_{$rule}") )
+		{
+			return call_user_func("parser_{$this->parser}_{$this->renderer}_{$rule}", $text, $this->flags);
+		}
+		
+		// We don't have that, so start looking for other ways or means of doing this
+		if ( method_exists($parser, $rule) && method_exists($renderer, $rule) )
+		{
+			// Both the parser and render have callbacks they want to use.
+			$pieces = $parser->$rule($text);
+			$text = call_user_func(array($renderer, $rule), $text, $pieces);
+		}
+		else if ( method_exists($parser, $rule) && !method_exists($renderer, $rule) && isset($renderer->rules[$rule]) )
+		{
+			// The parser has a callback, but the renderer does not
+			$pieces = $parser->$rule($text);
+			$text = $this->generic_render($text, $pieces, $renderer->rules[$rule]);
+		}
+		else if ( !method_exists($parser, $rule) && isset($parser->rules[$rule]) && method_exists($renderer, $rule) )
+		{
+			// The parser has no callback, but the renderer does
+			$text = preg_replace_callback($parser->rules[$rule], array($renderer, $rule), $text);
+		}
+		else if ( isset($parser->rules[$rule]) && isset($renderer->rules[$rule]) )
+		{
+			// This is a straight-up regex only rule
+			$text = preg_replace($parser->rules[$rule], $renderer->rules[$rule], $text);
+		}
+		else
+		{
+			// Either the renderer or parser does not support this rule, ignore it
+		}
+		
+		return $text;
+	}
+	
+	/**
+ 	* Generic renderer
+ 	* @access protected
+ 	*/
+	
+	protected function generic_render($text, $pieces, $rule)
+	{
+		foreach ( $pieces as $i => $piece )
+		{
+			$replacement = $rule;
+			
+			// if the piece is an array, replace $1, $2, etc. in the rule with each value in the piece
+			if ( is_array($piece) )
+			{
+				$j = 0;
+				foreach ( $piece as $part )
+				{
+					$j++;
+					$replacement = str_replace(array("\\$j", "\${$j}"), $part, $replacement);
+				}
+			}
+			// else, just replace \\1 or $1 in the rule with the piece
+			else
+			{
+				$replacement = str_replace(array("\\1", "\$1"), $piece, $replacement);
+			}
+			
+			$text = str_replace(self::generate_token($i), $replacement, $text);
+		}
+		
+		return $text;
+	}
+	
+	/**
+ 	* Add a hook into the parser.
+ 	* @param callback Function to call
+ 	* @param int PO_* constant
+ 	* @param string If PO_{BEFORE,AFTER} used, rule
+ 	*/
+	
+	public function hook($callback, $when, $rule = false)
+	{
+		if ( !is_int($when) )
+			return null;
+		if ( ($when == PO_BEFORE || $when == PO_AFTER) && !is_string($rule) )
+			return null;
+		if ( ( is_string($callback) && !function_exists($callback) ) || ( is_array($callback) && !method_exists($callback[0], $callback[1]) ) || ( !is_string($callback) && !is_array($callback) ) )
+		{
+			trigger_error("Attempt to hook with undefined function/method " . print_r($callback, true), E_USER_ERROR);
+			return null;
+		}
+		
+		$this->hooks[] = array(
+				'callback' => $callback,
+				'when'     => $when,
+				'rule'     => $rule
+			);
+	}
+	
+	/**
+ 	* Disable a render stage
+ 	* @param string stage
+ 	* @return null
+ 	*/
+	
+	public function disable_rule($rule)
+	{
+		foreach ( $this->rules as $i => $current_rule )
+		{
+			if ( $current_rule === $rule )
+			{
+				unset($this->rules[$i]);
+				return null;
+			}
+		}
+		return null;
+	}
+	
+	/**
+ 	* Disables all rules.
+ 	* @return null
+ 	*/
+	
+	public function disable_all_rules()
+	{
+		$this->rules = array();
+		return null;
+	}
+	
+	/**
+ 	* Enables a rule
+ 	* @param string rule
+ 	* @return null
+ 	*/
+ 	
+	public function enable_rule($rule)
+	{
+		$this->rules[] = $rule;
+		return null;
+	}
+	
+	/**
+ 	* Make a rule exclusive (the only one called)
+ 	* @param string stage
+ 	* @return null
+ 	*/
+	
+	public function exclusive_rule($rule)
+	{
+		if ( is_string($rule) )
+			$this->rules = array($rule);
+		
+		return null;
+	}
+	
+	/**
+ 	* Generate a token
+ 	* @param int Token index
+ 	* @return string
+ 	* @static
+ 	*/
+	
+	public static function generate_token($i)
+	{
+		return self::PARSER_TOKEN . $i . self::PARSER_TOKEN;
+	}
+	
+	/**
+ 	* Tokenize string
+ 	* @param string
+ 	* @param array Matches
+ 	* @return string
+ 	* @static
+ 	*/
+	
+	public static function tokenize($text, $matches)
+	{
+		$matches = array_values($matches);
+		foreach ( $matches as $i => $match )
+		{
+			$text = str_replace_once($match, self::generate_token($i), $text);
+		}
+		
+		return $text;
+	}
 }
 
--- a/index.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/index.php	Sun Mar 28 23:10:46 2010 -0400
@@ -30,7 +30,7 @@
 
 if($aggressive_optimize_html || $do_gzip)
 {
-  ob_start();
+	ob_start();
 }
 
 global $db, $session, $paths, $template, $plugins; // Common objects
@@ -38,659 +38,659 @@
 
 if ( !isset($_GET['do']) )
 {
-  $_GET['do'] = 'view';
+	$_GET['do'] = 'view';
 }
 switch($_GET['do'])
 {
-  default:
-    $code = $plugins->setHook('page_action');
-    ob_start();
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    if ( $contents = ob_get_contents() )
-    {
-      ob_end_clean();
-      echo $contents;
-    }
-    else
-    {
-      die_friendly('Invalid action', '<p>The action "'.htmlspecialchars($_GET['do']).'" is not defined. Return to <a href="'.makeUrl($paths->page).'">viewing this page\'s text</a>.</p>');
-    }
-    break;
-  case 'view':
-    // echo PageUtils::getpage($paths->page, true, ( (isset($_GET['oldid'])) ? $_GET['oldid'] : false ));
-    $rev_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
-    $page = new PageProcessor( $paths->page_id, $paths->namespace, $rev_id );
-    // Feed this PageProcessor to the template processor. This prevents $template from starting another
-    // PageProcessor when we already have one going.
-    $template->set_page($page);
-    $page->send_headers = true;
-    $page->allow_redir = ( !isset($_GET['redirect']) || (isset($_GET['redirect']) && $_GET['redirect'] !== 'no') );
-    $pagepass = ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '';
-    $page->password = $pagepass;
-    $page->send(true);
-    $page_timestamp = $page->revision_time;
-    break;
-  case 'comments':
-    $output->header();
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    $sub = ( isset ($_GET['sub']) ) ? $_GET['sub'] : false;
-    switch($sub)
-    {
-      case 'admin':
-      default:
-        $act = ( isset ($_GET['action']) ) ? $_GET['action'] : false;
-        $id = ( isset ($_GET['id']) ) ? intval($_GET['id']) : -1;
-        echo PageUtils::comments_html($paths->page_id, $paths->namespace, $act, Array('id'=>$id));
-        break;
-      case 'postcomment':
-        if(empty($_POST['name']) ||
-           empty($_POST['subj']) ||
-           empty($_POST['text'])
-           ) { echo 'Invalid request'; break; }
-        $cid = ( isset($_POST['captcha_id']) ) ? $_POST['captcha_id'] : false;
-        $cin = ( isset($_POST['captcha_input']) ) ? $_POST['captcha_input'] : false;
-        
-        require_once('includes/comment.php');
-        $comments = new Comments($paths->page_id, $paths->namespace);
-        
-        $submission = array(
-            'mode' => 'submit',
-            'captcha_id' => $cid,
-            'captcha_code' => $cin,
-            'name' => $_POST['name'],
-            'subj' => $_POST['subj'],
-            'text' => $_POST['text'],
-          );
-        
-        $result = $comments->process_json($submission);
-        if ( $result['mode'] == 'error' )
-        {
-          echo '<div class="error-box">' . htmlspecialchars($result['error']) . '</div>';
-        }
-        else
-        {
-          echo '<div class="info-box">' . $lang->get('comment_msg_comment_posted') . '</div>';
-        }
-        
-        echo PageUtils::comments_html($paths->page_id, $paths->namespace);
-        break;
-      case 'editcomment':
-        if(!isset($_GET['id']) || ( isset($_GET['id']) && !preg_match('#^([0-9]+)$#', $_GET['id']) )) { echo '<p>Invalid comment ID</p>'; break; }
-        $q = $db->sql_query('SELECT subject,comment_data,comment_id FROM '.table_prefix.'comments WHERE comment_id='.$_GET['id']);
-        if(!$q) $db->_die('The comment data could not be selected.');
-        $row = $db->fetchrow();
-        $db->free_result();
-        $row['subject'] = str_replace('\'', '&#039;', $row['subject']);
-        echo '<form action="'.makeUrl($paths->page, 'do=comments&amp;sub=savecomment').'" method="post">';
-        echo "<br /><div class='tblholder'><table border='0' width='100%' cellspacing='1' cellpadding='4'>
-                <tr><td class='row1'>" . $lang->get('comment_postform_field_subject') . "</td><td class='row1'><input type='text' name='subj' value='{$row['subject']}' /></td></tr>
-                <tr><td class='row2'>" . $lang->get('comment_postform_field_comment') . "</td><td class='row2'><textarea rows='10' cols='40' style='width: 98%;' name='text'>{$row['comment_data']}</textarea></td></tr>
-                <tr><td class='row1' colspan='2' class='row1' style='text-align: center;'><input type='hidden' name='id' value='{$row['comment_id']}' /><input type='submit' value='" . $lang->get('etc_save_changes') . "' /></td></tr>
-              </table></div>";
-        echo '</form>';
-        break;
-      case 'savecomment':
-        if(empty($_POST['subj']) || empty($_POST['text'])) { echo '<p>Invalid request</p>'; break; }
-        $r = PageUtils::savecomment_neater($paths->page_id, $paths->namespace, $_POST['subj'], $_POST['text'], (int)$_POST['id']);
-        if($r != 'good') { echo "<pre>$r</pre>"; break; }
-        echo PageUtils::comments_html($paths->page_id, $paths->namespace);
-        break;
-      case 'deletecomment':
-        if(!empty($_GET['id']))
-        {
-          PageUtils::deletecomment_neater($paths->page_id, $paths->namespace, (int)$_GET['id']);
-        }
-        echo PageUtils::comments_html($paths->page_id, $paths->namespace);
-        break;
-    }
-    $output->footer();
-    break;
-  case 'edit':
-    if(isset($_POST['_cancel']))
-    {
-      redirect(makeUrl($paths->page), '', '', 0);
-      break;
-    }
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    if(isset($_POST['_save']))
-    {
-      $captcha_valid = true;
-      if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
-      {
-        $captcha_valid = false;
-        if ( isset($_POST['captcha_id']) && isset($_POST['captcha_code']) )
-        {
-          $hash_correct = strtolower($session->get_captcha($_POST['captcha_id']));
-          $hash_input   = strtolower($_POST['captcha_code']);
-          if ( $hash_input === $hash_correct )
-            $captcha_valid = true;
-        }
-      }
-      if ( $captcha_valid )
-      {
-        $e = PageUtils::savepage($paths->page_id, $paths->namespace, $_POST['page_text'], $_POST['edit_summary'], isset($_POST['minor']));
-        if ( $e == 'good' )
-        {
-          redirect(makeUrl($paths->page), $lang->get('editor_msg_save_success_title'), $lang->get('editor_msg_save_success_body'), 3);
-        }
-      }
-    }
-    $template->header();
-    if ( isset($captcha_valid) )
-    {
-      echo '<div class="usermessage">' . $lang->get('editor_err_captcha_wrong') . '</div>';
-    }
-    if(isset($_POST['_preview']))
-    {
-      $text = $_POST['page_text'];
-      $edsumm = $_POST['edit_summary'];
-      echo PageUtils::genPreview($_POST['page_text']);
-      $text = htmlspecialchars($text);
-      $revid = 0;
-    }
-    else
-    {
-      $revid = ( isset($_GET['revid']) ) ? intval($_GET['revid']) : 0;
-      $page = new PageProcessor($paths->page_id, $paths->namespace, $revid);
-      $text = $page->fetch_source();
-      $edsumm = '';
-      // $text = RenderMan::getPage($paths->cpage['urlname_nons'], $paths->namespace, 0, false, false, false, false);
-    }
-    if ( $revid > 0 )
-    {
-      $time = $page->revision_time;
-      // Retrieve information about this revision and the current one
-      $q = $db->sql_query('SELECT l1.author AS currentrev_author, l2.author AS oldrev_author FROM ' . table_prefix . 'logs AS l1
+	default:
+		$code = $plugins->setHook('page_action');
+		ob_start();
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		if ( $contents = ob_get_contents() )
+		{
+			ob_end_clean();
+			echo $contents;
+		}
+		else
+		{
+			die_friendly('Invalid action', '<p>The action "'.htmlspecialchars($_GET['do']).'" is not defined. Return to <a href="'.makeUrl($paths->page).'">viewing this page\'s text</a>.</p>');
+		}
+		break;
+	case 'view':
+		// echo PageUtils::getpage($paths->page, true, ( (isset($_GET['oldid'])) ? $_GET['oldid'] : false ));
+		$rev_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
+		$page = new PageProcessor( $paths->page_id, $paths->namespace, $rev_id );
+		// Feed this PageProcessor to the template processor. This prevents $template from starting another
+		// PageProcessor when we already have one going.
+		$template->set_page($page);
+		$page->send_headers = true;
+		$page->allow_redir = ( !isset($_GET['redirect']) || (isset($_GET['redirect']) && $_GET['redirect'] !== 'no') );
+		$pagepass = ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '';
+		$page->password = $pagepass;
+		$page->send(true);
+		$page_timestamp = $page->revision_time;
+		break;
+	case 'comments':
+		$output->header();
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		$sub = ( isset ($_GET['sub']) ) ? $_GET['sub'] : false;
+		switch($sub)
+		{
+			case 'admin':
+			default:
+				$act = ( isset ($_GET['action']) ) ? $_GET['action'] : false;
+				$id = ( isset ($_GET['id']) ) ? intval($_GET['id']) : -1;
+				echo PageUtils::comments_html($paths->page_id, $paths->namespace, $act, Array('id'=>$id));
+				break;
+			case 'postcomment':
+				if(empty($_POST['name']) ||
+ 					empty($_POST['subj']) ||
+ 					empty($_POST['text'])
+ 					) { echo 'Invalid request'; break; }
+				$cid = ( isset($_POST['captcha_id']) ) ? $_POST['captcha_id'] : false;
+				$cin = ( isset($_POST['captcha_input']) ) ? $_POST['captcha_input'] : false;
+				
+				require_once('includes/comment.php');
+				$comments = new Comments($paths->page_id, $paths->namespace);
+				
+				$submission = array(
+						'mode' => 'submit',
+						'captcha_id' => $cid,
+						'captcha_code' => $cin,
+						'name' => $_POST['name'],
+						'subj' => $_POST['subj'],
+						'text' => $_POST['text'],
+					);
+				
+				$result = $comments->process_json($submission);
+				if ( $result['mode'] == 'error' )
+				{
+					echo '<div class="error-box">' . htmlspecialchars($result['error']) . '</div>';
+				}
+				else
+				{
+					echo '<div class="info-box">' . $lang->get('comment_msg_comment_posted') . '</div>';
+				}
+				
+				echo PageUtils::comments_html($paths->page_id, $paths->namespace);
+				break;
+			case 'editcomment':
+				if(!isset($_GET['id']) || ( isset($_GET['id']) && !preg_match('#^([0-9]+)$#', $_GET['id']) )) { echo '<p>Invalid comment ID</p>'; break; }
+				$q = $db->sql_query('SELECT subject,comment_data,comment_id FROM '.table_prefix.'comments WHERE comment_id='.$_GET['id']);
+				if(!$q) $db->_die('The comment data could not be selected.');
+				$row = $db->fetchrow();
+				$db->free_result();
+				$row['subject'] = str_replace('\'', '&#039;', $row['subject']);
+				echo '<form action="'.makeUrl($paths->page, 'do=comments&amp;sub=savecomment').'" method="post">';
+				echo "<br /><div class='tblholder'><table border='0' width='100%' cellspacing='1' cellpadding='4'>
+								<tr><td class='row1'>" . $lang->get('comment_postform_field_subject') . "</td><td class='row1'><input type='text' name='subj' value='{$row['subject']}' /></td></tr>
+								<tr><td class='row2'>" . $lang->get('comment_postform_field_comment') . "</td><td class='row2'><textarea rows='10' cols='40' style='width: 98%;' name='text'>{$row['comment_data']}</textarea></td></tr>
+								<tr><td class='row1' colspan='2' class='row1' style='text-align: center;'><input type='hidden' name='id' value='{$row['comment_id']}' /><input type='submit' value='" . $lang->get('etc_save_changes') . "' /></td></tr>
+							</table></div>";
+				echo '</form>';
+				break;
+			case 'savecomment':
+				if(empty($_POST['subj']) || empty($_POST['text'])) { echo '<p>Invalid request</p>'; break; }
+				$r = PageUtils::savecomment_neater($paths->page_id, $paths->namespace, $_POST['subj'], $_POST['text'], (int)$_POST['id']);
+				if($r != 'good') { echo "<pre>$r</pre>"; break; }
+				echo PageUtils::comments_html($paths->page_id, $paths->namespace);
+				break;
+			case 'deletecomment':
+				if(!empty($_GET['id']))
+				{
+					PageUtils::deletecomment_neater($paths->page_id, $paths->namespace, (int)$_GET['id']);
+				}
+				echo PageUtils::comments_html($paths->page_id, $paths->namespace);
+				break;
+		}
+		$output->footer();
+		break;
+	case 'edit':
+		if(isset($_POST['_cancel']))
+		{
+			redirect(makeUrl($paths->page), '', '', 0);
+			break;
+		}
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		if(isset($_POST['_save']))
+		{
+			$captcha_valid = true;
+			if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
+			{
+				$captcha_valid = false;
+				if ( isset($_POST['captcha_id']) && isset($_POST['captcha_code']) )
+				{
+					$hash_correct = strtolower($session->get_captcha($_POST['captcha_id']));
+					$hash_input   = strtolower($_POST['captcha_code']);
+					if ( $hash_input === $hash_correct )
+						$captcha_valid = true;
+				}
+			}
+			if ( $captcha_valid )
+			{
+				$e = PageUtils::savepage($paths->page_id, $paths->namespace, $_POST['page_text'], $_POST['edit_summary'], isset($_POST['minor']));
+				if ( $e == 'good' )
+				{
+					redirect(makeUrl($paths->page), $lang->get('editor_msg_save_success_title'), $lang->get('editor_msg_save_success_body'), 3);
+				}
+			}
+		}
+		$template->header();
+		if ( isset($captcha_valid) )
+		{
+			echo '<div class="usermessage">' . $lang->get('editor_err_captcha_wrong') . '</div>';
+		}
+		if(isset($_POST['_preview']))
+		{
+			$text = $_POST['page_text'];
+			$edsumm = $_POST['edit_summary'];
+			echo PageUtils::genPreview($_POST['page_text']);
+			$text = htmlspecialchars($text);
+			$revid = 0;
+		}
+		else
+		{
+			$revid = ( isset($_GET['revid']) ) ? intval($_GET['revid']) : 0;
+			$page = new PageProcessor($paths->page_id, $paths->namespace, $revid);
+			$text = $page->fetch_source();
+			$edsumm = '';
+			// $text = RenderMan::getPage($paths->cpage['urlname_nons'], $paths->namespace, 0, false, false, false, false);
+		}
+		if ( $revid > 0 )
+		{
+			$time = $page->revision_time;
+			// Retrieve information about this revision and the current one
+			$q = $db->sql_query('SELECT l1.author AS currentrev_author, l2.author AS oldrev_author FROM ' . table_prefix . 'logs AS l1
 LEFT JOIN ' . table_prefix . 'logs AS l2
-  ON ( l2.log_id = ' . $revid . '
-       AND l2.log_type  = \'page\'
-       AND l2.action    = \'edit\'
-       AND l2.page_id   = \'' . $db->escape($paths->page_id) . '\'
-       AND l2.namespace = \'' . $db->escape($paths->namespace) . '\'
-       AND l1.is_draft != 1
-      )
+	ON ( l2.log_id = ' . $revid . '
+ 			AND l2.log_type  = \'page\'
+ 			AND l2.action    = \'edit\'
+ 			AND l2.page_id   = \'' . $db->escape($paths->page_id) . '\'
+ 			AND l2.namespace = \'' . $db->escape($paths->namespace) . '\'
+ 			AND l1.is_draft != 1
+			)
 WHERE l1.log_type  = \'page\'
-  AND l1.action    = \'edit\'
-  AND l1.page_id   = \'' . $db->escape($paths->page_id) . '\'
-  AND l1.namespace = \'' . $db->escape($paths->namespace) . '\'
-  AND l1.time_id > ' . $time . '
-  AND l1.is_draft != 1
+	AND l1.action    = \'edit\'
+	AND l1.page_id   = \'' . $db->escape($paths->page_id) . '\'
+	AND l1.namespace = \'' . $db->escape($paths->namespace) . '\'
+	AND l1.time_id > ' . $time . '
+	AND l1.is_draft != 1
 ORDER BY l1.time_id DESC;');
-      if ( !$q )
-        $db->die_json();
-      
-      if ( $db->numrows() > 0 )
-      {
-        echo '<div class="usermessage">' . $lang->get('editor_msg_editing_old_revision') . '</div>';
-        
-        $rev_count = $db->numrows() - 2;
-        $row = $db->fetchrow();
-        $undo_info = array(
-          'old_author'     => $row['oldrev_author'],
-          'current_author' => $row['currentrev_author'],
-          'undo_count'     => max($rev_count, 1),
-          'last_rev_id'    => $revid
-        );
-      }
-      else
-      {
-        $revid = 0;
-      }
-      $db->free_result();
-    }
-    echo '
-      <form action="'.makeUrl($paths->page, 'do=edit').'" method="post" enctype="multipart/form-data">
-      <br />
-      <textarea name="page_text" rows="20" cols="60" style="width: 97%;">'.$text.'</textarea><br />
-      <br />
-      ';
-    $edsumm = ( $revid > 0 ) ? $lang->get('editor_reversion_edit_summary', $undo_info) : $edsumm;
-    echo $lang->get('editor_lbl_edit_summary') . ' <input name="edit_summary" type="text" size="40" value="' . htmlspecialchars($edsumm) . '" /><br /><label><input type="checkbox" name="minor" /> ' . $lang->get('editor_lbl_minor_edit_field') . '</label><br />';
-    if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
-    {
-      echo '<br /><table border="0"><tr><td>';
-      echo '<b>' . $lang->get('editor_lbl_field_captcha') . '</b><br />'
-           . '<br />'
-           . $lang->get('editor_msg_captcha_pleaseenter') . '<br /><br />'
-           . $lang->get('editor_msg_captcha_blind');
-      echo '</td><td>';
-      $hash = $session->make_captcha();
-      echo '<img src="' . makeUrlNS('Special', "Captcha/$hash") . '" onclick="this.src+=\'/a\'" style="cursor: pointer;" /><br />';
-      echo '<input type="hidden" name="captcha_id" value="' . $hash . '" />';
-      echo $lang->get('editor_lbl_field_captcha_code') . ' <input type="text" name="captcha_code" value="" size="9" />';
-      echo '</td></tr></table>';
-    }
-    echo '<br />
-        <input type="submit" name="_save"    value="' . $lang->get('editor_btn_save') . '" style="font-weight: bold;" />
-        <input type="submit" name="_preview" value="' . $lang->get('editor_btn_preview') . '" />
-        <input type="submit" name="_revert"  value="' . $lang->get('editor_btn_revert') . '" />
-        <input type="submit" name="_cancel"  value="' . $lang->get('editor_btn_cancel') . '" />
-      </form>
-    ';
-    if ( getConfig('wiki_edit_notice', '0') == '1' )
-    {
-      $notice = getConfig('wiki_edit_notice_text');
-      echo RenderMan::render($notice);
-    }
-    $template->footer();
-    break;
-  case 'viewsource':
-    $template->header();
-    $text = RenderMan::getPage($paths->page_id, $paths->namespace, 0, false, false, false, false);
-    $text = htmlspecialchars($text);
-    echo '
-      <form action="'.makeUrl($paths->page, 'do=edit').'" method="post">
-      <br />
-      <textarea readonly="readonly" name="page_text" rows="20" cols="60" style="width: 97%;">'.$text.'</textarea>';
-    echo '<br />
-        <input type="submit" name="_cancel" value="' . $lang->get('editor_btn_closeviewer') . '" />
-      </form>
-    ';
-    $template->footer();
-    break;
-  case 'history':
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    $hist = PageUtils::histlist($paths->page_id, $paths->namespace);
-    $template->header();
-    echo $hist;
-    $template->footer();
-    break;
-  case 'rollback':
-    $id = (isset($_GET['id'])) ? $_GET['id'] : false;
-    if(!$id || !ctype_digit($id)) die_friendly('Invalid action ID', '<p>The URL parameter "id" is not an integer. Exiting to prevent nasties like SQL injection, etc.</p>');
-    
-    $id = intval($id);
-    
-    $page = new PageProcessor($paths->page_id, $paths->namespace);
-    $result = $page->rollback_log_entry($id);
-    
-    if ( $result['success'] )
-    {
-      $result = $lang->get("page_msg_rb_success_{$result['action']}", array('dateline' => $result['dateline']));
-    }
-    else
-    {
-      $result = $lang->get("page_err_{$result['error']}", array('action' => @$result['action']));
-    }
-    
-    $template->header();
-    echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a></p>';
-    $template->footer();
-    break;
-  case 'catedit':
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    if(isset($_POST['__enanoSaveButton']))
-    {
-      unset($_POST['__enanoSaveButton']);
-      $val = PageUtils::catsave($paths->page_id, $paths->namespace, $_POST);
-      if($val == 'GOOD')
-      {
-        header('Location: '.makeUrl($paths->page)); echo '<html><head><title>Redirecting...</title></head><body>If you haven\'t been redirected yet, <a href="'.makeUrl($paths->page).'">click here</a>.'; break;
-      } else {
-        die_friendly('Error saving category information', '<p>'.$val.'</p>');
-      }
-    }
-    elseif(isset($_POST['__enanoCatCancel']))
-    {
-      header('Location: '.makeUrl($paths->page)); echo '<html><head><title>Redirecting...</title></head><body>If you haven\'t been redirected yet, <a href="'.makeUrl($paths->page).'">click here</a>.'; break;
-    }
-    $template->header();
-    $c = PageUtils::catedit_raw($paths->page_id, $paths->namespace);
-    echo $c[1];
-    $template->footer();
-    break;
-  case 'moreoptions':
-    $template->header();
-    echo '<div class="menu_nojs" style="width: 150px; padding: 0;"><ul style="display: block;"><li><div class="label">' . $lang->get('ajax_lbl_moreoptions_nojs') . '</div><div style="clear: both;"></div></li>'.$template->toolbar_menu.'</ul></div>';
-    $template->footer();
-    break;
-  case 'protect':
-    if ( !$session->sid_super )
-    {
-      redirect(makeUrlNS('Special', "Login/{$paths->page}", 'target_do=protect&level=' . $session->user_level, false), $lang->get('etc_access_denied_short'), $lang->get('etc_access_denied_need_reauth'), 0);
-    }
-    
-    if ( isset($_POST['level']) && isset($_POST['reason']) )
-    {
-      $level = intval($_POST['level']);
-      if ( !in_array($level, array(PROTECT_FULL, PROTECT_SEMI, PROTECT_NONE)) )
-      {
-        $errors[] = 'bad level';
-      }
-      $reason = trim($_POST['reason']);
-      if ( empty($reason) )
-      {
-        $errors[] = $lang->get('onpage_protect_err_need_reason');
-      }
-      
-      $page = new PageProcessor($paths->page_id, $paths->namespace);
-      $result = $page->protect_page($level, $reason);
-      if ( $result['success'] )
-      {
-        redirect(makeUrl($paths->page), $lang->get('page_protect_lbl_success_title'), $lang->get('page_protect_lbl_success_body', array('page_link' => makeUrl($paths->page, false, true))), 3);
-      }
-      else
-      {
-        $errors[] = $lang->get('page_err_' . $result['error']);
-      }
-    }
-    $template->header();
-    ?>
-    <form action="<?php echo makeUrl($paths->page, 'do=protect'); ?>" method="post">
-      <h3><?php echo $lang->get('onpage_protect_heading'); ?></h3>
-      <p><?php echo $lang->get('onpage_protect_msg_select_level'); ?></p>
-      
-      <?php
-      if ( !empty($errors) )
-      {
-        echo '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>';
-      }
-      ?>
-      
-      <div class="protectlevel" style="line-height: 22px; margin-left: 17px;">
-        <label>
-          <input type="radio" name="level" value="<?php echo PROTECT_FULL; ?>" />
-          <?php echo gen_sprite(cdnPath . '/images/protect-icons.png', 22, 22, 0, 0); ?>
-          <?php echo $lang->get('onpage_protect_btn_full'); ?>
-        </label>
-      </div>
-      <div class="protectlevel_hint" style="font-size: smaller; margin-left: 68px;">
-        <?php echo $lang->get('onpage_protect_btn_full_hint'); ?>
-      </div>
-      
-      <div class="protectlevel" style="line-height: 22px; margin-left: 17px;">
-        <label>
-          <input type="radio" name="level" value="<?php echo PROTECT_SEMI; ?>" />
-          <?php echo gen_sprite(cdnPath . '/images/protect-icons.png', 22, 22, 22, 0); ?>
-          <?php echo $lang->get('onpage_protect_btn_semi'); ?>
-        </label>
-      </div>
-      <div class="protectlevel_hint" style="font-size: smaller; margin-left: 68px;">
-        <?php echo $lang->get('onpage_protect_btn_semi_hint'); ?>
-      </div>
-      
-      <div class="protectlevel" style="line-height: 22px; margin-left: 17px;">
-        <label>
-          <input type="radio" name="level" value="<?php echo PROTECT_NONE; ?>" />
-          <?php echo gen_sprite(cdnPath . '/images/protect-icons.png', 22, 22, 44, 0); ?>
-          <?php echo $lang->get('onpage_protect_btn_none'); ?>
-        </label>
-      </div>
-      <div class="protectlevel_hint" style="font-size: smaller; margin-left: 68px;">
-        <?php echo $lang->get('onpage_protect_btn_none_hint'); ?>
-      </div>
-      
-      <table style="margin-left: 1em;" cellspacing="10">
-        <tr>
-          <td valign="top">
-            <?php echo $lang->get('onpage_protect_lbl_reason'); ?>
-          </td>
-          <td>
-            <input type="text" name="reason" size="40" /><br />
-            <small><?php echo $lang->get('onpage_protect_lbl_reason_hint'); ?></small>
-          </td>
-        </tr>
-      </table>
-                            
-      <p>
-        <input type="submit" value="<?php echo htmlspecialchars($lang->get('page_protect_btn_submit')) ?>" style="font-weight: bold;" />
-        <a class="abutton" href="<?php echo makeUrl($paths->page, false, true); ?>"><?php echo $lang->get('etc_cancel'); ?></a>
-      </p> 
-    </form>
-    <?php
-    $template->footer();
-    break;
-  case 'rename':
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    if(!empty($_POST['newname']))
-    {
-      $r = PageUtils::rename($paths->page_id, $paths->namespace, $_POST['newname']);
-      die_friendly($lang->get('page_rename_success_title'), '<p>'.nl2br($r).' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>');
-    }
-    $template->header();
-    ?>
-    <form action="<?php echo makeUrl($paths->page, 'do=rename'); ?>" method="post">
-      <?php if(isset($_POST['newname'])) echo '<p style="color: red;">' . $lang->get('page_rename_err_need_name') . '</p>'; ?>
-      <p><?php echo $lang->get('page_rename_lbl'); ?></p>
-      <p><input type="text" name="newname" size="40" /></p>
-      <p><input type="submit" value="<?php echo htmlspecialchars($lang->get('page_rename_btn_submit')); ?>" style="font-weight: bold;" /></p> 
-    </form>
-    <?php
-    $template->footer();    
-    break;
-  case 'flushlogs':
-    if(!$session->get_permissions('clear_logs'))
-    {
-      die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
-    }
-    if ( !$session->sid_super )
-    {
-      redirect(makeUrlNS('Special', "Login/{$paths->page}", 'target_do=flushlogs&level=' . $session->user_level, false), $lang->get('etc_access_denied_short'), $lang->get('etc_access_denied_need_reauth'), 0);
-    }
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    if(isset($_POST['_downthejohn']))
-    {
-      $template->header();
-        $result = PageUtils::flushlogs($paths->page_id, $paths->namespace);
-        echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>';
-      $template->footer();
-      break;
-    }
-    $template->header();
-      ?>
-      <form action="<?php echo makeUrl($paths->page, 'do=flushlogs'); ?>" method="post">
-         <?php echo $lang->get('page_flushlogs_warning_stern'); ?>
-         <p><input type="submit" name="_downthejohn" value="<?php echo htmlspecialchars($lang->get('page_flushlogs_btn_submit')); ?>" style="color: red; font-weight: bold;" /></p>
-      </form>
-      <?php
-    $template->footer();
-    break;
-  case 'delvote':
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    if(isset($_POST['_ballotbox']))
-    {
-      $template->header();
-      $result = PageUtils::delvote($paths->page_id, $paths->namespace);
-      echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>';
-      $template->footer();
-      break;
-    }
-    $template->header();
-      ?>
-      <form action="<?php echo makeUrl($paths->page, 'do=delvote'); ?>" method="post">
-         <?php
-           echo $lang->get('page_delvote_warning_stern');
-           echo '<p>';
-           switch($paths->cpage['delvotes'])
-           {
-             case 0:  echo $lang->get('page_delvote_count_zero'); break;
-             case 1:  echo $lang->get('page_delvote_count_one'); break;
-             default: echo $lang->get('page_delvote_count_plural', array('delvotes' => $paths->cpage['delvotes'])); break;
-           }
-           echo '</p>';
-         ?>
-         <p><input type="submit" name="_ballotbox" value="<?php echo htmlspecialchars($lang->get('page_delvote_btn_submit')); ?>" /></p>
-      </form>
-      <?php
-    $template->footer();
-    break;
-  case 'resetvotes':
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    if(!$session->get_permissions('vote_reset'))
-    {
-      die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
-    }
-    if(isset($_POST['_youmaylivealittlelonger']))
-    {
-      $template->header();
-        $result = PageUtils::resetdelvotes($paths->page_id, $paths->namespace);
-        echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>';
-      $template->footer();
-      break;
-    }
-    $template->header();
-      ?>
-      <form action="<?php echo makeUrl($paths->page, 'do=resetvotes'); ?>" method="post">
-        <p><?php echo $lang->get('ajax_delvote_reset_confirm'); ?></p>
-        <p><input type="submit" name="_youmaylivealittlelonger" value="<?php echo htmlspecialchars($lang->get('page_delvote_reset_btn_submit')); ?>" /></p>
-      </form>
-      <?php
-    $template->footer();
-    break;
-  case 'deletepage':
-    if ( !$session->get_permissions('delete_page') )
-    {
-      die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
-    }
-    if ( !$session->sid_super )
-    {
-      redirect(makeUrlNS('Special', "Login/{$paths->page}", 'target_do=deletepage&level=' . $session->user_level, false), $lang->get('etc_access_denied_short'), $lang->get('etc_access_denied_need_reauth'), 0);
-    }
-    
-    require_once(ENANO_ROOT . '/includes/pageutils.php');
-    if ( isset($_POST['_adiossucker']) )
-    {
-      $reason = ( isset($_POST['reason']) ) ? $_POST['reason'] : false;
-      if ( empty($reason) )
-        $error = $lang->get('ajax_delete_prompt_reason');
-      else
-      {
-        $template->header();
-          $result = PageUtils::deletepage($paths->page_id, $paths->namespace, $reason);
-          echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>';
-        $template->footer();
-        break;
-      }
-    }
-    $template->header();
-      ?>
-      <form action="<?php echo makeUrl($paths->page, 'do=deletepage'); ?>" method="post">
-         <?php echo $lang->get('page_delete_warning_stern'); ?>
-         <?php if ( isset($error) ) echo "<p>$error</p>"; ?>
-         <p><?php echo $lang->get('page_delete_lbl_reason'); ?> <input type="text" name="reason" size="50" /></p>
-         <p><input type="submit" name="_adiossucker" value="<?php echo htmlspecialchars($lang->get('page_delete_btn_submit')); ?>" style="font-weight: bold;" /></p>
-      </form>
-      <?php
-    $template->footer();
-    break;
-  case 'setwikimode':
-    if(!$session->get_permissions('set_wiki_mode'))
-    {
-      die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
-    }
-    if ( isset($_POST['finish']) )
-    {
-      $level = intval($_POST['level']);
-      if ( !in_array($level, array(0, 1, 2) ) )
-      {
-        die_friendly('Invalid request', '<p>Level not specified</p>');
-      }
-      $q = $db->sql_query('UPDATE '.table_prefix.'pages SET wiki_mode=' . $level . ' WHERE urlname=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $paths->namespace . '\';');
-      if ( !$q )
-        $db->_die();
-      redirect(makeUrl($paths->page), htmlspecialchars($paths->cpage['name']), $lang->get('page_wikimode_success_redirect'), 2);
-    }
-    else
-    {
-      $template->header();
-      if(!isset($_GET['level']) || ( isset($_GET['level']) && !preg_match('#^([0-9])$#', $_GET['level']))) die_friendly('Invalid request', '<p>Level not specified</p>');
-        $level = intval($_GET['level']);
-        if ( !in_array($level, array(0, 1, 2) ) )
-        {
-          die_friendly('Invalid request', '<p>Level not specified</p>');
-        }
-      echo '<form action="' . makeUrl($paths->page, 'do=setwikimode', true) . '" method="post">';
-      echo '<input type="hidden" name="finish" value="foo" />';
-      echo '<input type="hidden" name="level" value="' . $level . '" />';
-      $level_txt = ( $level == 0 ) ? 'page_wikimode_level_off' : ( ( $level == 1 ) ? 'page_wikimode_level_on' : 'page_wikimode_level_global' );
-      $blurb = ( $level == 0 || ( $level == 2 && getConfig('wiki_mode') != '1' ) ) ? 'page_wikimode_blurb_disable' : 'page_wikimode_blurb_enable';
-      ?>
-      <h3><?php echo $lang->get('page_wikimode_heading'); ?></h3>
-      <p><?php echo $lang->get($level_txt) . ' ' . $lang->get($blurb); ?></p>
-      <p><?php echo $lang->get('page_wikimode_warning'); ?></p>
-      <p><input type="submit" value="<?php echo htmlspecialchars($lang->get('page_wikimode_btn_submit')); ?>" /></p>
-      <?php
-      echo '</form>';
-      $template->footer();
-    }
-    break;
-  case 'diff':
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    require_once(ENANO_ROOT.'/includes/diff.php');
-    $template->header();
-    $id1 = ( isset($_GET['diff1']) ) ? (int)$_GET['diff1'] : false;
-    $id2 = ( isset($_GET['diff2']) ) ? (int)$_GET['diff2'] : false;
-    if ( !$id1 || !$id2 )
-    {
-      echo '<p>Invalid request.</p>';
-      $template->footer();
-      break;
-    }
-    if ( !ctype_digit($_GET['diff1']) || !ctype_digit($_GET['diff1']) )
-    {
-      echo '<p>SQL injection attempt</p>';
-      $template->footer();
-      break;
-    }
-    echo PageUtils::pagediff($paths->page_id, $paths->namespace, $id1, $id2);
-    $template->footer();
-    break;
-  case 'detag':
-    if ( $session->user_level < USER_LEVEL_ADMIN )
-    {
-      die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
-    }
-    if ( $paths->page_exists )
-    {
-      die_friendly($lang->get('etc_invalid_request_short'), '<p>' . $lang->get('page_detag_err_page_exists') . '</p>');
-    }
-    $q = $db->sql_query('DELETE FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $paths->namespace . '\';');
-    if ( !$q )
-      $db->_die('Detag query, index.php:'.__LINE__);
-    die_friendly($lang->get('page_detag_success_title'), '<p>' . $lang->get('page_detag_success_body') . '</p>');
-    break;
-  case 'aclmanager':
-    if ( !$session->sid_super )
-    {
-      redirect(makeUrlNS('Special', "Login/{$paths->page}", 'target_do=aclmanager&level=' . $session->user_level, false), $lang->get('etc_access_denied_short'), $lang->get('etc_access_denied_need_reauth'), 0);
-    }
-    
-    require_once(ENANO_ROOT.'/includes/pageutils.php');
-    $data = ( isset($_POST['data']) ) ? $_POST['data'] : Array('mode' => 'listgroups');
-    PageUtils::aclmanager($data);
-    break;
-  case 'sql_report':
-    $rev_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
-    $page = new PageProcessor( $paths->page_id, $paths->namespace, $rev_id );
-    $page->send_headers = true;
-    $pagepass = ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '';
-    $page->password = $pagepass;
-    $page->send(true);
-    ob_end_clean();
-    ob_start();
-    $db->sql_report();
-    break;
+			if ( !$q )
+				$db->die_json();
+			
+			if ( $db->numrows() > 0 )
+			{
+				echo '<div class="usermessage">' . $lang->get('editor_msg_editing_old_revision') . '</div>';
+				
+				$rev_count = $db->numrows() - 2;
+				$row = $db->fetchrow();
+				$undo_info = array(
+					'old_author'     => $row['oldrev_author'],
+					'current_author' => $row['currentrev_author'],
+					'undo_count'     => max($rev_count, 1),
+					'last_rev_id'    => $revid
+				);
+			}
+			else
+			{
+				$revid = 0;
+			}
+			$db->free_result();
+		}
+		echo '
+			<form action="'.makeUrl($paths->page, 'do=edit').'" method="post" enctype="multipart/form-data">
+			<br />
+			<textarea name="page_text" rows="20" cols="60" style="width: 97%;">'.$text.'</textarea><br />
+			<br />
+			';
+		$edsumm = ( $revid > 0 ) ? $lang->get('editor_reversion_edit_summary', $undo_info) : $edsumm;
+		echo $lang->get('editor_lbl_edit_summary') . ' <input name="edit_summary" type="text" size="40" value="' . htmlspecialchars($edsumm) . '" /><br /><label><input type="checkbox" name="minor" /> ' . $lang->get('editor_lbl_minor_edit_field') . '</label><br />';
+		if ( !$session->user_logged_in && getConfig('guest_edit_require_captcha') == '1' )
+		{
+			echo '<br /><table border="0"><tr><td>';
+			echo '<b>' . $lang->get('editor_lbl_field_captcha') . '</b><br />'
+ 					. '<br />'
+ 					. $lang->get('editor_msg_captcha_pleaseenter') . '<br /><br />'
+ 					. $lang->get('editor_msg_captcha_blind');
+			echo '</td><td>';
+			$hash = $session->make_captcha();
+			echo '<img src="' . makeUrlNS('Special', "Captcha/$hash") . '" onclick="this.src+=\'/a\'" style="cursor: pointer;" /><br />';
+			echo '<input type="hidden" name="captcha_id" value="' . $hash . '" />';
+			echo $lang->get('editor_lbl_field_captcha_code') . ' <input type="text" name="captcha_code" value="" size="9" />';
+			echo '</td></tr></table>';
+		}
+		echo '<br />
+				<input type="submit" name="_save"    value="' . $lang->get('editor_btn_save') . '" style="font-weight: bold;" />
+				<input type="submit" name="_preview" value="' . $lang->get('editor_btn_preview') . '" />
+				<input type="submit" name="_revert"  value="' . $lang->get('editor_btn_revert') . '" />
+				<input type="submit" name="_cancel"  value="' . $lang->get('editor_btn_cancel') . '" />
+			</form>
+		';
+		if ( getConfig('wiki_edit_notice', '0') == '1' )
+		{
+			$notice = getConfig('wiki_edit_notice_text');
+			echo RenderMan::render($notice);
+		}
+		$template->footer();
+		break;
+	case 'viewsource':
+		$template->header();
+		$text = RenderMan::getPage($paths->page_id, $paths->namespace, 0, false, false, false, false);
+		$text = htmlspecialchars($text);
+		echo '
+			<form action="'.makeUrl($paths->page, 'do=edit').'" method="post">
+			<br />
+			<textarea readonly="readonly" name="page_text" rows="20" cols="60" style="width: 97%;">'.$text.'</textarea>';
+		echo '<br />
+				<input type="submit" name="_cancel" value="' . $lang->get('editor_btn_closeviewer') . '" />
+			</form>
+		';
+		$template->footer();
+		break;
+	case 'history':
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		$hist = PageUtils::histlist($paths->page_id, $paths->namespace);
+		$template->header();
+		echo $hist;
+		$template->footer();
+		break;
+	case 'rollback':
+		$id = (isset($_GET['id'])) ? $_GET['id'] : false;
+		if(!$id || !ctype_digit($id)) die_friendly('Invalid action ID', '<p>The URL parameter "id" is not an integer. Exiting to prevent nasties like SQL injection, etc.</p>');
+		
+		$id = intval($id);
+		
+		$page = new PageProcessor($paths->page_id, $paths->namespace);
+		$result = $page->rollback_log_entry($id);
+		
+		if ( $result['success'] )
+		{
+			$result = $lang->get("page_msg_rb_success_{$result['action']}", array('dateline' => $result['dateline']));
+		}
+		else
+		{
+			$result = $lang->get("page_err_{$result['error']}", array('action' => @$result['action']));
+		}
+		
+		$template->header();
+		echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a></p>';
+		$template->footer();
+		break;
+	case 'catedit':
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		if(isset($_POST['__enanoSaveButton']))
+		{
+			unset($_POST['__enanoSaveButton']);
+			$val = PageUtils::catsave($paths->page_id, $paths->namespace, $_POST);
+			if($val == 'GOOD')
+			{
+				header('Location: '.makeUrl($paths->page)); echo '<html><head><title>Redirecting...</title></head><body>If you haven\'t been redirected yet, <a href="'.makeUrl($paths->page).'">click here</a>.'; break;
+			} else {
+				die_friendly('Error saving category information', '<p>'.$val.'</p>');
+			}
+		}
+		elseif(isset($_POST['__enanoCatCancel']))
+		{
+			header('Location: '.makeUrl($paths->page)); echo '<html><head><title>Redirecting...</title></head><body>If you haven\'t been redirected yet, <a href="'.makeUrl($paths->page).'">click here</a>.'; break;
+		}
+		$template->header();
+		$c = PageUtils::catedit_raw($paths->page_id, $paths->namespace);
+		echo $c[1];
+		$template->footer();
+		break;
+	case 'moreoptions':
+		$template->header();
+		echo '<div class="menu_nojs" style="width: 150px; padding: 0;"><ul style="display: block;"><li><div class="label">' . $lang->get('ajax_lbl_moreoptions_nojs') . '</div><div style="clear: both;"></div></li>'.$template->toolbar_menu.'</ul></div>';
+		$template->footer();
+		break;
+	case 'protect':
+		if ( !$session->sid_super )
+		{
+			redirect(makeUrlNS('Special', "Login/{$paths->page}", 'target_do=protect&level=' . $session->user_level, false), $lang->get('etc_access_denied_short'), $lang->get('etc_access_denied_need_reauth'), 0);
+		}
+		
+		if ( isset($_POST['level']) && isset($_POST['reason']) )
+		{
+			$level = intval($_POST['level']);
+			if ( !in_array($level, array(PROTECT_FULL, PROTECT_SEMI, PROTECT_NONE)) )
+			{
+				$errors[] = 'bad level';
+			}
+			$reason = trim($_POST['reason']);
+			if ( empty($reason) )
+			{
+				$errors[] = $lang->get('onpage_protect_err_need_reason');
+			}
+			
+			$page = new PageProcessor($paths->page_id, $paths->namespace);
+			$result = $page->protect_page($level, $reason);
+			if ( $result['success'] )
+			{
+				redirect(makeUrl($paths->page), $lang->get('page_protect_lbl_success_title'), $lang->get('page_protect_lbl_success_body', array('page_link' => makeUrl($paths->page, false, true))), 3);
+			}
+			else
+			{
+				$errors[] = $lang->get('page_err_' . $result['error']);
+			}
+		}
+		$template->header();
+		?>
+		<form action="<?php echo makeUrl($paths->page, 'do=protect'); ?>" method="post">
+			<h3><?php echo $lang->get('onpage_protect_heading'); ?></h3>
+			<p><?php echo $lang->get('onpage_protect_msg_select_level'); ?></p>
+			
+			<?php
+			if ( !empty($errors) )
+			{
+				echo '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>';
+			}
+			?>
+			
+			<div class="protectlevel" style="line-height: 22px; margin-left: 17px;">
+				<label>
+					<input type="radio" name="level" value="<?php echo PROTECT_FULL; ?>" />
+					<?php echo gen_sprite(cdnPath . '/images/protect-icons.png', 22, 22, 0, 0); ?>
+					<?php echo $lang->get('onpage_protect_btn_full'); ?>
+				</label>
+			</div>
+			<div class="protectlevel_hint" style="font-size: smaller; margin-left: 68px;">
+				<?php echo $lang->get('onpage_protect_btn_full_hint'); ?>
+			</div>
+			
+			<div class="protectlevel" style="line-height: 22px; margin-left: 17px;">
+				<label>
+					<input type="radio" name="level" value="<?php echo PROTECT_SEMI; ?>" />
+					<?php echo gen_sprite(cdnPath . '/images/protect-icons.png', 22, 22, 22, 0); ?>
+					<?php echo $lang->get('onpage_protect_btn_semi'); ?>
+				</label>
+			</div>
+			<div class="protectlevel_hint" style="font-size: smaller; margin-left: 68px;">
+				<?php echo $lang->get('onpage_protect_btn_semi_hint'); ?>
+			</div>
+			
+			<div class="protectlevel" style="line-height: 22px; margin-left: 17px;">
+				<label>
+					<input type="radio" name="level" value="<?php echo PROTECT_NONE; ?>" />
+					<?php echo gen_sprite(cdnPath . '/images/protect-icons.png', 22, 22, 44, 0); ?>
+					<?php echo $lang->get('onpage_protect_btn_none'); ?>
+				</label>
+			</div>
+			<div class="protectlevel_hint" style="font-size: smaller; margin-left: 68px;">
+				<?php echo $lang->get('onpage_protect_btn_none_hint'); ?>
+			</div>
+			
+			<table style="margin-left: 1em;" cellspacing="10">
+				<tr>
+					<td valign="top">
+						<?php echo $lang->get('onpage_protect_lbl_reason'); ?>
+					</td>
+					<td>
+						<input type="text" name="reason" size="40" /><br />
+						<small><?php echo $lang->get('onpage_protect_lbl_reason_hint'); ?></small>
+					</td>
+				</tr>
+			</table>
+														
+			<p>
+				<input type="submit" value="<?php echo htmlspecialchars($lang->get('page_protect_btn_submit')) ?>" style="font-weight: bold;" />
+				<a class="abutton" href="<?php echo makeUrl($paths->page, false, true); ?>"><?php echo $lang->get('etc_cancel'); ?></a>
+			</p> 
+		</form>
+		<?php
+		$template->footer();
+		break;
+	case 'rename':
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		if(!empty($_POST['newname']))
+		{
+			$r = PageUtils::rename($paths->page_id, $paths->namespace, $_POST['newname']);
+			die_friendly($lang->get('page_rename_success_title'), '<p>'.nl2br($r).' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>');
+		}
+		$template->header();
+		?>
+		<form action="<?php echo makeUrl($paths->page, 'do=rename'); ?>" method="post">
+			<?php if(isset($_POST['newname'])) echo '<p style="color: red;">' . $lang->get('page_rename_err_need_name') . '</p>'; ?>
+			<p><?php echo $lang->get('page_rename_lbl'); ?></p>
+			<p><input type="text" name="newname" size="40" /></p>
+			<p><input type="submit" value="<?php echo htmlspecialchars($lang->get('page_rename_btn_submit')); ?>" style="font-weight: bold;" /></p> 
+		</form>
+		<?php
+		$template->footer();    
+		break;
+	case 'flushlogs':
+		if(!$session->get_permissions('clear_logs'))
+		{
+			die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
+		}
+		if ( !$session->sid_super )
+		{
+			redirect(makeUrlNS('Special', "Login/{$paths->page}", 'target_do=flushlogs&level=' . $session->user_level, false), $lang->get('etc_access_denied_short'), $lang->get('etc_access_denied_need_reauth'), 0);
+		}
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		if(isset($_POST['_downthejohn']))
+		{
+			$template->header();
+				$result = PageUtils::flushlogs($paths->page_id, $paths->namespace);
+				echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>';
+			$template->footer();
+			break;
+		}
+		$template->header();
+			?>
+			<form action="<?php echo makeUrl($paths->page, 'do=flushlogs'); ?>" method="post">
+ 				<?php echo $lang->get('page_flushlogs_warning_stern'); ?>
+ 				<p><input type="submit" name="_downthejohn" value="<?php echo htmlspecialchars($lang->get('page_flushlogs_btn_submit')); ?>" style="color: red; font-weight: bold;" /></p>
+			</form>
+			<?php
+		$template->footer();
+		break;
+	case 'delvote':
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		if(isset($_POST['_ballotbox']))
+		{
+			$template->header();
+			$result = PageUtils::delvote($paths->page_id, $paths->namespace);
+			echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>';
+			$template->footer();
+			break;
+		}
+		$template->header();
+			?>
+			<form action="<?php echo makeUrl($paths->page, 'do=delvote'); ?>" method="post">
+ 				<?php
+ 					echo $lang->get('page_delvote_warning_stern');
+ 					echo '<p>';
+ 					switch($paths->cpage['delvotes'])
+ 					{
+ 						case 0:  echo $lang->get('page_delvote_count_zero'); break;
+ 						case 1:  echo $lang->get('page_delvote_count_one'); break;
+ 						default: echo $lang->get('page_delvote_count_plural', array('delvotes' => $paths->cpage['delvotes'])); break;
+ 					}
+ 					echo '</p>';
+ 				?>
+ 				<p><input type="submit" name="_ballotbox" value="<?php echo htmlspecialchars($lang->get('page_delvote_btn_submit')); ?>" /></p>
+			</form>
+			<?php
+		$template->footer();
+		break;
+	case 'resetvotes':
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		if(!$session->get_permissions('vote_reset'))
+		{
+			die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
+		}
+		if(isset($_POST['_youmaylivealittlelonger']))
+		{
+			$template->header();
+				$result = PageUtils::resetdelvotes($paths->page_id, $paths->namespace);
+				echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>';
+			$template->footer();
+			break;
+		}
+		$template->header();
+			?>
+			<form action="<?php echo makeUrl($paths->page, 'do=resetvotes'); ?>" method="post">
+				<p><?php echo $lang->get('ajax_delvote_reset_confirm'); ?></p>
+				<p><input type="submit" name="_youmaylivealittlelonger" value="<?php echo htmlspecialchars($lang->get('page_delvote_reset_btn_submit')); ?>" /></p>
+			</form>
+			<?php
+		$template->footer();
+		break;
+	case 'deletepage':
+		if ( !$session->get_permissions('delete_page') )
+		{
+			die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
+		}
+		if ( !$session->sid_super )
+		{
+			redirect(makeUrlNS('Special', "Login/{$paths->page}", 'target_do=deletepage&level=' . $session->user_level, false), $lang->get('etc_access_denied_short'), $lang->get('etc_access_denied_need_reauth'), 0);
+		}
+		
+		require_once(ENANO_ROOT . '/includes/pageutils.php');
+		if ( isset($_POST['_adiossucker']) )
+		{
+			$reason = ( isset($_POST['reason']) ) ? $_POST['reason'] : false;
+			if ( empty($reason) )
+				$error = $lang->get('ajax_delete_prompt_reason');
+			else
+			{
+				$template->header();
+					$result = PageUtils::deletepage($paths->page_id, $paths->namespace, $reason);
+					echo '<p>'.$result.' <a href="'.makeUrl($paths->page).'">' . $lang->get('etc_return_to_page') . '</a>.</p>';
+				$template->footer();
+				break;
+			}
+		}
+		$template->header();
+			?>
+			<form action="<?php echo makeUrl($paths->page, 'do=deletepage'); ?>" method="post">
+ 				<?php echo $lang->get('page_delete_warning_stern'); ?>
+ 				<?php if ( isset($error) ) echo "<p>$error</p>"; ?>
+ 				<p><?php echo $lang->get('page_delete_lbl_reason'); ?> <input type="text" name="reason" size="50" /></p>
+ 				<p><input type="submit" name="_adiossucker" value="<?php echo htmlspecialchars($lang->get('page_delete_btn_submit')); ?>" style="font-weight: bold;" /></p>
+			</form>
+			<?php
+		$template->footer();
+		break;
+	case 'setwikimode':
+		if(!$session->get_permissions('set_wiki_mode'))
+		{
+			die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
+		}
+		if ( isset($_POST['finish']) )
+		{
+			$level = intval($_POST['level']);
+			if ( !in_array($level, array(0, 1, 2) ) )
+			{
+				die_friendly('Invalid request', '<p>Level not specified</p>');
+			}
+			$q = $db->sql_query('UPDATE '.table_prefix.'pages SET wiki_mode=' . $level . ' WHERE urlname=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $paths->namespace . '\';');
+			if ( !$q )
+				$db->_die();
+			redirect(makeUrl($paths->page), htmlspecialchars($paths->cpage['name']), $lang->get('page_wikimode_success_redirect'), 2);
+		}
+		else
+		{
+			$template->header();
+			if(!isset($_GET['level']) || ( isset($_GET['level']) && !preg_match('#^([0-9])$#', $_GET['level']))) die_friendly('Invalid request', '<p>Level not specified</p>');
+				$level = intval($_GET['level']);
+				if ( !in_array($level, array(0, 1, 2) ) )
+				{
+					die_friendly('Invalid request', '<p>Level not specified</p>');
+				}
+			echo '<form action="' . makeUrl($paths->page, 'do=setwikimode', true) . '" method="post">';
+			echo '<input type="hidden" name="finish" value="foo" />';
+			echo '<input type="hidden" name="level" value="' . $level . '" />';
+			$level_txt = ( $level == 0 ) ? 'page_wikimode_level_off' : ( ( $level == 1 ) ? 'page_wikimode_level_on' : 'page_wikimode_level_global' );
+			$blurb = ( $level == 0 || ( $level == 2 && getConfig('wiki_mode') != '1' ) ) ? 'page_wikimode_blurb_disable' : 'page_wikimode_blurb_enable';
+			?>
+			<h3><?php echo $lang->get('page_wikimode_heading'); ?></h3>
+			<p><?php echo $lang->get($level_txt) . ' ' . $lang->get($blurb); ?></p>
+			<p><?php echo $lang->get('page_wikimode_warning'); ?></p>
+			<p><input type="submit" value="<?php echo htmlspecialchars($lang->get('page_wikimode_btn_submit')); ?>" /></p>
+			<?php
+			echo '</form>';
+			$template->footer();
+		}
+		break;
+	case 'diff':
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		require_once(ENANO_ROOT.'/includes/diff.php');
+		$template->header();
+		$id1 = ( isset($_GET['diff1']) ) ? (int)$_GET['diff1'] : false;
+		$id2 = ( isset($_GET['diff2']) ) ? (int)$_GET['diff2'] : false;
+		if ( !$id1 || !$id2 )
+		{
+			echo '<p>Invalid request.</p>';
+			$template->footer();
+			break;
+		}
+		if ( !ctype_digit($_GET['diff1']) || !ctype_digit($_GET['diff1']) )
+		{
+			echo '<p>SQL injection attempt</p>';
+			$template->footer();
+			break;
+		}
+		echo PageUtils::pagediff($paths->page_id, $paths->namespace, $id1, $id2);
+		$template->footer();
+		break;
+	case 'detag':
+		if ( $session->user_level < USER_LEVEL_ADMIN )
+		{
+			die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
+		}
+		if ( $paths->page_exists )
+		{
+			die_friendly($lang->get('etc_invalid_request_short'), '<p>' . $lang->get('page_detag_err_page_exists') . '</p>');
+		}
+		$q = $db->sql_query('DELETE FROM '.table_prefix.'tags WHERE page_id=\'' . $db->escape($paths->page_id) . '\' AND namespace=\'' . $paths->namespace . '\';');
+		if ( !$q )
+			$db->_die('Detag query, index.php:'.__LINE__);
+		die_friendly($lang->get('page_detag_success_title'), '<p>' . $lang->get('page_detag_success_body') . '</p>');
+		break;
+	case 'aclmanager':
+		if ( !$session->sid_super )
+		{
+			redirect(makeUrlNS('Special', "Login/{$paths->page}", 'target_do=aclmanager&level=' . $session->user_level, false), $lang->get('etc_access_denied_short'), $lang->get('etc_access_denied_need_reauth'), 0);
+		}
+		
+		require_once(ENANO_ROOT.'/includes/pageutils.php');
+		$data = ( isset($_POST['data']) ) ? $_POST['data'] : Array('mode' => 'listgroups');
+		PageUtils::aclmanager($data);
+		break;
+	case 'sql_report':
+		$rev_id = ( (isset($_GET['oldid'])) ? intval($_GET['oldid']) : 0 );
+		$page = new PageProcessor( $paths->page_id, $paths->namespace, $rev_id );
+		$page->send_headers = true;
+		$pagepass = ( isset($_REQUEST['pagepass']) ) ? sha1($_REQUEST['pagepass']) : '';
+		$page->password = $pagepass;
+		$page->send(true);
+		ob_end_clean();
+		ob_start();
+		$db->sql_report();
+		break;
 }
 
 // Generate an ETag
 /*
 // format: first 10 digits of SHA1 of page name, user id in hex, user and auth levels, page timestamp in hex
 $etag = substr(sha1($paths->namespace . ':' . $paths->page_id), 0, 10) . '-' .
-        "u{$session->user_id}l{$session->user_level}a{$session->auth_level}-" .
-        dechex($page_timestamp);
-        
+				"u{$session->user_id}l{$session->user_level}a{$session->auth_level}-" .
+				dechex($page_timestamp);
+				
 if ( isset($_SERVER['HTTP_IF_NONE_MATCH']) )
 {
-  if ( "\"$etag\"" == $_SERVER['HTTP_IF_NONE_MATCH'] )
-  {
-    header('HTTP/1.1 304 Not Modified');
-    exit();
-  }
+	if ( "\"$etag\"" == $_SERVER['HTTP_IF_NONE_MATCH'] )
+	{
+		header('HTTP/1.1 304 Not Modified');
+		exit();
+	}
 }
-          
+					
 header("ETag: \"$etag\"");
 */
 
@@ -698,5 +698,5 @@
 gzip_output();
 
 @ob_end_flush();
-  
+	
 ?>
--- a/install/images/css/installer.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/images/css/installer.css	Sun Mar 28 23:10:46 2010 -0400
@@ -12,304 +12,304 @@
  */
 
 body {
-  font-family: DejaVu Sans, Arial, Helvetica, sans-serif;
-  font-size: 9pt;
-  margin: 0;
-  padding: 0;
+	font-family: DejaVu Sans, Arial, Helvetica, sans-serif;
+	font-size: 9pt;
+	margin: 0;
+	padding: 0;
 }
 
 div#header {
-  margin: 0px auto;
-  width: 800px;
-  padding: 10px 0;
+	margin: 0px auto;
+	width: 800px;
+	padding: 10px 0;
 }
 
 div#step {
-  float: right;
-  font-size: 12pt;
-  color: #D84308;
-  line-height: 48px;
-  background-image: url(../icons/install.png);
-  background-position: right center;
-  background-repeat: no-repeat;
-  padding-right: 56px;
-  margin: 10px 0 0 0;
+	float: right;
+	font-size: 12pt;
+	color: #D84308;
+	line-height: 48px;
+	background-image: url(../icons/install.png);
+	background-position: right center;
+	background-repeat: no-repeat;
+	padding-right: 56px;
+	margin: 10px 0 0 0;
 }
 
 div.stages-holder {
-  color: #ffffff;
-  background-color: #2f527a;
-  width: 100%;
+	color: #ffffff;
+	background-color: #2f527a;
+	width: 100%;
 }
 
 ul.stages {
-  margin: 0;
-  line-height: 24px;
-  font-size: 8pt;
-  padding: 0;
+	margin: 0;
+	line-height: 24px;
+	font-size: 8pt;
+	padding: 0;
 }
 ul.stages-fixed {
-  /*
-  width: 840px;
-  */
-  display: table;
-  margin: 0 auto;
+	/*
+	width: 840px;
+	*/
+	display: table;
+	margin: 0 auto;
 }
 li.stage {
-  list-style-type: none;
-  float: left;
-  text-align: center;
-  padding: 3px 20px 3px 20px;
+	list-style-type: none;
+	float: left;
+	text-align: center;
+	padding: 3px 20px 3px 20px;
 }
 li.stage-active {
-  font-weight: bold;
-  padding: 1px 20px 5px 20px;
-  background-image: url(../marker.gif);
-  background-position: center bottom;
-  background-repeat: no-repeat;
-  background-color: #5f82aa;
+	font-weight: bold;
+	padding: 1px 20px 5px 20px;
+	background-image: url(../marker.gif);
+	background-position: center bottom;
+	background-repeat: no-repeat;
+	background-color: #5f82aa;
 }
 div#enano-fill {
-  background-image: url(../substages.png);
-  background-repeat: repeat-x;
+	background-image: url(../substages.png);
+	background-repeat: repeat-x;
 }
 div#enano-body {
-  width: 780px;
-  padding: 10px;
-  margin: 0 auto;
+	width: 780px;
+	padding: 10px;
+	margin: 0 auto;
 }
 div#enano-body a {
-  color: #003366;
-  text-decoration: underline;
+	color: #003366;
+	text-decoration: underline;
 }
 div#enano-body a:hover {
-  color: #0055AA;
+	color: #0055AA;
 }
 div#copyright {
-  border-top: 1px dotted #003399;
-  font-size: 6pt;
-  padding: 10px;
-  background-image: url(../substages.png);
-  background-repeat: repeat-x;
-  width: 77%;
-  margin: 20px auto 0 auto;
-  text-align: center;
-  color: #808080;
+	border-top: 1px dotted #003399;
+	font-size: 6pt;
+	padding: 10px;
+	background-image: url(../substages.png);
+	background-repeat: repeat-x;
+	width: 77%;
+	margin: 20px auto 0 auto;
+	text-align: center;
+	color: #808080;
 }
 td.balancer {
-  width: 21px;
-  background-image: url(../balancer.png);
-  background-position: center center;
-  background-repeat: no-repeat;
+	width: 21px;
+	background-image: url(../balancer.png);
+	background-position: center center;
+	background-repeat: no-repeat;
 }
 ul.icons {
-  margin: 0;
-  padding: 0;
-  list-style-type: none;
-  display: table;
+	margin: 0;
+	padding: 0;
+	list-style-type: none;
+	display: table;
 }
 ul.icons li:first-child {
-  border-top-color: #FFFFFF;
+	border-top-color: #FFFFFF;
 }
 ul.icons li {
-  /* Invisible border to prevent size-switching later */
-  border: 1px solid #FFFFFF;
-  border-top-color: #F0F0F0;
-  margin: 0 0 -2px 0;
-  padding: 0;
-  display: block;
+	/* Invisible border to prevent size-switching later */
+	border: 1px solid #FFFFFF;
+	border-top-color: #F0F0F0;
+	margin: 0 0 -2px 0;
+	padding: 0;
+	display: block;
 }
 ul.icons li:hover {
-  border-color: #D0D0D0;
-  -moz-border-radius: 5px;
+	border-color: #D0D0D0;
+	-moz-border-radius: 5px;
 }
 a.icon, span.icon {
-  display: block;
-  font-size: 18pt;
-  line-height: 48px;
-  padding: 10px 20px 10px 68px;
-  background-position: 10px center;
-  background-repeat: no-repeat;
-  color: #002266;
-  text-decoration: none !important;
+	display: block;
+	font-size: 18pt;
+	line-height: 48px;
+	padding: 10px 20px 10px 68px;
+	background-position: 10px center;
+	background-repeat: no-repeat;
+	color: #002266;
+	text-decoration: none !important;
 }
 a.icon:hover, span.icon:hover {
-  cursor: pointer;
-  color: #002266 !important;
-  background-color: #F0F0F0;
+	cursor: pointer;
+	color: #002266 !important;
+	background-color: #F0F0F0;
 }
 a.icon-disabled, span.icon-disabled {
-  color: #808080 !important;
-  opacity: 0.7;
-  filter: alpha(opacity=70);
+	color: #808080 !important;
+	opacity: 0.7;
+	filter: alpha(opacity=70);
 }
 a.icon-disabled:hover, span.icon-disabled:hover {
-  color: #808080 !important;
-  background-color: #FCFCFC;
-  border-color: #F8F8F8;
+	color: #808080 !important;
+	background-color: #FCFCFC;
+	border-color: #F8F8F8;
 }
 a.icon small, a.icon-disabled small, span.icon small, span.icon-disabled small {
-  display: block;
-  font-size: 8pt;
-  line-height: normal;
-  margin-top: -10px;
+	display: block;
+	font-size: 8pt;
+	line-height: normal;
+	margin-top: -10px;
 }
 a.readme {
-  background-image: url(../icons/readme.png);
+	background-image: url(../icons/readme.png);
 }
 a.install {
-  background-image: url(../icons/install.png);
+	background-image: url(../icons/install.png);
 }
 a.install-disabled {
-  background-image: url(../icons/install-disabled.png);
+	background-image: url(../icons/install-disabled.png);
 }
 a.upgrade {
-  background-image: url(../icons/upgrade.png);
+	background-image: url(../icons/upgrade.png);
 }
 a.upgrade-disabled, span.upgrade-disabled {
-  background-image: url(../icons/upgrade-disabled.png);
+	background-image: url(../icons/upgrade-disabled.png);
 }
 
 .scroller {
-  padding: 10px;
-  border: 1px dotted #002266;
-  background-color: #F0F0F0;
-  max-height: 500px;
-  clip: rect(0px, auto, auto, 0px);
-  overflow: auto;
+	padding: 10px;
+	border: 1px dotted #002266;
+	background-color: #F0F0F0;
+	max-height: 500px;
+	clip: rect(0px, auto, auto, 0px);
+	overflow: auto;
 }
 
 div#installnotice {
-  margin: 5% 0 0 0;
+	margin: 5% 0 0 0;
 }
 
 table#installmenu {
-  margin: 0 auto 5% auto;
+	margin: 0 auto 5% auto;
 }
 span.fieldtip_js {
-  display: block;
-  background-color: #F0F0FF;
-  padding: 10px;
-  border: 1px solid #245687;
-  position: absolute;
+	display: block;
+	background-color: #F0F0FF;
+	padding: 10px;
+	border: 1px solid #245687;
+	position: absolute;
 }
 
 /* Inputs, form controls */
 
 input[type ^="button"], button {
-  background-color: #F8F8FB;
-  color: #202020;
-  border-color: #B0B0B8 #D0D0D8 #D0D0D8 #B0B0B8;
-  border-width: 1px;
-  border-style: solid;
+	background-color: #F8F8FB;
+	color: #202020;
+	border-color: #B0B0B8 #D0D0D8 #D0D0D8 #B0B0B8;
+	border-width: 1px;
+	border-style: solid;
 }
 input[type ^="submit"], button.submit {
-  background-color: #4F729A;
-  color: #FFFFFF;
-  border-width: 1px;
-  border-style: solid;
-  border-color: #1F426A #7FA2CA #7FA2CA #1F426A;
+	background-color: #4F729A;
+	color: #FFFFFF;
+	border-width: 1px;
+	border-style: solid;
+	border-color: #1F426A #7FA2CA #7FA2CA #1F426A;
 }
 input[type ^="submit"]:hover, input[type ^="submit"]:focus, button.submit:hover, button.submit:focus {
-  background-color: #5f82aa;
+	background-color: #5f82aa;
 }
 input[type ^="text"], input[type ^="password"] {
-  color: #202020;
-  background-color: #F0F0F4;
-  border: 1px solid #D6D6E9;
+	color: #202020;
+	background-color: #F0F0F4;
+	border: 1px solid #D6D6E9;
 }
 input[type ^="text"]:hover, input[type ^="password"]:hover {
-  color: #202020;
-  background-color: #F4F4F8;
-  border: 1px solid #D6D6E9;
+	color: #202020;
+	background-color: #F4F4F8;
+	border: 1px solid #D6D6E9;
 }
 input[type ^="text"]:focus, input[type ^="password"]:focus {
-  color: #202020;
-  background-color: #FFFFFF;
-  border: 1px solid #D6D6E9;
+	color: #202020;
+	background-color: #FFFFFF;
+	border: 1px solid #D6D6E9;
 }
 
 /* SysReqs: table */
 
 table.sysreqs {
-  width: 100%;
+	width: 100%;
 }
 
 table.sysreqs td {
-  border-right: 1px solid #ededed;
-  width: 70%;
-  padding: 8px;
+	border-right: 1px solid #ededed;
+	width: 70%;
+	padding: 8px;
 }
 
 table.sysreqs th {
-  border-right-width: 0;
-  width: 100%;
-  border-bottom: 1px solid #acacac;
-  text-align: left;
-  padding: 7px 0 0 0;
+	border-right-width: 0;
+	width: 100%;
+	border-bottom: 1px solid #acacac;
+	text-align: left;
+	padding: 7px 0 0 0;
 }
 
 table.sysreqs td.good, table.sysreqs td.bad, table.sysreqs td.warn {
-  font-weight: bold;
-  border-right-width: 0;
-  width: 30%;
+	font-weight: bold;
+	border-right-width: 0;
+	width: 30%;
 }
 
 table.sysreqs td.good small, table.sysreqs td.bad small, table.sysreqs td.warn small {
-  font-weight: normal;
+	font-weight: normal;
 }
 
 table.sysreqs td.good {
-  color: #00aa00;
+	color: #00aa00;
 }
 
 table.sysreqs td.bad {
-  color: #aa0000;
+	color: #aa0000;
 }
 
 table.sysreqs td.warn {
-  color: #aaaa00;
+	color: #aaaa00;
 }
 
 div.sysreqs_error, div.sysreqs_warning, div.sysreqs_success {
-  background-image: url(../box_error.png);
-  background-repeat: repeat-x;
-  background-position: top center;
-  background-color: #ffd1d1;
-  border: 1px solid #6e0000;
-  padding: 7px;
-  margin-bottom: 7px;
+	background-image: url(../box_error.png);
+	background-repeat: repeat-x;
+	background-position: top center;
+	background-color: #ffd1d1;
+	border: 1px solid #6e0000;
+	padding: 7px;
+	margin-bottom: 7px;
 }
 
 div.sysreqs_warning {
-  background-image: url(../box_warning.png);
-  background-color: #fffed1;
-  border-color: #6e6e00;
+	background-image: url(../box_warning.png);
+	background-color: #fffed1;
+	border-color: #6e6e00;
 }
 
 div.sysreqs_success {
-  background-image: url(../box_success.png);
-  background-color: #d1ffd1;
-  border-color: #006e00;
+	background-image: url(../box_success.png);
+	background-color: #d1ffd1;
+	border-color: #006e00;
 }
 
 div.sysreqs_error h3, div.sysreqs_warning h3, div.sysreqs_success h3 {
-  font-size: 9pt;
-  border-bottom: 1px solid #8f3131;
-  margin-top: 0;
-  margin-bottom: 6px;
+	font-size: 9pt;
+	border-bottom: 1px solid #8f3131;
+	margin-top: 0;
+	margin-bottom: 6px;
 }
 
 div.sysreqs_warning h3 {
-  border-bottom-color: #8f8f31;
+	border-bottom-color: #8f8f31;
 }
 
 div.sysreqs_success h3 {
-  border-bottom-color: #318f31;
+	border-bottom-color: #318f31;
 }
 
 div.sysreqs_error p, div.sysreqs_warning p, div.sysreqs_success p {
-  margin: 0;
+	margin: 0;
 }
 
 
--- a/install/includes/cli-core.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/cli-core.php	Sun Mar 28 23:10:46 2010 -0400
@@ -18,142 +18,142 @@
 require(dirname(__FILE__) . '/common.php');
 if ( !defined('ENANO_CLI') )
 {
-  $ui = new Enano_Installer_UI('Enano installation', false);
-  $ui->set_visible_stage($ui->add_stage('Error', true));
+	$ui = new Enano_Installer_UI('Enano installation', false);
+	$ui->set_visible_stage($ui->add_stage('Error', true));
 
-  $ui->step = 'Access denied';  
-  $ui->show_header();
-  echo '<h2>CLI only</h2>
-        <p>This script must be run from the command line.</p>';
-  $ui->show_footer();
-  exit;
+	$ui->step = 'Access denied';  
+	$ui->show_header();
+	echo '<h2>CLI only</h2>
+				<p>This script must be run from the command line.</p>';
+	$ui->show_footer();
+	exit;
 }
 
 if ( defined('ENANO_INSTALLED') )
 {
-  // start up the API to let it error out if something's wrong
-  require(ENANO_ROOT . '/includes/common.php');
-  
-  installer_fail('Enano is already installed. Uninstall it by deleting config.php and creating a blank file called config.new.php.');
+	// start up the API to let it error out if something's wrong
+	require(ENANO_ROOT . '/includes/common.php');
+	
+	installer_fail('Enano is already installed. Uninstall it by deleting config.php and creating a blank file called config.new.php.');
 }
 
 // parse command line args
 foreach ( array('silent', 'driver', 'dbhost', 'dbport', 'dbuser', 'dbpasswd', 'dbname', 'db_prefix', 'user', 'pass', 'email', 'sitename', 'sitedesc', 'copyright', 'urlscheme', 'lang_id', 'scriptpath') as $var )
 {
-  if ( !isset($$var) )
-  {
-    $$var = false;
-  }
+	if ( !isset($$var) )
+	{
+		$$var = false;
+	}
 }
 
 for ( $i = 1; $i < count($argv); $i++ )
 {
-  switch($argv[$i])
-  {
-    case '-q':
-      $silent = true;
-      break;
-    case '--db-driver':
-    case '-b':
-      $driver = @$argv[++$i];
-      break;
-    case '--db-host':
-    case '-h':
-      $dbhost = @$argv[++$i];
-      break;
-    case '--db-port':
-    case '-o':
-      $dbport = @$argv[++$i];
-      break;
-    case '--db-user':
-    case '-u':
-      $dbuser = @$argv[++$i];
-      break;
-    case '--db-pass':
-    case '-p':
-      $dbpasswd = @$argv[++$i];
-      break;
-    case '--db-name':
-    case '-d':
-      $dbname = @$argv[++$i];
-      break;
-    case '--table-prefix':
-    case '-t':
-      $db_prefix = @$argv[++$i];
-      break;
-    case '--admin-user':
-    case '-a':
-      $user = @$argv[++$i];
-      break;
-    case '--admin-pass':
-    case '-w':
-      $pass = @$argv[++$i];
-      break;
-    case '--admin-email':
-    case '-e':
-      $email = @$argv[++$i];
-      break;
-    case '--site-name':
-    case '-n':
-      $sitename = @$argv[++$i];
-      break;
-    case '--site-desc':
-    case '-s':
-      $sitedesc = @$argv[++$i];
-      break;
-    case '--copyright':
-    case '-c':
-      $copyright = @$argv[++$i];
-      break;
-    case '--url-scheme':
-    case '-r':
-      $urlscheme_temp = @$argv[++$i];
-      if ( in_array($urlscheme_temp, array('standard', 'short', 'rewrite', 'tiny')) )
-        $urlscheme = $urlscheme_temp;
-      break;
-    case '--language':
-    case '-l':
-      $lang_id = @$argv[++$i];
-      break;
-    case '-i':
-    case '--scriptpath':
-      $scriptpath = @$argv[++$i];
-      break;
-    default:
-      $vers = installer_enano_version();
-      echo <<<EOF
+	switch($argv[$i])
+	{
+		case '-q':
+			$silent = true;
+			break;
+		case '--db-driver':
+		case '-b':
+			$driver = @$argv[++$i];
+			break;
+		case '--db-host':
+		case '-h':
+			$dbhost = @$argv[++$i];
+			break;
+		case '--db-port':
+		case '-o':
+			$dbport = @$argv[++$i];
+			break;
+		case '--db-user':
+		case '-u':
+			$dbuser = @$argv[++$i];
+			break;
+		case '--db-pass':
+		case '-p':
+			$dbpasswd = @$argv[++$i];
+			break;
+		case '--db-name':
+		case '-d':
+			$dbname = @$argv[++$i];
+			break;
+		case '--table-prefix':
+		case '-t':
+			$db_prefix = @$argv[++$i];
+			break;
+		case '--admin-user':
+		case '-a':
+			$user = @$argv[++$i];
+			break;
+		case '--admin-pass':
+		case '-w':
+			$pass = @$argv[++$i];
+			break;
+		case '--admin-email':
+		case '-e':
+			$email = @$argv[++$i];
+			break;
+		case '--site-name':
+		case '-n':
+			$sitename = @$argv[++$i];
+			break;
+		case '--site-desc':
+		case '-s':
+			$sitedesc = @$argv[++$i];
+			break;
+		case '--copyright':
+		case '-c':
+			$copyright = @$argv[++$i];
+			break;
+		case '--url-scheme':
+		case '-r':
+			$urlscheme_temp = @$argv[++$i];
+			if ( in_array($urlscheme_temp, array('standard', 'short', 'rewrite', 'tiny')) )
+				$urlscheme = $urlscheme_temp;
+			break;
+		case '--language':
+		case '-l':
+			$lang_id = @$argv[++$i];
+			break;
+		case '-i':
+		case '--scriptpath':
+			$scriptpath = @$argv[++$i];
+			break;
+		default:
+			$vers = installer_enano_version();
+			echo <<<EOF
 Enano CMS v$vers - CLI Installer
 Usage: {$argv[0]} [-q] [-b driver] [-h host] [-u username] [-p password]
-                  [-d database] [-a adminuser] [-w adminpass] [-e email]
+									[-d database] [-a adminuser] [-w adminpass] [-e email]
 All arguments are optional; missing information will be prompted for.
-  -q                Quiet mode (minimal output)
-  -b, --db-driver   Database driver (mysql or postgresql)
-  -h, --db-host     Hostname of database server
-  -o, --db-port     TCP port on which to connect to database server
-  -u, --db-user     Username to use on database server
-  -p, --db-pass     Password to use on database server
-  -d, --db-name     Name of database
-  -a, --admin-user  Administrator username
-  -w, --admin-pass  Administrator password
-  -e, --admin-email Administrator e-mail address
-  -n, --site-name   Name of site
-  -s, --site-desc   *SHORT* Description of site
-  -c, --copyright   Copyright notice shown on pages
-  -r, --url-scheme  URL scheme (standard, short, rewrite, or tiny)
-  -l, --language    Language to be used on site and in installer
-  -i, --scriptpath  Where Enano is relative to your website root (no trailing
-                    slash)
+	-q                Quiet mode (minimal output)
+	-b, --db-driver   Database driver (mysql or postgresql)
+	-h, --db-host     Hostname of database server
+	-o, --db-port     TCP port on which to connect to database server
+	-u, --db-user     Username to use on database server
+	-p, --db-pass     Password to use on database server
+	-d, --db-name     Name of database
+	-a, --admin-user  Administrator username
+	-w, --admin-pass  Administrator password
+	-e, --admin-email Administrator e-mail address
+	-n, --site-name   Name of site
+	-s, --site-desc   *SHORT* Description of site
+	-c, --copyright   Copyright notice shown on pages
+	-r, --url-scheme  URL scheme (standard, short, rewrite, or tiny)
+	-l, --language    Language to be used on site and in installer
+	-i, --scriptpath  Where Enano is relative to your website root (no trailing
+										slash)
 
 
 EOF;
-      exit(1);
-      break;
-  }
+			exit(1);
+			break;
+	}
 }
 
 if ( $silent )
 {
-  define('ENANO_LIBINSTALL_SILENT', '');
+	define('ENANO_LIBINSTALL_SILENT', '');
 }
 
 ##
@@ -162,12 +162,12 @@
 
 if ( version_compare(PHP_VERSION, '5.0.0', '<' ) )
 {
-  if ( !$silent )
-  {
-    echo "\x1B[1mWelcome to the \x1B[34mEnano\x1B[0m CMS\x1B[1m installation wizard.\x1B[0m\n";
-    echo "Installing Enano version \x1B[1m" . installer_enano_version() . "\x1B[0m on PHP " . PHP_VERSION . "\n";
-  }
-  installer_fail('Your version of PHP (' . PHP_VERSION . ') doesn\'t meet Enano requirements (5.0.0)');
+	if ( !$silent )
+	{
+		echo "\x1B[1mWelcome to the \x1B[34mEnano\x1B[0m CMS\x1B[1m installation wizard.\x1B[0m\n";
+		echo "Installing Enano version \x1B[1m" . installer_enano_version() . "\x1B[0m on PHP " . PHP_VERSION . "\n";
+	}
+	installer_fail('Your version of PHP (' . PHP_VERSION . ') doesn\'t meet Enano requirements (5.0.0)');
 }
 
 ##
@@ -182,24 +182,24 @@
 $langids = array_keys($languages);
 if ( $silent )
 {
-  if ( !in_array($lang_id, $langids ) )
-    $lang_id = $langids[0];
+	if ( !in_array($lang_id, $langids ) )
+		$lang_id = $langids[0];
 }
 else if ( !in_array($lang_id, $langids) )
 {
-  echo "\x1B[1mPlease select a language.\x1B[0m\n";
-  echo "\x1B[32mAvailable languages:\x1B[0m\n";
-  foreach ( $languages as $id => $metadata )
-  {
-    $id_spaced = $id;
-    while ( strlen($id_spaced) < 10 )
-      $id_spaced = "$id_spaced ";
-    echo "  \x1B[1;34m$id_spaced\x1B[0m {$metadata['name']} ({$metadata['name_eng']})\n";
-  }
-  while ( !in_array($lang_id, $langids) )
-  {
-    $lang_id = cli_prompt('Language: ', $langids[0]);
-  }
+	echo "\x1B[1mPlease select a language.\x1B[0m\n";
+	echo "\x1B[32mAvailable languages:\x1B[0m\n";
+	foreach ( $languages as $id => $metadata )
+	{
+		$id_spaced = $id;
+		while ( strlen($id_spaced) < 10 )
+			$id_spaced = "$id_spaced ";
+		echo "  \x1B[1;34m$id_spaced\x1B[0m {$metadata['name']} ({$metadata['name_eng']})\n";
+	}
+	while ( !in_array($lang_id, $langids) )
+	{
+		$lang_id = cli_prompt('Language: ', $langids[0]);
+	}
 }
 
 // We have a language ID - init language
@@ -215,108 +215,108 @@
 
 if ( !$silent )
 {
-  echo parse_shellcolor_string($lang->get('cli_welcome_line1'));
-  echo parse_shellcolor_string($lang->get('cli_welcome_line2', array('enano_version' => installer_enano_version(), 'php_version' => PHP_VERSION)));
+	echo parse_shellcolor_string($lang->get('cli_welcome_line1'));
+	echo parse_shellcolor_string($lang->get('cli_welcome_line2', array('enano_version' => installer_enano_version(), 'php_version' => PHP_VERSION)));
 }
 
 $defaults = array(
-  'driver'  => 'mysql',
-  'dbhost'    => 'localhost',
-  'dbport'    => 3306,
-  'dbuser'    => false,
-  'dbpasswd'  => false,
-  'dbname'    => false,
-  'db_prefix'    => '',
-  'user'      => 'admin',
-  'pass'      => false,
-  'email'     => false,
-  'sitename'  => $lang->get('cli_default_site_name'),
-  'sitedesc'  => $lang->get('cli_default_site_desc'),
-  'copyright' => $lang->get('cli_default_copyright', array('year' => date('Y'))),
-  'urlscheme' => 'standard',
-  'scriptpath'=> '/enano'
+	'driver'  => 'mysql',
+	'dbhost'    => 'localhost',
+	'dbport'    => 3306,
+	'dbuser'    => false,
+	'dbpasswd'  => false,
+	'dbname'    => false,
+	'db_prefix'    => '',
+	'user'      => 'admin',
+	'pass'      => false,
+	'email'     => false,
+	'sitename'  => $lang->get('cli_default_site_name'),
+	'sitedesc'  => $lang->get('cli_default_site_desc'),
+	'copyright' => $lang->get('cli_default_copyright', array('year' => date('Y'))),
+	'urlscheme' => 'standard',
+	'scriptpath'=> '/enano'
 );
 
 $terms = array(
-  'driver'  => $lang->get('cli_prompt_driver'),
-  'dbhost'    => $lang->get('cli_prompt_dbhost'),
-  'dbport'    => $lang->get('cli_prompt_dbport'),
-  'dbuser'    => $lang->get('cli_prompt_dbuser'),
-  'dbpasswd'  => $lang->get('cli_prompt_dbpasswd'),
-  'dbname'    => $lang->get('cli_prompt_dbname'),
-  'db_prefix'    => $lang->get('cli_prompt_db_prefix'),
-  'user'      => $lang->get('cli_prompt_user'),
-  'pass'      => $lang->get('cli_prompt_pass'),
-  'email'     => $lang->get('cli_prompt_email'),
-  'sitename'  => $lang->get('cli_prompt_sitename'),
-  'sitedesc'  => $lang->get('cli_prompt_sitedesc'),
-  'copyright' => $lang->get('cli_prompt_copyright'),
-  'urlscheme' => $lang->get('cli_prompt_urlscheme'),
-  'scriptpath'=> $lang->get('cli_prompt_scriptpath')
+	'driver'  => $lang->get('cli_prompt_driver'),
+	'dbhost'    => $lang->get('cli_prompt_dbhost'),
+	'dbport'    => $lang->get('cli_prompt_dbport'),
+	'dbuser'    => $lang->get('cli_prompt_dbuser'),
+	'dbpasswd'  => $lang->get('cli_prompt_dbpasswd'),
+	'dbname'    => $lang->get('cli_prompt_dbname'),
+	'db_prefix'    => $lang->get('cli_prompt_db_prefix'),
+	'user'      => $lang->get('cli_prompt_user'),
+	'pass'      => $lang->get('cli_prompt_pass'),
+	'email'     => $lang->get('cli_prompt_email'),
+	'sitename'  => $lang->get('cli_prompt_sitename'),
+	'sitedesc'  => $lang->get('cli_prompt_sitedesc'),
+	'copyright' => $lang->get('cli_prompt_copyright'),
+	'urlscheme' => $lang->get('cli_prompt_urlscheme'),
+	'scriptpath'=> $lang->get('cli_prompt_scriptpath')
 );
 
 $defaults['dbport'] = ( strtolower($driver) == 'postgresql' ) ? 5432 : 3306;
 
 foreach ( array('driver', 'dbhost', 'dbport', 'dbuser', 'dbpasswd', 'dbname', 'db_prefix', 'scriptpath', 'user', 'pass', 'email', 'sitename', 'sitedesc', 'copyright', 'urlscheme') as $var )
 {
-  if ( empty($$var) )
-  {
-    switch($var)
-    {
-      default:
-        $$var = cli_prompt($terms[$var], $defaults[$var]);
-        break;
-      case 'driver':
-        $$var = cli_prompt($terms[$var], $defaults[$var]);
-        $defaults['dbport'] = ( strtolower($driver) == 'postgresql' ) ? 5432 : 3306;
-        break;
-      case 'pass':
-      case 'dbpasswd':
-        if ( @file_exists('/bin/stty') && @is_executable('/bin/stty') )
-        {
-          exec('/bin/stty -echo');
-          while ( true )
-          {
-            $$var = cli_prompt($terms[$var], $defaults[$var]);
-            echo "\n";
-            $confirm = cli_prompt($lang->get('cli_prompt_confirm'), $defaults[$var]);
-            echo "\n";
-            if ( $$var === $confirm )
-              break;
-            else
-              echo parse_shellcolor_string($lang->get('cli_err_pass_no_match'));
-          }
-          exec('/bin/stty echo');
-        }
-        else
-        {
-          $$var = cli_prompt("{$terms[$var]} " . $lang->get('cli_msg_echo_warning'), $defaults[$var]);
-        }
-        break;
-      case 'urlscheme':
-        $temp = '';
-        while ( !in_array($temp, array('standard', 'short', 'rewrite', 'tiny')) )
-        {
-          $temp = cli_prompt($terms[$var], $defaults[$var]);
-        }
-        $$var = $temp;
-        break;
-      case 'db_prefix':
-        while ( !preg_match('/^[a-z0-9_]*$/', $$var) )
-        {
-          $$var = cli_prompt($terms[$var], $defaults[$var]);
-        }
-        break;
-      case 'dbport':
-        $$var = cli_prompt($terms[$var], strval($defaults[$var]));
-        while ( !preg_match('/^[0-9]*$/', $$var) )
-        {
-          $$var = cli_prompt($terms[$var], $defaults[$var]);
-        }
-        $$var = intval($$var);
-        break;
-    }
-  }
+	if ( empty($$var) )
+	{
+		switch($var)
+		{
+			default:
+				$$var = cli_prompt($terms[$var], $defaults[$var]);
+				break;
+			case 'driver':
+				$$var = cli_prompt($terms[$var], $defaults[$var]);
+				$defaults['dbport'] = ( strtolower($driver) == 'postgresql' ) ? 5432 : 3306;
+				break;
+			case 'pass':
+			case 'dbpasswd':
+				if ( @file_exists('/bin/stty') && @is_executable('/bin/stty') )
+				{
+					exec('/bin/stty -echo');
+					while ( true )
+					{
+						$$var = cli_prompt($terms[$var], $defaults[$var]);
+						echo "\n";
+						$confirm = cli_prompt($lang->get('cli_prompt_confirm'), $defaults[$var]);
+						echo "\n";
+						if ( $$var === $confirm )
+							break;
+						else
+							echo parse_shellcolor_string($lang->get('cli_err_pass_no_match'));
+					}
+					exec('/bin/stty echo');
+				}
+				else
+				{
+					$$var = cli_prompt("{$terms[$var]} " . $lang->get('cli_msg_echo_warning'), $defaults[$var]);
+				}
+				break;
+			case 'urlscheme':
+				$temp = '';
+				while ( !in_array($temp, array('standard', 'short', 'rewrite', 'tiny')) )
+				{
+					$temp = cli_prompt($terms[$var], $defaults[$var]);
+				}
+				$$var = $temp;
+				break;
+			case 'db_prefix':
+				while ( !preg_match('/^[a-z0-9_]*$/', $$var) )
+				{
+					$$var = cli_prompt($terms[$var], $defaults[$var]);
+				}
+				break;
+			case 'dbport':
+				$$var = cli_prompt($terms[$var], strval($defaults[$var]));
+				while ( !preg_match('/^[0-9]*$/', $$var) )
+				{
+					$$var = cli_prompt($terms[$var], $defaults[$var]);
+				}
+				$$var = intval($$var);
+				break;
+		}
+	}
 }
 
 ##
@@ -328,21 +328,21 @@
 $dbal = new $driver();
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_msg_testing_db'));
+	echo parse_shellcolor_string($lang->get('cli_msg_testing_db'));
 
 $result = $dbal->connect(true, $dbhost, $dbuser, $dbpasswd, $dbname, $dbport);
 if ( !$result )
 {
-  if ( !$silent )
-  {
-    echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
-    echo "[$driver] " . $dbal->sql_error() . "\n";
-  }
-  installer_fail($lang->get('cli_err_db_connect_fail'));
+	if ( !$silent )
+	{
+		echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+		echo "[$driver] " . $dbal->sql_error() . "\n";
+	}
+	installer_fail($lang->get('cli_err_db_connect_fail'));
 }
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+	echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
 
 ##
 ## SERVER REQUIREMENTS
@@ -350,7 +350,7 @@
 
 if ( !$silent )
 {
-  echo parse_shellcolor_string($lang->get('cli_stage_sysreqs'));
+	echo parse_shellcolor_string($lang->get('cli_stage_sysreqs'));
 }
 
 $failed = false;
@@ -360,17 +360,17 @@
 if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_php') . ': ';
 if ( version_compare(PHP_VERSION, '5.2.0', '>=') )
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
 }
 else if ( version_compare(PHP_VERSION, '5.0.0', '>=') )
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_vwarn')) . "\n";
-  $warnings[] = $lang->get('sysreqs_req_help_php', array('php_version' => PHP_VERSION));
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_vwarn')) . "\n";
+	$warnings[] = $lang->get('sysreqs_req_help_php', array('php_version' => PHP_VERSION));
 }
 else
 {
-  $failed = true;
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+	$failed = true;
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
 }
 
 // Test: MySQL
@@ -378,12 +378,12 @@
 $req_mysql = function_exists('mysql_connect');
 if ( $req_mysql )
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
-  $have_dbms = true;
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+	$have_dbms = true;
 }
 else
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
 }
 
 // Test: PostgreSQL
@@ -391,29 +391,29 @@
 $req_pgsql = function_exists('pg_connect');
 if ( $req_pgsql )
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
-  $have_dbms = true;
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+	$have_dbms = true;
 }
 else
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
 }
 
 if ( !$have_dbms )
-  $failed = true;
+	$failed = true;
 
 // Test: Safe Mode
 if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_safemode') . ': ';
 $req_safemode = !intval(@ini_get('safe_mode'));
 if ( !$req_safemode )
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
-  $warnings[] = $lang->get('sysreqs_req_help_safemode');
-  $failed = true;
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+	$warnings[] = $lang->get('sysreqs_req_help_safemode');
+	$failed = true;
 }
 else
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
 }
 
 // Test: File uploads
@@ -421,11 +421,11 @@
 $req_uploads = intval(@ini_get('file_uploads'));
 if ( $req_uploads )
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
 }
 else
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_warn')) . "\n";
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_warn')) . "\n";
 }
 
 // Test: ctype validation
@@ -433,33 +433,33 @@
 $req_ctype = function_exists('ctype_digit');
 if ( $req_ctype )
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_pass')) . "\n";
 }
 else
 {
-  if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
-  $failed = true;
+	if ( !$silent ) echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+	$failed = true;
 }
 
 // Test: crypto
 $crypto_backend = install_get_crypto_backend();
 if ( !$silent )
 {
-  echo '  ' . $lang->get('sysreqs_req_crypto') . ': ';
-  switch($crypto_backend)
-  {
-    case 'bcmath':
-      echo parse_shellcolor_string($lang->get('cli_test_warn') . " [<c 0;33>" . $lang->get("sysreqs_req_{$crypto_backend}") . "</c>]") . "\n";
-      $warnings[] = $lang->get('sysreqs_req_help_crypto_bcmath');
-      break;
-    case 'none':
-      echo parse_shellcolor_string($lang->get('cli_test_warn') . " [<c 0;31>" . $lang->get("sysreqs_req_notfound") . "</c>]") . "\n";
-      $warnings[] = $lang->get('sysreqs_req_help_crypto_none');
-      break;
-    default:
-      echo parse_shellcolor_string($lang->get('cli_test_pass') . " [<c 0;32>" . $lang->get("sysreqs_req_{$crypto_backend}") . "</c>]") . "\n";
-      break;
-  }
+	echo '  ' . $lang->get('sysreqs_req_crypto') . ': ';
+	switch($crypto_backend)
+	{
+		case 'bcmath':
+			echo parse_shellcolor_string($lang->get('cli_test_warn') . " [<c 0;33>" . $lang->get("sysreqs_req_{$crypto_backend}") . "</c>]") . "\n";
+			$warnings[] = $lang->get('sysreqs_req_help_crypto_bcmath');
+			break;
+		case 'none':
+			echo parse_shellcolor_string($lang->get('cli_test_warn') . " [<c 0;31>" . $lang->get("sysreqs_req_notfound") . "</c>]") . "\n";
+			$warnings[] = $lang->get('sysreqs_req_help_crypto_none');
+			break;
+		default:
+			echo parse_shellcolor_string($lang->get('cli_test_pass') . " [<c 0;32>" . $lang->get("sysreqs_req_{$crypto_backend}") . "</c>]") . "\n";
+			break;
+	}
 }
 
 // Write tests
@@ -474,38 +474,38 @@
 if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_cache_writable') . ': ' . parse_shellcolor_string($lang->get($req_cache_w ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
 
 if ( !$req_config_w || !$req_htaccess_w || !$req_files_w || !$req_cache_w )
-  $warnings[] = $lang->get('sysreqs_req_help_writable');
+	$warnings[] = $lang->get('sysreqs_req_help_writable');
 
 if ( !$req_config_w )
-  $failed = true;
-      
+	$failed = true;
+			
 // Extension test: GD
 $req_gd = function_exists('imagecreatefrompng') && function_exists('getimagesize') && function_exists('imagecreatetruecolor') && function_exists('imagecopyresampled');
 if ( !$req_gd )
-  $warnings[] = $lang->get('sysreqs_req_help_gd2');
+	$warnings[] = $lang->get('sysreqs_req_help_gd2');
 
 if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_gd2') . ': ' . parse_shellcolor_string($lang->get($req_gd ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
 
 // FS test: ImageMagick
 $req_imagick = which('convert');
 if ( !$req_imagick )
-  $warnings[] = $lang->get('sysreqs_req_help_imagemagick');
+	$warnings[] = $lang->get('sysreqs_req_help_imagemagick');
 
 if ( !$silent ) echo '  ' . $lang->get('sysreqs_req_imagemagick') . ': ' . parse_shellcolor_string($lang->get($req_imagick ? 'cli_test_pass' : 'cli_test_warn')) . "\n";
 
 if ( !empty($warnings) && !$silent )
 {
-  echo parse_shellcolor_string($lang->get('cli_msg_test_warnings')) . "\n";
-  echo "  " . implode("\n  ", $warnings) . "\n";
+	echo parse_shellcolor_string($lang->get('cli_msg_test_warnings')) . "\n";
+	echo "  " . implode("\n  ", $warnings) . "\n";
 }
 
 if ( !function_exists('mysql_connect') && !function_exists('pg_connect') )
 {
-  installer_fail($lang->get('cli_err_no_drivers'));
+	installer_fail($lang->get('cli_err_no_drivers'));
 }
 if ( $failed )
 {
-  installer_fail($lang->get('cli_err_sysreqs_fail'));
+	installer_fail($lang->get('cli_err_sysreqs_fail'));
 }
 
 ##
@@ -514,54 +514,54 @@
 
 if ( !$silent )
 {
-  echo parse_shellcolor_string($lang->get('cli_msg_tests_passed'));
-  echo parse_shellcolor_string($lang->get('cli_msg_installing_db_stage1'));
+	echo parse_shellcolor_string($lang->get('cli_msg_tests_passed'));
+	echo parse_shellcolor_string($lang->get('cli_msg_installing_db_stage1'));
 }
 
 // Create the config table
 try
 {
-  $sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$driver}_stage1.sql" );
+	$sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$driver}_stage1.sql" );
 }
 catch ( Exception $e )
 {
-  if ( !$silent )
-    echo "\n";
-  installer_fail($lang->get('cli_err_schema_load'));
+	if ( !$silent )
+		echo "\n";
+	installer_fail($lang->get('cli_err_schema_load'));
 }
 // Check to see if the config table already exists
 $q = $dbal->sql_query('SELECT config_name, config_value FROM ' . $db_prefix . 'config LIMIT 1;');
 if ( !$q )
 {
-  $sql_parser->assign_vars(array(
-      'TABLE_PREFIX' => $db_prefix
-    ));
-  $sql = $sql_parser->parse();
-  foreach ( $sql as $q )
-  {
-    if ( !$dbal->sql_query($q) )
-    {
-      if ( !$silent )
-        echo "\n";
-      echo "[$driver] " . $dbal->sql_error() . "\n";
-      installer_fail($lang->get('cli_err_db_query'));
-    }
-  }
+	$sql_parser->assign_vars(array(
+			'TABLE_PREFIX' => $db_prefix
+		));
+	$sql = $sql_parser->parse();
+	foreach ( $sql as $q )
+	{
+		if ( !$dbal->sql_query($q) )
+		{
+			if ( !$silent )
+				echo "\n";
+			echo "[$driver] " . $dbal->sql_error() . "\n";
+			installer_fail($lang->get('cli_err_db_query'));
+		}
+	}
 }
 else
 {
-  $dbal->free_result();
-  if ( !$dbal->sql_query('DELETE FROM ' . $db_prefix . 'config WHERE config_name = \'install_aes_key\';') )
-  {
-    if ( !$silent )
-      echo "\n";
-    echo "[$driver] " . $dbal->sql_error() . "\n";
-    installer_fail($lang->get('cli_err_db_query'));
-  }
+	$dbal->free_result();
+	if ( !$dbal->sql_query('DELETE FROM ' . $db_prefix . 'config WHERE config_name = \'install_aes_key\';') )
+	{
+		if ( !$silent )
+			echo "\n";
+		echo "[$driver] " . $dbal->sql_error() . "\n";
+		installer_fail($lang->get('cli_err_db_query'));
+	}
 }
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
+	echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
 
 define('table_prefix', $db_prefix);
 
@@ -576,7 +576,7 @@
 $_SERVER['REMOTE_ADDR'] = ( intval(date('Y')) >= 2011 ) ? '::1' : '127.0.0.1';
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_msg_parsing_schema'));
+	echo parse_shellcolor_string($lang->get('cli_msg_parsing_schema'));
 
 require_once( ENANO_ROOT . '/includes/rijndael.php' );
 require_once( ENANO_ROOT . '/includes/hmac.php' );
@@ -591,69 +591,69 @@
 
 try
 {
-  $sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$dbdriver}_stage2.sql" );
+	$sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$dbdriver}_stage2.sql" );
 }
 catch ( Exception $e )
 {
-  if ( !$silent )
-    echo "\n";
-  installer_fail($lang->get('cli_err_schema_load'));
+	if ( !$silent )
+		echo "\n";
+	installer_fail($lang->get('cli_err_schema_load'));
 }
 
 $vars = array(
-    'TABLE_PREFIX'         => table_prefix,
-    'SITE_NAME'            => $db->escape($sitename),
-    'SITE_DESC'            => $db->escape($sitedesc),
-    'COPYRIGHT'            => $db->escape($copyright),
-    'WIKI_MODE'            => '0',
-    'ENABLE_CACHE'         => ( is_writable( ENANO_ROOT . '/cache/' ) ? '1' : '0' ),
-    'VERSION'              => installer_enano_version(),
-    'ADMIN_USER'           => $db->escape($user),
-    'ADMIN_PASS'           => $admin_pass,
-    'ADMIN_PASS_SALT'      => $hmac_secret,
-    'ADMIN_EMAIL'          => $db->escape($email),
-    'REAL_NAME'            => '', // This has always been stubbed.
-    'ADMIN_EMBED_PHP'      => strval(AUTH_DISALLOW),
-    'UNIX_TIME'            => strval(time()),
-    'IP_ADDRESS'           => $_SERVER['REMOTE_ADDR']
-  );
+		'TABLE_PREFIX'         => table_prefix,
+		'SITE_NAME'            => $db->escape($sitename),
+		'SITE_DESC'            => $db->escape($sitedesc),
+		'COPYRIGHT'            => $db->escape($copyright),
+		'WIKI_MODE'            => '0',
+		'ENABLE_CACHE'         => ( is_writable( ENANO_ROOT . '/cache/' ) ? '1' : '0' ),
+		'VERSION'              => installer_enano_version(),
+		'ADMIN_USER'           => $db->escape($user),
+		'ADMIN_PASS'           => $admin_pass,
+		'ADMIN_PASS_SALT'      => $hmac_secret,
+		'ADMIN_EMAIL'          => $db->escape($email),
+		'REAL_NAME'            => '', // This has always been stubbed.
+		'ADMIN_EMBED_PHP'      => strval(AUTH_DISALLOW),
+		'UNIX_TIME'            => strval(time()),
+		'IP_ADDRESS'           => $_SERVER['REMOTE_ADDR']
+	);
 
 $sql_parser->assign_vars($vars);
 $schema = $sql_parser->parse();
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
+	echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
 
 ##
 ## PAYLOAD DELIVERY
 ##
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_msg_installing_db_stage2'));
+	echo parse_shellcolor_string($lang->get('cli_msg_installing_db_stage2'));
 
 foreach ( $schema as $sql )
 {
-  if ( !$db->check_query($sql) )
-  {
-    if ( !$silent )
-      echo "\n";
-    installer_fail($lang->get('cli_err_query_sanity_failed'));
-  }
+	if ( !$db->check_query($sql) )
+	{
+		if ( !$silent )
+			echo "\n";
+		installer_fail($lang->get('cli_err_query_sanity_failed'));
+	}
 }
 
 foreach ( $schema as $sql )
 {
-  if ( !$db->sql_query($sql) )
-  {
-    if ( !$silent )
-      echo "\n";
-    echo "[$dbdriver] " . $db->sql_error() . "\n";
-    installer_fail($lang->get('cli_err_db_query'));
-  }
+	if ( !$db->sql_query($sql) )
+	{
+		if ( !$silent )
+			echo "\n";
+		echo "[$dbdriver] " . $db->sql_error() . "\n";
+		installer_fail($lang->get('cli_err_db_query'));
+	}
 }
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
+	echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
 
 ##
 ## CONFIG FILE GENERATION
@@ -663,7 +663,7 @@
 require_once( ENANO_ROOT . '/install/includes/libenanoinstallcli.php' );
 define('scriptPath', $scriptpath);
 $urlscheme = strtr($urlscheme, array(
-  'short' => 'shortened'
+	'short' => 'shortened'
 ));
 $_POST['url_scheme'] =& $urlscheme;
 
@@ -674,7 +674,7 @@
 ##
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_msg_starting_api'));
+	echo parse_shellcolor_string($lang->get('cli_msg_starting_api'));
 
 // Start up the Enano API
 $db->close();
@@ -683,7 +683,7 @@
 require(ENANO_ROOT . '/includes/common.php');
 
 if ( !$silent )
-  echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
+	echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
 
 $_POST['username'] =& $user;
 $_POST['default_content_type'] = ( isset($start_with) ) ? $start_with : 'blank';
@@ -697,7 +697,7 @@
 
 if ( !$silent )
 {
-  echo parse_shellcolor_string($lang->get('cli_msg_install_success'));
+	echo parse_shellcolor_string($lang->get('cli_msg_install_success'));
 }
 
 return true;
@@ -708,90 +708,90 @@
 
 function cli_prompt($prompt, $default = false)
 {
-  if ( is_string($default) )
-  {
-    echo "$prompt [$default]: ";
-    $stdin = fopen('php://stdin', 'r');
-    $input = trim(fgets($stdin, 1024));
-    fclose($stdin);
-    if ( empty($input) )
-      return $default;
-    return $input;
-  }
-  else
-  {
-    while ( true )
-    {
-      echo "$prompt: ";
-      $stdin = fopen('php://stdin', 'r');
-      $input = trim(fgets($stdin, 1024));
-      fclose($stdin);
-      if ( !empty($input) )
-        return $input;
-    }
-  }
+	if ( is_string($default) )
+	{
+		echo "$prompt [$default]: ";
+		$stdin = fopen('php://stdin', 'r');
+		$input = trim(fgets($stdin, 1024));
+		fclose($stdin);
+		if ( empty($input) )
+			return $default;
+		return $input;
+	}
+	else
+	{
+		while ( true )
+		{
+			echo "$prompt: ";
+			$stdin = fopen('php://stdin', 'r');
+			$input = trim(fgets($stdin, 1024));
+			fclose($stdin);
+			if ( !empty($input) )
+				return $input;
+		}
+	}
 }
 
 function run_test($evalme, $test, $description, $warnonly = false)
 {
-  global $silent, $test_failed, $lang;
-  if ( !$silent )
-    echo "$test: ";
-  $result = eval($evalme);
-  if ( $result )
-  {
-    if ( !$silent )
-      echo parse_shellcolor_string($lang->get('cli_test_pass'));
-  }
-  else
-  {
-    if ( !$silent )
-      echo $warnonly ? parse_shellcolor_string($lang->get('cli_test_warn')) : parse_shellcolor_string($lang->get('cli_test_fail'));
-    if ( !$silent )
-      echo "\n" . preg_replace('/^/m', '  ', wordwrap(strip_tags($description)));
-    if ( !$warnonly )
-      $test_failed = true;
-  }
-  if ( !$silent )
-    echo "\n";
+	global $silent, $test_failed, $lang;
+	if ( !$silent )
+		echo "$test: ";
+	$result = eval($evalme);
+	if ( $result )
+	{
+		if ( !$silent )
+			echo parse_shellcolor_string($lang->get('cli_test_pass'));
+	}
+	else
+	{
+		if ( !$silent )
+			echo $warnonly ? parse_shellcolor_string($lang->get('cli_test_warn')) : parse_shellcolor_string($lang->get('cli_test_fail'));
+		if ( !$silent )
+			echo "\n" . preg_replace('/^/m', '  ', wordwrap(strip_tags($description)));
+		if ( !$warnonly )
+			$test_failed = true;
+	}
+	if ( !$silent )
+		echo "\n";
 }
 
 function installer_fail($message)
 {
-  global $silent;
-  if ( $silent )
-    file_put_contents('php://stderr', "$message\n");
-  else
-    echo "\x1B[1;31m" . "Error:\x1B[0;1m $message\x1B[0m\n";
-  exit(1);
+	global $silent;
+	if ( $silent )
+		file_put_contents('php://stderr', "$message\n");
+	else
+		echo "\x1B[1;31m" . "Error:\x1B[0;1m $message\x1B[0m\n";
+	exit(1);
 }
 
 function config_write_test()
 {
-  if ( !is_writable(ENANO_ROOT.'/config.new.php') )
-    return false;
-  // We need to actually _open_ the file to make sure it can be written, because sometimes this fails even when is_writable() returns
-  // true on Windows/IIS servers. Don't ask me why.
-  $h = @fopen( ENANO_ROOT . '/config.new.php', 'a+' );
-  if ( !$h )
-    return false;
-  fclose($h);
-  return true;
+	if ( !is_writable(ENANO_ROOT.'/config.new.php') )
+		return false;
+	// We need to actually _open_ the file to make sure it can be written, because sometimes this fails even when is_writable() returns
+	// true on Windows/IIS servers. Don't ask me why.
+	$h = @fopen( ENANO_ROOT . '/config.new.php', 'a+' );
+	if ( !$h )
+		return false;
+	fclose($h);
+	return true;
 }
 
 function parse_shellcolor_string($str)
 {
-  // only compute this once (saves some CPU time)
-  static $do_colors = null;
-  if ( $do_colors === null )
-  {
-    $do_colors = ( isset($_SERVER['TERM']) && $_SERVER['TERM'] != 'dumb' );
-  }
-  
-  $expr = '/<c ((?:[0-9]+)(?:;[0-9]+)*)>([\w\W]*?)<\/c>/';
-  while ( preg_match($expr, $str) )
-    $str = $do_colors ? preg_replace($expr, "\x1B[\\1m\\2\x1B[0m", $str) : preg_replace($expr, "\\2", $str);
-  
-  return $str;
+	// only compute this once (saves some CPU time)
+	static $do_colors = null;
+	if ( $do_colors === null )
+	{
+		$do_colors = ( isset($_SERVER['TERM']) && $_SERVER['TERM'] != 'dumb' );
+	}
+	
+	$expr = '/<c ((?:[0-9]+)(?:;[0-9]+)*)>([\w\W]*?)<\/c>/';
+	while ( preg_match($expr, $str) )
+		$str = $do_colors ? preg_replace($expr, "\x1B[\\1m\\2\x1B[0m", $str) : preg_replace($expr, "\\2", $str);
+	
+	return $str;
 }
 
--- a/install/includes/common.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/common.php	Sun Mar 28 23:10:46 2010 -0400
@@ -15,40 +15,40 @@
 
 // Our version number. This needs to be changed for any custom releases.
 $installer_version = array(
-  'version' => '1.1.8',
-  'type' => 'beta'
-  // If type is set to "rc", "beta", or "alpha", optionally another version number can be issued with the key 'sub':
-  // 'sub' => '3' will produce Enano 1.1.1a3 / Enano 1.1.1 alpha 3
+	'version' => '1.1.8',
+	'type' => 'beta'
+	// If type is set to "rc", "beta", or "alpha", optionally another version number can be issued with the key 'sub':
+	// 'sub' => '3' will produce Enano 1.1.1a3 / Enano 1.1.1 alpha 3
 );
 
 function installer_enano_version($long = false)
 {
-  global $installer_version;
-  static $keywords = array(
-    'alpha' => 'a',
-    'beta' => 'b',
-    'RC' => 'rc'
-    );
-  $v = $installer_version['version'];
-  if ( isset($installer_version['sub']) )
-  {
-    $v .= ( !$long ) ? $keywords[$installer_version['type']] : " {$installer_version['type']} ";
-    $v .= $installer_version['sub'];
-  }
-  return $v;
+	global $installer_version;
+	static $keywords = array(
+		'alpha' => 'a',
+		'beta' => 'b',
+		'RC' => 'rc'
+		);
+	$v = $installer_version['version'];
+	if ( isset($installer_version['sub']) )
+	{
+		$v .= ( !$long ) ? $keywords[$installer_version['type']] : " {$installer_version['type']} ";
+		$v .= $installer_version['sub'];
+	}
+	return $v;
 }
  
 // Determine Enano root directory
 
 if ( !defined('ENANO_ROOT') )
 {
-  $enano_root = dirname(dirname(dirname(__FILE__)));
-  if ( preg_match('#/repo$#', $enano_root) && file_exists("$enano_root/../.enanodev") )
-  {
-    $enano_root = preg_replace('#/repo$#', '', $enano_root);
-  }
-  
-  define('ENANO_ROOT', $enano_root);
+	$enano_root = dirname(dirname(dirname(__FILE__)));
+	if ( preg_match('#/repo$#', $enano_root) && file_exists("$enano_root/../.enanodev") )
+	{
+		$enano_root = preg_replace('#/repo$#', '', $enano_root);
+	}
+	
+	define('ENANO_ROOT', $enano_root);
 }
 
 chdir(ENANO_ROOT);
@@ -56,45 +56,45 @@
 // Determine our scriptPath
 if ( isset($_SERVER['REQUEST_URI']) && !defined('scriptPath') )
 {
-  // Use reverse-matching to determine where the REQUEST_URI overlaps the Enano root.
-  $requri = $_SERVER['REQUEST_URI'];
-  if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
-  {
-    $requri = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $requri);
-  }
-  if ( !preg_match('/\.php$/', $requri) )
-  {
-    // user requested http://foo/enano as opposed to http://foo/enano/index.php
-    $requri .= '/index.php';
-  }
-  $sp = dirname($_SERVER['REQUEST_URI']);
-  if ( $sp == '/' || $sp == '\\' )
-  {
-    $sp = '';
-  }
-  $sp = preg_replace('#/install$#', '', $sp);
-  define('scriptPath', $sp);
+	// Use reverse-matching to determine where the REQUEST_URI overlaps the Enano root.
+	$requri = $_SERVER['REQUEST_URI'];
+	if ( isset($_SERVER['PATH_INFO']) && !preg_match('/index\.php$/', $_SERVER['PATH_INFO']) )
+	{
+		$requri = preg_replace(';' . preg_quote($_SERVER['PATH_INFO']) . '$;', '', $requri);
+	}
+	if ( !preg_match('/\.php$/', $requri) )
+	{
+		// user requested http://foo/enano as opposed to http://foo/enano/index.php
+		$requri .= '/index.php';
+	}
+	$sp = dirname($_SERVER['REQUEST_URI']);
+	if ( $sp == '/' || $sp == '\\' )
+	{
+		$sp = '';
+	}
+	$sp = preg_replace('#/install$#', '', $sp);
+	define('scriptPath', $sp);
 }
 
 // is Enano already installed?
 @include(ENANO_ROOT . '/config.php');
 if ( defined('ENANO_INSTALLED') && defined('ENANO_DANGEROUS') && !isset($_GET['debug_warn_php4']) )
 {
-  $title = 'Installation locked';
-  require('includes/common.php');
-  $template->header();
-  echo '<p>The installer has detected that an installation of Enano already exists on your server. You MUST delete config.php if you wish to reinstall Enano.</p>';
-  $template->footer();
-  exit();
+	$title = 'Installation locked';
+	require('includes/common.php');
+	$template->header();
+	echo '<p>The installer has detected that an installation of Enano already exists on your server. You MUST delete config.php if you wish to reinstall Enano.</p>';
+	$template->footer();
+	exit();
 }
 
 if ( !function_exists('microtime_float') )
 {
-  function microtime_float()
-  {
-    list($usec, $sec) = explode(" ", microtime());
-    return ((float)$usec + (float)$sec);
-  }
+	function microtime_float()
+	{
+		list($usec, $sec) = explode(" ", microtime());
+		return ((float)$usec + (float)$sec);
+	}
 }
 
 define('IN_ENANO_INSTALL', 1);
@@ -109,7 +109,7 @@
 // If we have at least PHP 5, load json2
 if ( version_compare(PHP_VERSION, '5.0.0', '>=') )
 {
-  require_once(ENANO_ROOT . '/includes/json2.php');
+	require_once(ENANO_ROOT . '/includes/json2.php');
 }
 
 strip_magic_quotes_gpc();
@@ -117,7 +117,7 @@
 // Build a list of available languages
 $dir = @opendir( ENANO_ROOT . '/language' );
 if ( !$dir )
-  die('CRITICAL: could not open language directory');
+	die('CRITICAL: could not open language directory');
 
 $languages = array();
 // Use the old PHP4-compatible JSON decoder
@@ -125,30 +125,30 @@
 
 while ( $dh = @readdir($dir) )
 {
-  if ( $dh == '.' || $dh == '..' )
-    continue;
-  if ( file_exists( ENANO_ROOT . "/language/$dh/meta.json" ) )
-  {
-    // Found a language directory, determine metadata
-    $meta = @file_get_contents( ENANO_ROOT . "/language/$dh/meta.json" );
-    if ( empty($meta) )
-      // Could not read metadata file, continue silently
-      continue;
-    $meta = $json->decode($meta);
-    if ( isset($meta['lang_name_english']) && isset($meta['lang_name_native']) && isset($meta['lang_code']) )
-    {
-      $languages[$meta['lang_code']] = array(
-          'name' => $meta['lang_name_native'],
-          'name_eng' => $meta['lang_name_english'],
-          'dir' => $dh
-        );
-    }
-  }
+	if ( $dh == '.' || $dh == '..' )
+		continue;
+	if ( file_exists( ENANO_ROOT . "/language/$dh/meta.json" ) )
+	{
+		// Found a language directory, determine metadata
+		$meta = @file_get_contents( ENANO_ROOT . "/language/$dh/meta.json" );
+		if ( empty($meta) )
+			// Could not read metadata file, continue silently
+			continue;
+		$meta = $json->decode($meta);
+		if ( isset($meta['lang_name_english']) && isset($meta['lang_name_native']) && isset($meta['lang_code']) )
+		{
+			$languages[$meta['lang_code']] = array(
+					'name' => $meta['lang_name_native'],
+					'name_eng' => $meta['lang_name_english'],
+					'dir' => $dh
+				);
+		}
+	}
 }
 
 if ( count($languages) < 1 )
 {
-  die('The Enano installer couldn\'t find any languages in the language/ folder. Enano needs at least one language in this folder to load and install properly.');
+	die('The Enano installer couldn\'t find any languages in the language/ folder. Enano needs at least one language in this folder to load and install properly.');
 }
 
 // List of available DB drivers
@@ -157,10 +157,10 @@
 // Divert to CLI loader if running from CLI
 if ( isset($argc) && isset($argv) )
 {
-  if ( is_int($argc) && is_array($argv) && !isset($_SERVER['REQUEST_URI']) )
-  {
-    define('ENANO_CLI', '');
-  }
+	if ( is_int($argc) && is_array($argv) && !isset($_SERVER['REQUEST_URI']) )
+	{
+		define('ENANO_CLI', '');
+	}
 }
 
 ?>
--- a/install/includes/libenanoinstall.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/libenanoinstall.php	Sun Mar 28 23:10:46 2010 -0400
@@ -17,232 +17,232 @@
 
 function run_installer_stage($stage_id, $stage_name, $function, $failure_explanation, $allow_skip = true)
 {
-  static $resumed = false;
-  static $resume_stack = array();
-  
-  if ( empty($resume_stack) && isset($_POST['resume_stack']) && preg_match('/[a-z_]+((\|[a-z_]+)+)/', $_POST['resume_stack']) )
-  {
-    $resume_stack = explode('|', $_POST['resume_stack']);
-  }
-  
-  $already_run = false;
-  if ( in_array($stage_id, $resume_stack) )
-  {
-    $already_run = true;
-  }
-  
-  if ( !$resumed )
-  {
-    if ( !isset($_GET['sub']) )
-      $resumed = true;
-    if ( isset($_GET['sub']) && $_GET['sub'] == $stage_id )
-    {
-      $resumed = true;
-    }
-  }
-  if ( !$resumed && $allow_skip )
-  {
-    echo_stage_success($stage_id, $stage_name);
-    return false;
-  }
-  if ( !function_exists($function) )
-    die('libenanoinstall: CRITICAL: function "' . $function . '" for ' . $stage_id . ' doesn\'t exist');
-  $result = call_user_func($function, false, $already_run);
-  if ( $result )
-  {
-    echo_stage_success($stage_id, $stage_name);
-    $resume_stack[] = $stage_id;
-    return true;
-  }
-  else
-  {
-    echo_stage_failure($stage_id, $stage_name, $failure_explanation, $resume_stack);
-    return false;
-  }
+	static $resumed = false;
+	static $resume_stack = array();
+	
+	if ( empty($resume_stack) && isset($_POST['resume_stack']) && preg_match('/[a-z_]+((\|[a-z_]+)+)/', $_POST['resume_stack']) )
+	{
+		$resume_stack = explode('|', $_POST['resume_stack']);
+	}
+	
+	$already_run = false;
+	if ( in_array($stage_id, $resume_stack) )
+	{
+		$already_run = true;
+	}
+	
+	if ( !$resumed )
+	{
+		if ( !isset($_GET['sub']) )
+			$resumed = true;
+		if ( isset($_GET['sub']) && $_GET['sub'] == $stage_id )
+		{
+			$resumed = true;
+		}
+	}
+	if ( !$resumed && $allow_skip )
+	{
+		echo_stage_success($stage_id, $stage_name);
+		return false;
+	}
+	if ( !function_exists($function) )
+		die('libenanoinstall: CRITICAL: function "' . $function . '" for ' . $stage_id . ' doesn\'t exist');
+	$result = call_user_func($function, false, $already_run);
+	if ( $result )
+	{
+		echo_stage_success($stage_id, $stage_name);
+		$resume_stack[] = $stage_id;
+		return true;
+	}
+	else
+	{
+		echo_stage_failure($stage_id, $stage_name, $failure_explanation, $resume_stack);
+		return false;
+	}
 }
 
 function start_install_table()
 {
-  echo '<table border="0" cellspacing="0" cellpadding="0" style="margin-top: 10px;">' . "\n";
+	echo '<table border="0" cellspacing="0" cellpadding="0" style="margin-top: 10px;">' . "\n";
 }
 
 function close_install_table()
 {
-  echo '</table>' . "\n\n";
-  flush();
+	echo '</table>' . "\n\n";
+	flush();
 }
 
 function echo_stage_success($stage_id, $stage_name)
 {
-  global $neutral_color;
-  $neutral_color = ( $neutral_color == 'A' ) ? 'C' : 'A';
-  echo '<tr><td style="width: 500px; background-color: #' . "{$neutral_color}{$neutral_color}FF{$neutral_color}{$neutral_color}" . '; padding: 0 5px;">' . htmlspecialchars($stage_name) . '</td><td style="padding: 0 5px;"><img alt="Done" src="../images/check.png" /></td></tr>' . "\n";
-  flush();
+	global $neutral_color;
+	$neutral_color = ( $neutral_color == 'A' ) ? 'C' : 'A';
+	echo '<tr><td style="width: 500px; background-color: #' . "{$neutral_color}{$neutral_color}FF{$neutral_color}{$neutral_color}" . '; padding: 0 5px;">' . htmlspecialchars($stage_name) . '</td><td style="padding: 0 5px;"><img alt="Done" src="../images/check.png" /></td></tr>' . "\n";
+	flush();
 }
 
 function echo_stage_failure($stage_id, $stage_name, $failure_explanation, $resume_stack)
 {
-  global $neutral_color;
-  global $lang;
-  
-  $neutral_color = ( $neutral_color == 'A' ) ? 'C' : 'A';
-  echo '<tr><td style="width: 500px; background-color: #' . "FF{$neutral_color}{$neutral_color}{$neutral_color}{$neutral_color}" . '; padding: 0 5px;">' . htmlspecialchars($stage_name) . '</td><td style="padding: 0 5px;"><img alt="Failed" src="../images/checkbad.png" /></td></tr>' . "\n";
-  flush();
-  close_install_table();
-  $post_data = '';
-  $mysql_error = mysql_error();
-  $file = ( defined('IN_ENANO_UPGRADE') ) ? 'upgrade.php' : 'install.php';
-  foreach ( $_POST as $key => $value )
-  {
-    // FIXME: These should really also be sanitized for double quotes
-    $value = htmlspecialchars($value);
-    $key = htmlspecialchars($key);
-    $post_data .= "          <input type=\"hidden\" name=\"$key\" value=\"$value\" />\n";
-  }
-  if ( $stage_id == 'renameconfig' )
-    echo '<p>' . $failure_explanation . '</p>';
-  else
-    echo '<form action="' . $file . '?stage=install&amp;sub=' . $stage_id . '" method="post">
-            ' . $post_data . '
-            <input type="hidden" name="resume_stack" value="' . htmlspecialchars(implode('|', $resume_stack)) . '" />
-            <h3>' . $lang->get('meta_msg_err_stagefailed_title') . '</h3>
-             <p>' . $failure_explanation . '</p>
-             ' . ( !empty($mysql_error) ? "<p>" . $lang->get('meta_msg_err_stagefailed_mysqlerror') . " $mysql_error</p>" : '' ) . '
-             <p>' . $lang->get('meta_msg_err_stagefailed_body') . '</p>
-             <p style="text-align: center;"><input type="submit" value="' . $lang->get('meta_btn_retry_installation') . '" /></p>
-          </form>';
-  global $ui;
-  $ui->show_footer();
-  exit;
+	global $neutral_color;
+	global $lang;
+	
+	$neutral_color = ( $neutral_color == 'A' ) ? 'C' : 'A';
+	echo '<tr><td style="width: 500px; background-color: #' . "FF{$neutral_color}{$neutral_color}{$neutral_color}{$neutral_color}" . '; padding: 0 5px;">' . htmlspecialchars($stage_name) . '</td><td style="padding: 0 5px;"><img alt="Failed" src="../images/checkbad.png" /></td></tr>' . "\n";
+	flush();
+	close_install_table();
+	$post_data = '';
+	$mysql_error = mysql_error();
+	$file = ( defined('IN_ENANO_UPGRADE') ) ? 'upgrade.php' : 'install.php';
+	foreach ( $_POST as $key => $value )
+	{
+		// FIXME: These should really also be sanitized for double quotes
+		$value = htmlspecialchars($value);
+		$key = htmlspecialchars($key);
+		$post_data .= "          <input type=\"hidden\" name=\"$key\" value=\"$value\" />\n";
+	}
+	if ( $stage_id == 'renameconfig' )
+		echo '<p>' . $failure_explanation . '</p>';
+	else
+		echo '<form action="' . $file . '?stage=install&amp;sub=' . $stage_id . '" method="post">
+						' . $post_data . '
+						<input type="hidden" name="resume_stack" value="' . htmlspecialchars(implode('|', $resume_stack)) . '" />
+						<h3>' . $lang->get('meta_msg_err_stagefailed_title') . '</h3>
+ 						<p>' . $failure_explanation . '</p>
+ 						' . ( !empty($mysql_error) ? "<p>" . $lang->get('meta_msg_err_stagefailed_mysqlerror') . " $mysql_error</p>" : '' ) . '
+ 						<p>' . $lang->get('meta_msg_err_stagefailed_body') . '</p>
+ 						<p style="text-align: center;"><input type="submit" value="' . $lang->get('meta_btn_retry_installation') . '" /></p>
+					</form>';
+	global $ui;
+	$ui->show_footer();
+	exit;
 }
 
 function enano_perform_upgrade($target_branch)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  // Import version info
-  global $enano_versions;
-  // Import UI functions
-  global $ui;
-  // This is needed for upgrade abstraction
-  global $dbdriver;
-  
-  // see if we're actually supposed to be in post-upgrade
-  if ( getConfig('enano_version') == 'upg-' . installer_enano_version() )
-  {
-    // yep, fall out here to avoid errors
-    return true;
-  }
-  
-  // Main upgrade stage
-  
-  // Init vars
-  list($major_version, $minor_version) = explode('.', installer_enano_version());
-  $installer_branch = "$major_version.$minor_version";
-  $installer_branch = preg_replace('/^upg-/', '', $installer_branch);
-  $target_branch = preg_replace('/^upg-/', '', $target_branch);
-  
-  $version_flipped = array_flip($enano_versions[$target_branch]);
-  $version_curr = enano_version();
-  // Change this to be the last version in the current branch.
-  // If we're just upgrading within this branch, use the version the installer library
-  // reports to us. Else, use the latest in the old (current target) branch.
-  // $version_target = installer_enano_version();
-  $version_target = ( $target_branch === $installer_branch ) ? installer_enano_version() : $enano_versions[$target_branch][ count($enano_versions[$target_branch]) - 1 ];
-  
-  // Calculate which scripts to run
-  if ( !isset($version_flipped[$version_curr]) )
-  {
-    echo '<p>ERROR: Unsupported version</p>';
-    $ui->show_footer();
-    exit;
-  }
-  if ( !isset($version_flipped[$version_target]) )
-  {
-    echo '<p>ERROR: Upgrader doesn\'t support its own version</p>';
-    $ui->show_footer();
-    exit;
-  }
-  $upg_queue = array();
-  for ( $i = $version_flipped[$version_curr]; $i < $version_flipped[$version_target]; $i++ )
-  {
-    if ( !isset($enano_versions[$target_branch][$i + 1]) )
-    {
-      echo '<p>ERROR: Unsupported intermediate version</p>';
-      $ui->show_footer();
-      exit;
-    }
-    $ver_this = $enano_versions[$target_branch][$i];
-    $ver_next = $enano_versions[$target_branch][$i + 1];
-    $upg_queue[] = array($ver_this, $ver_next);
-  }
-  
-  // Verify that all upgrade scripts are usable
-  foreach ( $upg_queue as $verset )
-  {
-    $file = ENANO_ROOT . "/install/schemas/upgrade/{$verset[0]}-{$verset[1]}-$dbdriver.sql";
-    if ( !file_exists($file) )
-    {
-      echo "<p>ERROR: Couldn't find required schema file: $file</p>";
-      $ui->show_footer();
-      exit;
-    }
-  }
-  // Perform upgrade
-  foreach ( $upg_queue as $verset )
-  {
-    $file = ENANO_ROOT . "/install/schemas/upgrade/{$verset[0]}-{$verset[1]}-$dbdriver.sql";
-    try
-    {
-      $parser = new SQL_Parser($file);
-    }
-    catch(Exception $e)
-    {
-      die("<pre>$e</pre>");
-    }
-    
-    $parser->assign_vars(array(
-      'TABLE_PREFIX' => table_prefix
-    ));
-  
-    $sql_list = $parser->parse();
-    // Check for empty schema file
-    if ( $sql_list[0] === ';' && count($sql_list) == 1 )
-    {
-      // It's empty, report success for this version
-      // See below for explanation of why setConfig() is called here
-      setConfig('enano_version', $verset[1]);
-      continue;
-    }
-    
-    foreach ( $sql_list as $sql )
-    {
-      // check for '@' operator on query
-      if ( substr($sql, 0, 1) == '@' )
-      {
-        // Yes - perform query but don't check for errors
-        $db->sql_query($sql);
-      }
-      else
-      {
-        // Perform as normal
-        if ( !$db->sql_query($sql) )
-          $db->_die();
-      }
-    }
-    
-    // Is there an additional script (logic) to be run after the schema?
-    $postscript = ENANO_ROOT . "/install/schemas/upgrade/{$verset[0]}-{$verset[1]}.php";
-    if ( file_exists($postscript) )
-      @include($postscript);
-    
-    // The advantage of calling setConfig on the system version here?
-    // Simple. If the upgrade fails, it will pick up from the last
-    // version, not try to start again from the beginning. This will
-    // still cause errors in most cases though. Eventually we probably
-    // need some sort of query-numbering system that tracks in-progress
-    // upgrades.
-    
-    setConfig('enano_version', $verset[1]);
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	// Import version info
+	global $enano_versions;
+	// Import UI functions
+	global $ui;
+	// This is needed for upgrade abstraction
+	global $dbdriver;
+	
+	// see if we're actually supposed to be in post-upgrade
+	if ( getConfig('enano_version') == 'upg-' . installer_enano_version() )
+	{
+		// yep, fall out here to avoid errors
+		return true;
+	}
+	
+	// Main upgrade stage
+	
+	// Init vars
+	list($major_version, $minor_version) = explode('.', installer_enano_version());
+	$installer_branch = "$major_version.$minor_version";
+	$installer_branch = preg_replace('/^upg-/', '', $installer_branch);
+	$target_branch = preg_replace('/^upg-/', '', $target_branch);
+	
+	$version_flipped = array_flip($enano_versions[$target_branch]);
+	$version_curr = enano_version();
+	// Change this to be the last version in the current branch.
+	// If we're just upgrading within this branch, use the version the installer library
+	// reports to us. Else, use the latest in the old (current target) branch.
+	// $version_target = installer_enano_version();
+	$version_target = ( $target_branch === $installer_branch ) ? installer_enano_version() : $enano_versions[$target_branch][ count($enano_versions[$target_branch]) - 1 ];
+	
+	// Calculate which scripts to run
+	if ( !isset($version_flipped[$version_curr]) )
+	{
+		echo '<p>ERROR: Unsupported version</p>';
+		$ui->show_footer();
+		exit;
+	}
+	if ( !isset($version_flipped[$version_target]) )
+	{
+		echo '<p>ERROR: Upgrader doesn\'t support its own version</p>';
+		$ui->show_footer();
+		exit;
+	}
+	$upg_queue = array();
+	for ( $i = $version_flipped[$version_curr]; $i < $version_flipped[$version_target]; $i++ )
+	{
+		if ( !isset($enano_versions[$target_branch][$i + 1]) )
+		{
+			echo '<p>ERROR: Unsupported intermediate version</p>';
+			$ui->show_footer();
+			exit;
+		}
+		$ver_this = $enano_versions[$target_branch][$i];
+		$ver_next = $enano_versions[$target_branch][$i + 1];
+		$upg_queue[] = array($ver_this, $ver_next);
+	}
+	
+	// Verify that all upgrade scripts are usable
+	foreach ( $upg_queue as $verset )
+	{
+		$file = ENANO_ROOT . "/install/schemas/upgrade/{$verset[0]}-{$verset[1]}-$dbdriver.sql";
+		if ( !file_exists($file) )
+		{
+			echo "<p>ERROR: Couldn't find required schema file: $file</p>";
+			$ui->show_footer();
+			exit;
+		}
+	}
+	// Perform upgrade
+	foreach ( $upg_queue as $verset )
+	{
+		$file = ENANO_ROOT . "/install/schemas/upgrade/{$verset[0]}-{$verset[1]}-$dbdriver.sql";
+		try
+		{
+			$parser = new SQL_Parser($file);
+		}
+		catch(Exception $e)
+		{
+			die("<pre>$e</pre>");
+		}
+		
+		$parser->assign_vars(array(
+			'TABLE_PREFIX' => table_prefix
+		));
+	
+		$sql_list = $parser->parse();
+		// Check for empty schema file
+		if ( $sql_list[0] === ';' && count($sql_list) == 1 )
+		{
+			// It's empty, report success for this version
+			// See below for explanation of why setConfig() is called here
+			setConfig('enano_version', $verset[1]);
+			continue;
+		}
+		
+		foreach ( $sql_list as $sql )
+		{
+			// check for '@' operator on query
+			if ( substr($sql, 0, 1) == '@' )
+			{
+				// Yes - perform query but don't check for errors
+				$db->sql_query($sql);
+			}
+			else
+			{
+				// Perform as normal
+				if ( !$db->sql_query($sql) )
+					$db->_die();
+			}
+		}
+		
+		// Is there an additional script (logic) to be run after the schema?
+		$postscript = ENANO_ROOT . "/install/schemas/upgrade/{$verset[0]}-{$verset[1]}.php";
+		if ( file_exists($postscript) )
+			@include($postscript);
+		
+		// The advantage of calling setConfig on the system version here?
+		// Simple. If the upgrade fails, it will pick up from the last
+		// version, not try to start again from the beginning. This will
+		// still cause errors in most cases though. Eventually we probably
+		// need some sort of query-numbering system that tracks in-progress
+		// upgrades.
+		
+		setConfig('enano_version', $verset[1]);
+	}
 }
 
--- a/install/includes/libenanoinstallcli.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/libenanoinstallcli.php	Sun Mar 28 23:10:46 2010 -0400
@@ -15,21 +15,21 @@
 
 function run_installer_stage($stage_id, $stage_name, $function, $failure_explanation, $allow_skip = true)
 {
-  global $silent, $lang;
-  
-  if ( !$silent )
-    echo parse_shellcolor_string($lang->get("cli_msg_$stage_name"));
-  
-  $result = call_user_func($function);
-  
-  if ( !$result )
-  {
-    if ( !$silent )
-      echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
-    installer_fail($lang->get("cli_err_$stage_name"));
-  }
-  
-  if ( !$silent )
-    echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
+	global $silent, $lang;
+	
+	if ( !$silent )
+		echo parse_shellcolor_string($lang->get("cli_msg_$stage_name"));
+	
+	$result = call_user_func($function);
+	
+	if ( !$result )
+	{
+		if ( !$silent )
+			echo parse_shellcolor_string($lang->get('cli_test_fail')) . "\n";
+		installer_fail($lang->get("cli_err_$stage_name"));
+	}
+	
+	if ( !$silent )
+		echo parse_shellcolor_string($lang->get('cli_msg_ok')) . "\n";
 }
 
--- a/install/includes/payload.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/payload.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,182 +14,182 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 return true;
 
 function stg_sim_good()
 {
-  return true;
+	return true;
 }
 
 function stg_sim_bad()
 {
-  return true;
+	return true;
 }
 
 function stg_password_decode()
 {
-  global $db;
-  static $pass = false;
-  
-  if ( $pass )
-    return $pass;
-  
-  if ( !isset($_POST['crypt_data']) && !empty($_POST['password']) && $_POST['password'] === $_POST['password_confirm'] )
-    $pass = $_POST['password'];
-  
-  $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-  // retrieve encryption key
-  $q = $db->sql_query('SELECT config_value FROM ' . table_prefix . 'config WHERE config_name=\'install_aes_key\';');
-  if ( !$q )
-    $db->_die();
-  if ( $db->numrows() < 1 )
-    return false;
-  list($aes_key) = $db->fetchrow_num();
-  $aes_key = hexdecode($aes_key);
-  
-  $pass = $aes->decrypt($_POST['crypt_data'], $aes_key, ENC_HEX);
-  if ( !$pass )
-    return false;
-  
-  return $pass; // Will be true if the password isn't crapped
+	global $db;
+	static $pass = false;
+	
+	if ( $pass )
+		return $pass;
+	
+	if ( !isset($_POST['crypt_data']) && !empty($_POST['password']) && $_POST['password'] === $_POST['password_confirm'] )
+		$pass = $_POST['password'];
+	
+	$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+	// retrieve encryption key
+	$q = $db->sql_query('SELECT config_value FROM ' . table_prefix . 'config WHERE config_name=\'install_aes_key\';');
+	if ( !$q )
+		$db->_die();
+	if ( $db->numrows() < 1 )
+		return false;
+	list($aes_key) = $db->fetchrow_num();
+	$aes_key = hexdecode($aes_key);
+	
+	$pass = $aes->decrypt($_POST['crypt_data'], $aes_key, ENC_HEX);
+	if ( !$pass )
+		return false;
+	
+	return $pass; // Will be true if the password isn't crapped
 }
 
 function stg_make_private_key()
 {
-  global $db;
-  static $site_key = false;
-  
-  if ( $site_key )
-    return $site_key;
-  
-  // Is there already a key cached in the database?
-  $q = $db->sql_query('SELECT config_value FROM ' . table_prefix . 'config WHERE config_name=\'site_aes_key\';');
-  if ( !$q )
-    $db->_die();
-  
-  if ( $db->numrows() > 0 )
-  {
-    list($site_key) = $db->fetchrow_num();
-    $db->free_result();
-    return $site_key;
-  }
-  
-  $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-  // This will use /dev/urandom if possible
-  $site_key = $aes->gen_readymade_key();
-  
-  // Stash it in the database, don't check for errors though because we can always regenerate it
-  $db->sql_query('INSERT INTO ' . table_prefix . 'config ( config_name, config_value ) VALUES ( \'site_aes_key\', \'' . $site_key . '\' );');
-  
-  return $site_key;
+	global $db;
+	static $site_key = false;
+	
+	if ( $site_key )
+		return $site_key;
+	
+	// Is there already a key cached in the database?
+	$q = $db->sql_query('SELECT config_value FROM ' . table_prefix . 'config WHERE config_name=\'site_aes_key\';');
+	if ( !$q )
+		$db->_die();
+	
+	if ( $db->numrows() > 0 )
+	{
+		list($site_key) = $db->fetchrow_num();
+		$db->free_result();
+		return $site_key;
+	}
+	
+	$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+	// This will use /dev/urandom if possible
+	$site_key = $aes->gen_readymade_key();
+	
+	// Stash it in the database, don't check for errors though because we can always regenerate it
+	$db->sql_query('INSERT INTO ' . table_prefix . 'config ( config_name, config_value ) VALUES ( \'site_aes_key\', \'' . $site_key . '\' );');
+	
+	return $site_key;
 }
 
 function stg_load_schema()
 {
-  global $db, $dbdriver, $installer_version, $lang_id, $languages;
-  static $sql_parser = false;
-  
-  if ( is_object($sql_parser) )
-    return $sql_parser->parse();
-  
-  $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-  $hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
-  
-  $site_key = stg_make_private_key();
-  $site_key = hexdecode($site_key);
-  $admin_pass_clean = stg_password_decode();
-  $admin_pass = hmac_sha1($admin_pass_clean, $hmac_secret);
-  
-  unset($admin_pass_clean); // Security
-  
-  try
-  {
-    $sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$dbdriver}_stage2.sql" );
-  }
-  catch ( Exception $e )
-  {
-    echo "<pre>$e</pre>";
-    return false;
-  }
-  
-  $vars = array(
-      'TABLE_PREFIX'         => table_prefix,
-      'SITE_NAME'            => $db->escape($_POST['site_name']),
-      'SITE_DESC'            => $db->escape($_POST['site_desc']),
-      'COPYRIGHT'            => $db->escape($_POST['copyright']),
-      // FIXME: update form
-      'WIKI_MODE'            => ( isset($_POST['wiki_mode']) ? '1' : '0' ),
-      'ENABLE_CACHE'         => ( is_writable( ENANO_ROOT . '/cache/' ) ? '1' : '0' ),
-      'VERSION'              => $installer_version['version'],
-      'ADMIN_USER'           => $db->escape($_POST['username']),
-      'ADMIN_PASS'           => $admin_pass,
-      'ADMIN_PASS_SALT'      => $hmac_secret,
-      'ADMIN_EMAIL'          => $db->escape($_POST['email']),
-      'REAL_NAME'            => '', // This has always been stubbed.
-      'ADMIN_EMBED_PHP'      => strval(AUTH_DISALLOW),
-      'UNIX_TIME'            => strval(time()),
-      'IP_ADDRESS'           => $db->escape($_SERVER['REMOTE_ADDR'])
-    );
-  
-  $sql_parser->assign_vars($vars);
-  return true;
+	global $db, $dbdriver, $installer_version, $lang_id, $languages;
+	static $sql_parser = false;
+	
+	if ( is_object($sql_parser) )
+		return $sql_parser->parse();
+	
+	$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+	$hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
+	
+	$site_key = stg_make_private_key();
+	$site_key = hexdecode($site_key);
+	$admin_pass_clean = stg_password_decode();
+	$admin_pass = hmac_sha1($admin_pass_clean, $hmac_secret);
+	
+	unset($admin_pass_clean); // Security
+	
+	try
+	{
+		$sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$dbdriver}_stage2.sql" );
+	}
+	catch ( Exception $e )
+	{
+		echo "<pre>$e</pre>";
+		return false;
+	}
+	
+	$vars = array(
+			'TABLE_PREFIX'         => table_prefix,
+			'SITE_NAME'            => $db->escape($_POST['site_name']),
+			'SITE_DESC'            => $db->escape($_POST['site_desc']),
+			'COPYRIGHT'            => $db->escape($_POST['copyright']),
+			// FIXME: update form
+			'WIKI_MODE'            => ( isset($_POST['wiki_mode']) ? '1' : '0' ),
+			'ENABLE_CACHE'         => ( is_writable( ENANO_ROOT . '/cache/' ) ? '1' : '0' ),
+			'VERSION'              => $installer_version['version'],
+			'ADMIN_USER'           => $db->escape($_POST['username']),
+			'ADMIN_PASS'           => $admin_pass,
+			'ADMIN_PASS_SALT'      => $hmac_secret,
+			'ADMIN_EMAIL'          => $db->escape($_POST['email']),
+			'REAL_NAME'            => '', // This has always been stubbed.
+			'ADMIN_EMBED_PHP'      => strval(AUTH_DISALLOW),
+			'UNIX_TIME'            => strval(time()),
+			'IP_ADDRESS'           => $db->escape($_SERVER['REMOTE_ADDR'])
+		);
+	
+	$sql_parser->assign_vars($vars);
+	return true;
 }
 
 function stg_deliver_payload()
 {
-  global $db;
-  $schema = stg_load_schema();
-  foreach ( $schema as $sql )
-  {
-    if ( !$db->sql_query($sql) )
-    {
-      echo $db->get_error();
-      return false;
-    }
-  }
-  return true;
+	global $db;
+	$schema = stg_load_schema();
+	foreach ( $schema as $sql )
+	{
+		if ( !$db->sql_query($sql) )
+		{
+			echo $db->get_error();
+			return false;
+		}
+	}
+	return true;
 }
 
 function stg_write_config()
 {
-  global $dbhost, $dbuser, $dbpasswd, $dbname, $dbdriver, $dbport;
-  $db_data = array(
-      'host' => str_replace("'", "\\'", $dbhost),
-      'user' => str_replace("'", "\\'", $dbuser),
-      'pass' => str_replace("'", "\\'", $dbpasswd),
-      'name' => str_replace("'", "\\'", $dbname),
-      'port' => intval($dbport),
-      'tp' => table_prefix,
-      'drv' => $dbdriver
-    );
-  
-  // Retrieves the existing key
-  $site_key = stg_make_private_key();
-  
-  // Determine contentPath
-  switch ( @$_POST['url_scheme'] )
-  {
-    case 'standard':
-    default:
-      $sp_append = '/index.php?title=';
-      break;
-    case 'shortened':
-      $sp_append = '/index.php/';
-      break;
-    case 'tiny':
-      $sp_append = '/?/';
-      break;
-    case 'rewrite':
-      $sp_append = '/';
-      break;
-  }
-  
-  $scriptpath = scriptPath;
-  $contentpath = $scriptpath . $sp_append;
-  
-  $config_file = <<<EOF
+	global $dbhost, $dbuser, $dbpasswd, $dbname, $dbdriver, $dbport;
+	$db_data = array(
+			'host' => str_replace("'", "\\'", $dbhost),
+			'user' => str_replace("'", "\\'", $dbuser),
+			'pass' => str_replace("'", "\\'", $dbpasswd),
+			'name' => str_replace("'", "\\'", $dbname),
+			'port' => intval($dbport),
+			'tp' => table_prefix,
+			'drv' => $dbdriver
+		);
+	
+	// Retrieves the existing key
+	$site_key = stg_make_private_key();
+	
+	// Determine contentPath
+	switch ( @$_POST['url_scheme'] )
+	{
+		case 'standard':
+		default:
+			$sp_append = '/index.php?title=';
+			break;
+		case 'shortened':
+			$sp_append = '/index.php/';
+			break;
+		case 'tiny':
+			$sp_append = '/?/';
+			break;
+		case 'rewrite':
+			$sp_append = '/';
+			break;
+	}
+	
+	$scriptpath = scriptPath;
+	$contentpath = $scriptpath . $sp_append;
+	
+	$config_file = <<<EOF
 <?php
 
 /**
@@ -227,30 +227,30 @@
 // if they're already defined, no use re-defining them
 if ( !defined('ENANO_CONSTANTS') )
 {
-  // The prefix for the tables in the database. Useful for holding more than
-  // one Enano installation in the same database.
-  define('table_prefix', '{$db_data['tp']}');
-  
-  // The path to Enano's files on your server, from the document root. If
-  // Enano is installed in your document root this will be blank; installing
-  // Enano in /enano/ will result in "/enano" here, etc.
-  define('scriptPath', '$scriptpath');
-  
-  // The authoritative prefix for pages. This should be very literal: to
-  // generate a URL on the site, the format is basically
-  // contentPath . \$page_name. This is based off of scriptPath and the URL
-  // scheme selected during installation. Pattern:
-  //
-  //    * Standard URLs:  scriptPath . '/index.php?title='
-  //    * Shortened URLs: scriptPath . '/index.php/'
-  //    * mod_rewrite:    scriptPath . '/'
-  
-  define('contentPath', '$contentpath');
-  
-  // Tell the Enano API that we're installed and that this file is complete
-  define('ENANO_INSTALLED', 'You bet!');
-  
-  define('ENANO_CONSTANTS', '');
+	// The prefix for the tables in the database. Useful for holding more than
+	// one Enano installation in the same database.
+	define('table_prefix', '{$db_data['tp']}');
+	
+	// The path to Enano's files on your server, from the document root. If
+	// Enano is installed in your document root this will be blank; installing
+	// Enano in /enano/ will result in "/enano" here, etc.
+	define('scriptPath', '$scriptpath');
+	
+	// The authoritative prefix for pages. This should be very literal: to
+	// generate a URL on the site, the format is basically
+	// contentPath . \$page_name. This is based off of scriptPath and the URL
+	// scheme selected during installation. Pattern:
+	//
+	//    * Standard URLs:  scriptPath . '/index.php?title='
+	//    * Shortened URLs: scriptPath . '/index.php/'
+	//    * mod_rewrite:    scriptPath . '/'
+	
+	define('contentPath', '$contentpath');
+	
+	// Tell the Enano API that we're installed and that this file is complete
+	define('ENANO_INSTALLED', 'You bet!');
+	
+	define('ENANO_CONSTANTS', '');
 }
 
 // The AES encryption key used for encrypting various bits of information,
@@ -262,23 +262,23 @@
 \$crypto_key = '$site_key';
 
 EOF;
-  
-  // Write config file
-  
-  $ch = @fopen ( ENANO_ROOT . '/config.new.php', 'w' );
-  if ( !$ch )
-    return false;
-  
-  fwrite($ch, $config_file);
-  fclose($ch);
-  
-  // If we are using mod_rewrite, also append any existing .htaccess
-  if ( @$_POST['url_scheme'] === 'rewrite' )
-  {
-    $hh = @fopen ( ENANO_ROOT . '/.htaccess.new', 'w' );
-    if ( !$hh )
-      return false;
-    $hhc = <<<EOF
+	
+	// Write config file
+	
+	$ch = @fopen ( ENANO_ROOT . '/config.new.php', 'w' );
+	if ( !$ch )
+		return false;
+	
+	fwrite($ch, $config_file);
+	fclose($ch);
+	
+	// If we are using mod_rewrite, also append any existing .htaccess
+	if ( @$_POST['url_scheme'] === 'rewrite' )
+	{
+		$hh = @fopen ( ENANO_ROOT . '/.htaccess.new', 'w' );
+		if ( !$hh )
+			return false;
+		$hhc = <<<EOF
 #
 # START ENANO RULES
 #
@@ -297,217 +297,217 @@
 RewriteRule (.*) index.php?title=\$1 [L,QSA]
 
 EOF;
-    fwrite($hh, $hhc);
-    fclose($hh);
-  }
-  
-  return true;
+		fwrite($hh, $hhc);
+		fclose($hh);
+	}
+	
+	return true;
 }
 
 function stg_language_setup()
 {
-  global $languages, $db;
-  global $lang_id;
-  $lang_info =& $languages[$lang_id];
-  if ( !is_array($lang_info) )
-    return false;
-  
-  // Install the language
-  // ($lang_code, $lang_name_neutral, $lang_name_local, $lang_file = false)
-  $result = install_language($lang_id, $lang_info['name_eng'], $lang_info['name'], ENANO_ROOT . "/language/{$lang_info['dir']}/core.json");
-  if ( !$result )
-    return false;
-  
-  $lang_local = new Language($lang_id);
-  
-  $lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/user.json" );
-  $lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/tools.json" );
-  $lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/admin.json" );
-  
-  $q = $db->sql_query('SELECT lang_id FROM ' . table_prefix . 'language ORDER BY lang_id DESC LIMIT 1;');
-  if ( !$q )
-    $db->_die();
-  
-  list($lang_id_int) = $db->fetchrow_num();
-  $db->free_result();
-  setConfig('default_language', $lang_id_int);
-  
-  return true;
+	global $languages, $db;
+	global $lang_id;
+	$lang_info =& $languages[$lang_id];
+	if ( !is_array($lang_info) )
+		return false;
+	
+	// Install the language
+	// ($lang_code, $lang_name_neutral, $lang_name_local, $lang_file = false)
+	$result = install_language($lang_id, $lang_info['name_eng'], $lang_info['name'], ENANO_ROOT . "/language/{$lang_info['dir']}/core.json");
+	if ( !$result )
+		return false;
+	
+	$lang_local = new Language($lang_id);
+	
+	$lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/user.json" );
+	$lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/tools.json" );
+	$lang_local->import( ENANO_ROOT . "/language/{$lang_info['dir']}/admin.json" );
+	
+	$q = $db->sql_query('SELECT lang_id FROM ' . table_prefix . 'language ORDER BY lang_id DESC LIMIT 1;');
+	if ( !$q )
+		$db->_die();
+	
+	list($lang_id_int) = $db->fetchrow_num();
+	$db->free_result();
+	setConfig('default_language', $lang_id_int);
+	
+	return true;
 }
 
 function stg_add_content()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $cache;
-  
-  global $languages;
-  global $lang_id;
-  $lang_info =& $languages[$lang_id];
-  if ( !is_array($lang_info) )
-    return false;
-  
-  if ( $_POST['default_content_type'] === 'tutorial' )
-  {
-    $dir = ENANO_ROOT . "/language/{$lang_info['dir']}/install/default-tutorial";
-  }
-  else
-  {
-    $dir = ENANO_ROOT . "/language/{$lang_info['dir']}/install/default-blank";
-  }
-  
-  if ( !$dr = @opendir($dir) )
-    return false;
-  
-  while ( $dh = @readdir($dr) )
-  {
-    if ( !preg_match('/\.txt$/', $dh) )
-      continue;
-    
-    $page_contents = @file_get_contents("$dir/$dh");
-    if ( empty($page_contents) )
-      return false;
-    
-    $page_name = preg_replace('/\.txt$/', '', $dh);
-    
-    if ( !install_primitive_page_creator($page_name, 'Article', $page_contents) )
-      return false;
-  }
-  
-  closedir($dr);
-  
-  $cache->purge('page_meta');
-  
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $cache;
+	
+	global $languages;
+	global $lang_id;
+	$lang_info =& $languages[$lang_id];
+	if ( !is_array($lang_info) )
+		return false;
+	
+	if ( $_POST['default_content_type'] === 'tutorial' )
+	{
+		$dir = ENANO_ROOT . "/language/{$lang_info['dir']}/install/default-tutorial";
+	}
+	else
+	{
+		$dir = ENANO_ROOT . "/language/{$lang_info['dir']}/install/default-blank";
+	}
+	
+	if ( !$dr = @opendir($dir) )
+		return false;
+	
+	while ( $dh = @readdir($dr) )
+	{
+		if ( !preg_match('/\.txt$/', $dh) )
+			continue;
+		
+		$page_contents = @file_get_contents("$dir/$dh");
+		if ( empty($page_contents) )
+			return false;
+		
+		$page_name = preg_replace('/\.txt$/', '', $dh);
+		
+		if ( !install_primitive_page_creator($page_name, 'Article', $page_contents) )
+			return false;
+	}
+	
+	closedir($dr);
+	
+	$cache->purge('page_meta');
+	
+	return true;
 }
 
 function install_primitive_page_creator($page_id, $namespace, $content)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  $page_title = $db->escape(str_replace('_', ' ', dirtify_page_id($page_id)));
-  $author = $db->escape($_POST['username']);
-  $page_id = $db->escape($page_id);
-  $namespace = $db->escape($namespace);
-  // yes, we do probably want strip_all_php ON.
-  $content = RenderMan::preprocess_text($content, true, true);
-  $now = time();
-  
-  // query 1: logs
-  $q = $db->sql_query('INSERT INTO ' . table_prefix . "logs(time_id, date_string, log_type, action, page_id, namespace, author, page_text) VALUES\n"
-                    . "  ( $now, 'DEPRECATED', 'page', 'edit', '$page_id', '$namespace', '$author', '$content');");
-  if ( !$q )
-  {
-    echo $db->get_error();
-    return false;
-  }
-  
-  // query 2: page_text
-  $q = $db->sql_query('INSERT INTO ' . table_prefix . "page_text(page_id, namespace, page_text) VALUES\n"
-                    . "  ( '$page_id', '$namespace', '$content');");
-  if ( !$q )
-  {
-    echo $db->get_error();
-    return false;
-  }
-  
-  // query 3: pages
-  $q = $db->sql_query('INSERT INTO ' . table_prefix . "pages(page_order, name, urlname, namespace, special, visible, comments_on, protected, delvotes, delvote_ips) VALUES\n"
-                    . "  (NULL, '$page_title', '$page_id', '$namespace', 0, 1, 1, 1, 0, '');");
-  if ( !$q )
-  {
-    echo $db->get_error();
-    return false;
-  }
-  
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	$page_title = $db->escape(str_replace('_', ' ', dirtify_page_id($page_id)));
+	$author = $db->escape($_POST['username']);
+	$page_id = $db->escape($page_id);
+	$namespace = $db->escape($namespace);
+	// yes, we do probably want strip_all_php ON.
+	$content = RenderMan::preprocess_text($content, true, true);
+	$now = time();
+	
+	// query 1: logs
+	$q = $db->sql_query('INSERT INTO ' . table_prefix . "logs(time_id, date_string, log_type, action, page_id, namespace, author, page_text) VALUES\n"
+										. "  ( $now, 'DEPRECATED', 'page', 'edit', '$page_id', '$namespace', '$author', '$content');");
+	if ( !$q )
+	{
+		echo $db->get_error();
+		return false;
+	}
+	
+	// query 2: page_text
+	$q = $db->sql_query('INSERT INTO ' . table_prefix . "page_text(page_id, namespace, page_text) VALUES\n"
+										. "  ( '$page_id', '$namespace', '$content');");
+	if ( !$q )
+	{
+		echo $db->get_error();
+		return false;
+	}
+	
+	// query 3: pages
+	$q = $db->sql_query('INSERT INTO ' . table_prefix . "pages(page_order, name, urlname, namespace, special, visible, comments_on, protected, delvotes, delvote_ips) VALUES\n"
+										. "  (NULL, '$page_title', '$page_id', '$namespace', 0, 1, 1, 1, 0, '');");
+	if ( !$q )
+	{
+		echo $db->get_error();
+		return false;
+	}
+	
+	return true;
 }
 
 function stg_init_logs()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $installer_version;
-  
-  $q = $db->sql_query('INSERT INTO ' . table_prefix . 'logs(log_type,action,time_id,date_string,author,author_uid,page_text,edit_summary) VALUES(\'security\', \'install_enano\', ' . time() . ', \'' . enano_date(ED_DATE | ED_TIME) . '\', \'' . $db->escape($_POST['username']) . '\', 2, \'' . $db->escape(enano_version()) . '\', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\');');
-  if ( !$q )
-  {
-    echo '<p><tt>MySQL return: ' . $db->sql_error() . '</tt></p>';
-    return false;
-  }
-  
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $installer_version;
+	
+	$q = $db->sql_query('INSERT INTO ' . table_prefix . 'logs(log_type,action,time_id,date_string,author,author_uid,page_text,edit_summary) VALUES(\'security\', \'install_enano\', ' . time() . ', \'' . enano_date(ED_DATE | ED_TIME) . '\', \'' . $db->escape($_POST['username']) . '\', 2, \'' . $db->escape(enano_version()) . '\', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\');');
+	if ( !$q )
+	{
+		echo '<p><tt>MySQL return: ' . $db->sql_error() . '</tt></p>';
+		return false;
+	}
+	
+	return true;
 }
 
 function stg_aes_cleanup()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  $q = $db->sql_query('DELETE FROM ' . table_prefix . 'config WHERE config_name = \'install_aes_key\' OR config_name = \'site_aes_key\';');
-  if ( !$q )
-    $db->_die();
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	$q = $db->sql_query('DELETE FROM ' . table_prefix . 'config WHERE config_name = \'install_aes_key\' OR config_name = \'site_aes_key\';');
+	if ( !$q )
+		$db->_die();
+	return true;
 }
 
 function _stg_rename_config_revert()
 {
-  if ( file_exists('./config.php') )
-  {
-    @rename('./config.php', './config.new.php');
-  }
-  
-  $handle = @fopen('./config.php.new', 'w');
-  if ( !$handle )
-    return false;
-  $contents = '<?php $cryptkey = \'' . _INSTRESUME_AES_KEYBACKUP . '\'; ?>';
-  fwrite($handle, $contents);
-  fclose($handle);
-  return true;
+	if ( file_exists('./config.php') )
+	{
+		@rename('./config.php', './config.new.php');
+	}
+	
+	$handle = @fopen('./config.php.new', 'w');
+	if ( !$handle )
+		return false;
+	$contents = '<?php $cryptkey = \'' . _INSTRESUME_AES_KEYBACKUP . '\'; ?>';
+	fwrite($handle, $contents);
+	fclose($handle);
+	return true;
 }
 
 function stg_build_index()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if ( $paths->rebuild_search_index() )
-    return true;
-  return false;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	if ( $paths->rebuild_search_index() )
+		return true;
+	return false;
 }
 
 function stg_rename_config()
 {
-  if ( !@rename(ENANO_ROOT . '/config.new.php', ENANO_ROOT . '/config.php') )
-  {
-    echo '<p>Can\'t rename config.php</p>';
-    _stg_rename_config_revert();
-    return false;
-  }
-  
-  if ( @filesize(ENANO_ROOT . '/.htaccess.new') > 1 )
-  {
-    // rename/possibly concatenate .htaccess.new
-    $htaccess_base = '';
-    if ( file_exists(ENANO_ROOT . '/.htaccess') )
-      $htaccess_base .= @file_get_contents(ENANO_ROOT . '/.htaccess');
-    if ( strlen($htaccess_base) > 0 && !preg_match("/\n$/", $htaccess_base) )
-      $htaccess_base .= "\n\n";
-    $htaccess_base .= @file_get_contents(ENANO_ROOT . '/.htaccess.new');
-    if ( file_exists(ENANO_ROOT . '/.htaccess') )
-    {
-      $hh = @fopen(ENANO_ROOT . '/.htaccess', 'w');
-      if ( !$hh )
-        return false;
-      fwrite($hh, $htaccess_base);
-      fclose($hh);
-      @unlink(ENANO_ROOT . '/.htaccess.new');
-      return true;
-    }
-    else
-    {
-      return @rename(ENANO_ROOT . '/.htaccess.new', ENANO_ROOT . '/.htaccess');
-    }
-  }
-  else
-  {
-    @unlink(ENANO_ROOT . '/.htaccess.new');
-  }
-  return true;
+	if ( !@rename(ENANO_ROOT . '/config.new.php', ENANO_ROOT . '/config.php') )
+	{
+		echo '<p>Can\'t rename config.php</p>';
+		_stg_rename_config_revert();
+		return false;
+	}
+	
+	if ( @filesize(ENANO_ROOT . '/.htaccess.new') > 1 )
+	{
+		// rename/possibly concatenate .htaccess.new
+		$htaccess_base = '';
+		if ( file_exists(ENANO_ROOT . '/.htaccess') )
+			$htaccess_base .= @file_get_contents(ENANO_ROOT . '/.htaccess');
+		if ( strlen($htaccess_base) > 0 && !preg_match("/\n$/", $htaccess_base) )
+			$htaccess_base .= "\n\n";
+		$htaccess_base .= @file_get_contents(ENANO_ROOT . '/.htaccess.new');
+		if ( file_exists(ENANO_ROOT . '/.htaccess') )
+		{
+			$hh = @fopen(ENANO_ROOT . '/.htaccess', 'w');
+			if ( !$hh )
+				return false;
+			fwrite($hh, $htaccess_base);
+			fclose($hh);
+			@unlink(ENANO_ROOT . '/.htaccess.new');
+			return true;
+		}
+		else
+		{
+			return @rename(ENANO_ROOT . '/.htaccess.new', ENANO_ROOT . '/.htaccess');
+		}
+	}
+	else
+	{
+		@unlink(ENANO_ROOT . '/.htaccess.new');
+	}
+	return true;
 }
 
 /**
@@ -516,61 +516,61 @@
 
 function stg_lang_import()
 {
-  global $db, $languages, $do_langimport;
-  
-  define('IN_ENANO_UPGRADE_POST', 1);
-  
-  //
-  // IMPORT NEW STRINGS
-  //
-  
-  // for each installed language, look for the json files in the filesystem and if they're ok, import new strings from them
-  $q = $db->sql_query('SELECT lang_id, lang_code FROM ' . table_prefix . "language;");
-  if ( !$q )
-    $db->_die();
-  
-  while ( $row = $db->fetchrow($q) )
-  {
-    if ( isset($languages[$row['lang_code']]) )
-    {
-      // found a language and it's good on the filesystem; load it and call a reimport
-      $lang_local = new Language($row['lang_id']);
-      // call fetch to make sure we're up to date
-      $lang_local->fetch();
-      // import
-      foreach ( array('core', 'admin', 'user', 'tools') as $language_file )
-      {
-        // generate full path
-        $language_file = ENANO_ROOT . "/language/{$languages[$row['lang_code']]['dir']}/$language_file.json";
-        // setting the second parameter to bool(true) causes it to skip existing strings
-        if ( !$lang_local->import($language_file, ( !$do_langimport )) )
-          // on failure, report failure to libenanoinstall
-          return false;
-      }
-      // unload this lang_local object to save memory
-      unset($lang_local);
-    }
-  }
-  
-  return true;
+	global $db, $languages, $do_langimport;
+	
+	define('IN_ENANO_UPGRADE_POST', 1);
+	
+	//
+	// IMPORT NEW STRINGS
+	//
+	
+	// for each installed language, look for the json files in the filesystem and if they're ok, import new strings from them
+	$q = $db->sql_query('SELECT lang_id, lang_code FROM ' . table_prefix . "language;");
+	if ( !$q )
+		$db->_die();
+	
+	while ( $row = $db->fetchrow($q) )
+	{
+		if ( isset($languages[$row['lang_code']]) )
+		{
+			// found a language and it's good on the filesystem; load it and call a reimport
+			$lang_local = new Language($row['lang_id']);
+			// call fetch to make sure we're up to date
+			$lang_local->fetch();
+			// import
+			foreach ( array('core', 'admin', 'user', 'tools') as $language_file )
+			{
+				// generate full path
+				$language_file = ENANO_ROOT . "/language/{$languages[$row['lang_code']]['dir']}/$language_file.json";
+				// setting the second parameter to bool(true) causes it to skip existing strings
+				if ( !$lang_local->import($language_file, ( !$do_langimport )) )
+					// on failure, report failure to libenanoinstall
+					return false;
+			}
+			// unload this lang_local object to save memory
+			unset($lang_local);
+		}
+	}
+	
+	return true;
 }
 
 function stg_flush_cache()
 {
-  return purge_all_caches();
+	return purge_all_caches();
 }
 
 function stg_set_version()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  // log the upgrade
-  $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,page_text,edit_summary) VALUES'
-         . '(\'security\', \'upgrade_enano\', ' . time() . ', \'[DEPRECATED]\', \'' . $db->escape($session->username) . '\', ' . $session->user_id . ', \'' . $db->escape(installer_enano_version()) . '\', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\');');
-  if ( !$q )
-  {
-    $db->_die();
-    return false;
-  }
-  setConfig('enano_version', installer_enano_version());
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	// log the upgrade
+	$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,page_text,edit_summary) VALUES'
+ 				. '(\'security\', \'upgrade_enano\', ' . time() . ', \'[DEPRECATED]\', \'' . $db->escape($session->username) . '\', ' . $session->user_id . ', \'' . $db->escape(installer_enano_version()) . '\', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\');');
+	if ( !$q )
+	{
+		$db->_die();
+		return false;
+	}
+	setConfig('enano_version', installer_enano_version());
+	return true;
 }
--- a/install/includes/stages/confirm.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/confirm.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,39 +14,39 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 require_once( ENANO_ROOT . '/includes/constants.php' );
 
 $ui->show_header();
 ?>
-           <h3><?php echo $lang->get('confirm_title'); ?></h3>
-            <p><?php echo $lang->get('confirm_body'); ?></p>
-            <p style="font-size: smaller;"><b><?php echo $lang->get('confirm_info_aes_title'); ?></b>
-               <?php echo $lang->get('confirm_info_aes_body', array('aes_bits' => AES_BITS)); ?>
-               </p>
-            <?php
-            if ( $installer_version['type'] !== 'stable' )
-            {
-              echo '<div class="sysreqs_error">';
-              echo '<h3>' . $lang->get('confirm_msg_installing_unstable_title') . '</h3>';
-              echo '<p>' . $lang->get('confirm_msg_installing_unstable_body') . '</p>';
-              echo '</div>';
-            }
-            ?>
-            <form action="install.php?stage=install" method="post" name="install_login"><?php
-  foreach ( $_POST as $key => &$value )
-  {
-    if ( !preg_match('/^[a-z0-9_]+$/', $key) )
-      die('You idiot hacker...');
-    if ( $key == '_cont' )
-      continue;
-    $value_clean = str_replace(array('\\', '"', '<', '>'), array('\\\\', '\\"', '&lt;', '&gt;'), $value);
-    echo "\n              <input type=\"hidden\" name=\"$key\" value=\"$value_clean\" />";
-  }
+ 					<h3><?php echo $lang->get('confirm_title'); ?></h3>
+						<p><?php echo $lang->get('confirm_body'); ?></p>
+						<p style="font-size: smaller;"><b><?php echo $lang->get('confirm_info_aes_title'); ?></b>
+ 							<?php echo $lang->get('confirm_info_aes_body', array('aes_bits' => AES_BITS)); ?>
+ 							</p>
+						<?php
+						if ( $installer_version['type'] !== 'stable' )
+						{
+							echo '<div class="sysreqs_error">';
+							echo '<h3>' . $lang->get('confirm_msg_installing_unstable_title') . '</h3>';
+							echo '<p>' . $lang->get('confirm_msg_installing_unstable_body') . '</p>';
+							echo '</div>';
+						}
+						?>
+						<form action="install.php?stage=install" method="post" name="install_login"><?php
+	foreach ( $_POST as $key => &$value )
+	{
+		if ( !preg_match('/^[a-z0-9_]+$/', $key) )
+			die('You idiot hacker...');
+		if ( $key == '_cont' )
+			continue;
+		$value_clean = str_replace(array('\\', '"', '<', '>'), array('\\\\', '\\"', '&lt;', '&gt;'), $value);
+		echo "\n              <input type=\"hidden\" name=\"$key\" value=\"$value_clean\" />";
+	}
 ?>
 
-              <div style="text-align: center;">
-                <input type="submit" name="_cont" value="<?php echo $lang->get('confirm_btn_install_enano'); ?>" />
-              </div>
-            </form>
+							<div style="text-align: center;">
+								<input type="submit" name="_cont" value="<?php echo $lang->get('confirm_btn_install_enano'); ?>" />
+							</div>
+						</form>
--- a/install/includes/stages/database.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/database.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,13 +14,13 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 echo '<h3>' . $lang->get('database_driver_heading') . '</h3>';
 echo '<p>' . $lang->get('database_driver_intro') . '</p>';
 if ( @file_exists('/etc/enano-is-virt-appliance') )
 {
-  echo '<p>' . $lang->get('database_driver_msg_virt_appliance') . '</p>';
+	echo '<p>' . $lang->get('database_driver_msg_virt_appliance') . '</p>';
 }
 
 $mysql_disable_reason = '';
@@ -29,81 +29,81 @@
 $pgsql_disable = '';
 if ( !function_exists('mysql_connect') )
 {
-  $mysql_disable = ' disabled="disabled"';
-  $mysql_disable_reason = $lang->get('database_driver_err_no_mysql');
+	$mysql_disable = ' disabled="disabled"';
+	$mysql_disable_reason = $lang->get('database_driver_err_no_mysql');
 }
 if ( !function_exists('pg_connect') )
 {
-  $pgsql_disable = ' disabled="disabled"';
-  $pgsql_disable_reason = $lang->get('database_driver_err_no_pgsql');
+	$pgsql_disable = ' disabled="disabled"';
+	$pgsql_disable_reason = $lang->get('database_driver_err_no_pgsql');
 }
 
 echo '<form action="install.php?stage=database" method="post" enctype="multipart/form-data">';
 echo '<input type="hidden" name="language" value="' . $lang_id . '" />';
 ?>
 <table border="0" cellspacing="5">
-  <tr>
-    <td>
-      <?php 
-      if ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ):
-      ?>
-      <input type="radio" checked="checked" name="driver" value="mysql" <?php echo $mysql_disable; ?>/>
-      <?php
-      else:
-      ?>
-      <button name="driver" value="mysql"<?php echo $mysql_disable; ?>>
-        <img src="../images/about-powered-mysql.png" />
-      </button>
-      <?php
-      endif;
-      ?>
-    </td>
-    <td<?php if ( $mysql_disable ) echo ' style="opacity: 0.5; filter: alpha(opacity=50);"'; ?>>
-      <b><?php echo $lang->get('database_driver_mysql'); ?></b><br />
-      <?php echo $lang->get('database_driver_mysql_intro'); ?>
-      <?php
-      if ( $mysql_disable )
-      {
-        echo "<br /><br /><b>$mysql_disable_reason</b>";
-      }
-      ?>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <?php
-      if ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ):
-      ?>
-      <input type="radio" name="driver" value="mysql" <?php echo $pgsql_disable; ?>/>
-      <?php
-      else:
-      ?>
-      <button name="driver" value="postgresql"<?php echo $pgsql_disable; ?>>
-        <img src="../images/about-powered-pgsql.png" />
-      </button>
-      <?php
-      endif;
-      ?>
-    </td>
-    <td<?php if ( $pgsql_disable ) echo ' style="opacity: 0.5; filter: alpha(opacity=50);"'; ?>>
-      <b><?php echo $lang->get('database_driver_pgsql'); ?></b><br />
-      <?php echo $lang->get('database_driver_pgsql_intro'); ?>
-      <?php
-      if ( $pgsql_disable )
-      {
-        echo "<br /><br /><b>$pgsql_disable_reason</b>";
-      }
-      ?>
-    </td>
-  </tr>
+	<tr>
+		<td>
+			<?php 
+			if ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ):
+			?>
+			<input type="radio" checked="checked" name="driver" value="mysql" <?php echo $mysql_disable; ?>/>
+			<?php
+			else:
+			?>
+			<button name="driver" value="mysql"<?php echo $mysql_disable; ?>>
+				<img src="../images/about-powered-mysql.png" />
+			</button>
+			<?php
+			endif;
+			?>
+		</td>
+		<td<?php if ( $mysql_disable ) echo ' style="opacity: 0.5; filter: alpha(opacity=50);"'; ?>>
+			<b><?php echo $lang->get('database_driver_mysql'); ?></b><br />
+			<?php echo $lang->get('database_driver_mysql_intro'); ?>
+			<?php
+			if ( $mysql_disable )
+			{
+				echo "<br /><br /><b>$mysql_disable_reason</b>";
+			}
+			?>
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<?php
+			if ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') ):
+			?>
+			<input type="radio" name="driver" value="mysql" <?php echo $pgsql_disable; ?>/>
+			<?php
+			else:
+			?>
+			<button name="driver" value="postgresql"<?php echo $pgsql_disable; ?>>
+				<img src="../images/about-powered-pgsql.png" />
+			</button>
+			<?php
+			endif;
+			?>
+		</td>
+		<td<?php if ( $pgsql_disable ) echo ' style="opacity: 0.5; filter: alpha(opacity=50);"'; ?>>
+			<b><?php echo $lang->get('database_driver_pgsql'); ?></b><br />
+			<?php echo $lang->get('database_driver_pgsql_intro'); ?>
+			<?php
+			if ( $pgsql_disable )
+			{
+				echo "<br /><br /><b>$pgsql_disable_reason</b>";
+			}
+			?>
+		</td>
+	</tr>
 </table>
 
 <?php
 if ( strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') )
 {
-  echo '<div style="text-align: center;">
-          <input type="submit" />
-        </div>';
+	echo '<div style="text-align: center;">
+					<input type="submit" />
+				</div>';
 }
 
 echo '</form>';
--- a/install/includes/stages/database_mysql.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/database_mysql.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,203 +14,203 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 if ( isset($_POST['_cont']) )
 {
-  $allow_go = true;
-  // Do we have everything? If so, continue with installation.
-  foreach ( array('db_host', 'db_port', 'db_name', 'db_user', 'db_pass') as $field )
-  {
-    if ( empty($_POST[$field]) )
-    {
-      $allow_go = false;
-    }
-  }
-  if ( $allow_go )
-  {
-    require( ENANO_ROOT . '/install/includes/stages/database_post.php' );
-    return true;
-  }
+	$allow_go = true;
+	// Do we have everything? If so, continue with installation.
+	foreach ( array('db_host', 'db_port', 'db_name', 'db_user', 'db_pass') as $field )
+	{
+		if ( empty($_POST[$field]) )
+		{
+			$allow_go = false;
+		}
+	}
+	if ( $allow_go )
+	{
+		require( ENANO_ROOT . '/install/includes/stages/database_post.php' );
+		return true;
+	}
 }
 
 if ( isset($_POST['ajax_test']) )
 {
-  // Test the database connection
-  $return = array(
-      'can_install' => false,
-      'host_good' => true,
-      'creating_user' => false,
-      'db_exist' => false,
-      'creating_db' => false,
-      'creating_db_grant' => false,
-      'root_fail' => false,
-      'version' => array(
-        'version' => 'unknown',
-        'good' => 'indeterminate'
-      ),
-      'last_error' => ''
-    );
-  
-  if ( !isset($_POST['info']) )
-    die();
-  
-  $info = $_POST['info'];
-  
-  // From here on out will be JSON responses
-  header('Content-type: application/json');
-  
-  try
-  {
-    $info = @enano_json_decode($info);
-  }
-  catch ( Zend_Json_Exception $e )
-  {
-    die(enano_json_encode(array(
-        'mode' => 'error',
-        'error' => 'Exception in JSON decoder'
-      )));
-  }
-  
-  if ( preg_match('/^:/', $info['db_host']) && !@file_exists(substr($info['db_host'], 1)) )
-  {
-    $return['host_good'] = false;
-    echo enano_json_encode($return);
-    exit();
-  }
-  
-  if ( $info['db_host'] == 'localhost' && !empty($info['db_port']) && $info['db_port'] != 3306 )
-    $info['db_host'] = '127.0.0.1';
-  
-  $dbhost = ( preg_match('/^:/', $info['db_host']) ) ? $info['db_host'] : "{$info['db_host']}:{$info['db_port']}";
-  
-  // Try to connect as the normal user
-  $test = @mysql_connect($dbhost, $info['db_user'], $info['db_pass']);
-  if ( !$test )
-  {
-    $return['creating_user'] = true;
-    $return['last_error'] = mysql_error();
-    if ( strstr( $return['last_error'], 'Lost connection' ) || strstr( $return['last_error'], 'Unknown MySQL server host' ) )
-    {
-      $return['host_good'] = false;
-    }
-    // Doing that failed. If we have root credentials, test those
-    if ( !empty($info['db_root_user']) && !empty($info['db_root_pass']) )
-    {
-      // Log in with root rights and if that works, tell 'em we'll reset the password or create
-      // the account if it doesn't exist already. This is done with GRANT ALL PRIVILEGES ON enano_db.*
-      // etc etc, a little hackish but known to work with MySQL >= 4.1.
-      $test_root = @mysql_connect($dbhost, $info['db_root_user'], $info['db_root_pass']);
-      if ( $test_root )
-      {
-        // We logged in with root rights, assume that we have appropriate permissions.
-        // If not, well, the installation will fail. Tough on the user, but creating
-        // test databases/users is too risky.
-        
-        // Does the database exist?
-        $q = @mysql_query('USE `' . mysql_real_escape_string($info['db_name']) . '`;', $test_root);
-        if ( !$q )
-        {
-          // Nope, we'll have to create it
-          $return['creating_db'] = true;
-          $return['last_error'] = mysql_error();
-        }
-        
-        $version = mysql_get_server_info($test_root);
-        $return['version'] = array(
-          'version' => $version,
-          'good' => version_compare($version, '4.0.17', '>=')
-        );
-        
-        $return['can_install'] = ( $return['version']['good'] ) ? true : false;
-      }
-      else
-      {
-        // Well that helped. Root credentials are bad.
-        $return['creating_db'] = true;
-        $return['root_fail'] = true;
-      }
-    }
-    else
-    {
-      // No root credentials, fail out
-      $return['root_fail'] = true;
-    }
-  }
-  else
-  {
-    // We're connected; do we have permission to use the database?
-    $have_database = false;
-    $q = @mysql_query('USE `' . mysql_real_escape_string($info['db_name']) . '`;', $test);
-    if ( $q )
-    {
-      // Permissions are good and we're all connected. Perform version check...
-      $version = mysql_get_server_info($test);
-      $return['version'] = array(
-        'version' => $version,
-        'good' => version_compare($version, '4.0.17', '>=')
-      );
-      
-      $return['can_install'] = ( $return['version']['good'] ) ? true : false;
-    }
-    else
-    {
-      $return['last_error'] = mysql_error();
-      $return['creating_db'] = true;
-      
-      // We don't have permission to use the database or it doesn't exist.
-      // See if we have a root login to work with, if not then fail
-      if ( !empty($info['db_root_user']) && !empty($info['db_root_pass']) )
-      {
-        // Log in with root rights and if that works, tell 'em we'll create the database.
-        $test_root = @mysql_connect($dbhost, $info['db_root_user'], $info['db_root_pass']);
-        if ( $test_root )
-        {
-          // We logged in with root rights, assume that we have appropriate permissions.
-          // If not, well, the installation will fail. Tough on the user, but creating
-          // test databases/users is too risky.
-          
-          // See if the database already exists
-          $dbname = mysql_real_escape_string($info['db_name']);
-          $q = @mysql_query("SHOW DATABASES LIKE '$dbname';", $test_root);
-          if ( $q )
-          {
-            if ( mysql_num_rows($q) > 0 )
-            {
-              $return['creating_db'] = false;
-              $return['creating_db_grant'] = true;
-            }
-            @mysql_free_result($q);
-          }
-          
-          $version = mysql_get_server_info($test);
-          $return['version'] = array(
-            'version' => $version,
-            'good' => version_compare($version, '4.0.17', '>=')
-          );
-          
-          $return['can_install'] = ( $return['version']['good'] ) ? true : false;
-        }
-        else
-        {
-          // Well that helped. Root credentials are bad.
-          $return['creating_db'] = true;
-          $return['root_fail'] = true;
-        }
-      }
-      // No root credentials, fail out
-    }
-  }
-  
-  if ( isset($test) && @is_resource($test) )
-    @mysql_close($test);
-  
-  if ( isset($test_root) && @is_resource($test_root) )
-    @mysql_close($test_root);
-  
-  echo enano_json_encode($return);
-  
-  exit();
+	// Test the database connection
+	$return = array(
+			'can_install' => false,
+			'host_good' => true,
+			'creating_user' => false,
+			'db_exist' => false,
+			'creating_db' => false,
+			'creating_db_grant' => false,
+			'root_fail' => false,
+			'version' => array(
+				'version' => 'unknown',
+				'good' => 'indeterminate'
+			),
+			'last_error' => ''
+		);
+	
+	if ( !isset($_POST['info']) )
+		die();
+	
+	$info = $_POST['info'];
+	
+	// From here on out will be JSON responses
+	header('Content-type: application/json');
+	
+	try
+	{
+		$info = @enano_json_decode($info);
+	}
+	catch ( Zend_Json_Exception $e )
+	{
+		die(enano_json_encode(array(
+				'mode' => 'error',
+				'error' => 'Exception in JSON decoder'
+			)));
+	}
+	
+	if ( preg_match('/^:/', $info['db_host']) && !@file_exists(substr($info['db_host'], 1)) )
+	{
+		$return['host_good'] = false;
+		echo enano_json_encode($return);
+		exit();
+	}
+	
+	if ( $info['db_host'] == 'localhost' && !empty($info['db_port']) && $info['db_port'] != 3306 )
+		$info['db_host'] = '127.0.0.1';
+	
+	$dbhost = ( preg_match('/^:/', $info['db_host']) ) ? $info['db_host'] : "{$info['db_host']}:{$info['db_port']}";
+	
+	// Try to connect as the normal user
+	$test = @mysql_connect($dbhost, $info['db_user'], $info['db_pass']);
+	if ( !$test )
+	{
+		$return['creating_user'] = true;
+		$return['last_error'] = mysql_error();
+		if ( strstr( $return['last_error'], 'Lost connection' ) || strstr( $return['last_error'], 'Unknown MySQL server host' ) )
+		{
+			$return['host_good'] = false;
+		}
+		// Doing that failed. If we have root credentials, test those
+		if ( !empty($info['db_root_user']) && !empty($info['db_root_pass']) )
+		{
+			// Log in with root rights and if that works, tell 'em we'll reset the password or create
+			// the account if it doesn't exist already. This is done with GRANT ALL PRIVILEGES ON enano_db.*
+			// etc etc, a little hackish but known to work with MySQL >= 4.1.
+			$test_root = @mysql_connect($dbhost, $info['db_root_user'], $info['db_root_pass']);
+			if ( $test_root )
+			{
+				// We logged in with root rights, assume that we have appropriate permissions.
+				// If not, well, the installation will fail. Tough on the user, but creating
+				// test databases/users is too risky.
+				
+				// Does the database exist?
+				$q = @mysql_query('USE `' . mysql_real_escape_string($info['db_name']) . '`;', $test_root);
+				if ( !$q )
+				{
+					// Nope, we'll have to create it
+					$return['creating_db'] = true;
+					$return['last_error'] = mysql_error();
+				}
+				
+				$version = mysql_get_server_info($test_root);
+				$return['version'] = array(
+					'version' => $version,
+					'good' => version_compare($version, '4.0.17', '>=')
+				);
+				
+				$return['can_install'] = ( $return['version']['good'] ) ? true : false;
+			}
+			else
+			{
+				// Well that helped. Root credentials are bad.
+				$return['creating_db'] = true;
+				$return['root_fail'] = true;
+			}
+		}
+		else
+		{
+			// No root credentials, fail out
+			$return['root_fail'] = true;
+		}
+	}
+	else
+	{
+		// We're connected; do we have permission to use the database?
+		$have_database = false;
+		$q = @mysql_query('USE `' . mysql_real_escape_string($info['db_name']) . '`;', $test);
+		if ( $q )
+		{
+			// Permissions are good and we're all connected. Perform version check...
+			$version = mysql_get_server_info($test);
+			$return['version'] = array(
+				'version' => $version,
+				'good' => version_compare($version, '4.0.17', '>=')
+			);
+			
+			$return['can_install'] = ( $return['version']['good'] ) ? true : false;
+		}
+		else
+		{
+			$return['last_error'] = mysql_error();
+			$return['creating_db'] = true;
+			
+			// We don't have permission to use the database or it doesn't exist.
+			// See if we have a root login to work with, if not then fail
+			if ( !empty($info['db_root_user']) && !empty($info['db_root_pass']) )
+			{
+				// Log in with root rights and if that works, tell 'em we'll create the database.
+				$test_root = @mysql_connect($dbhost, $info['db_root_user'], $info['db_root_pass']);
+				if ( $test_root )
+				{
+					// We logged in with root rights, assume that we have appropriate permissions.
+					// If not, well, the installation will fail. Tough on the user, but creating
+					// test databases/users is too risky.
+					
+					// See if the database already exists
+					$dbname = mysql_real_escape_string($info['db_name']);
+					$q = @mysql_query("SHOW DATABASES LIKE '$dbname';", $test_root);
+					if ( $q )
+					{
+						if ( mysql_num_rows($q) > 0 )
+						{
+							$return['creating_db'] = false;
+							$return['creating_db_grant'] = true;
+						}
+						@mysql_free_result($q);
+					}
+					
+					$version = mysql_get_server_info($test);
+					$return['version'] = array(
+						'version' => $version,
+						'good' => version_compare($version, '4.0.17', '>=')
+					);
+					
+					$return['can_install'] = ( $return['version']['good'] ) ? true : false;
+				}
+				else
+				{
+					// Well that helped. Root credentials are bad.
+					$return['creating_db'] = true;
+					$return['root_fail'] = true;
+				}
+			}
+			// No root credentials, fail out
+		}
+	}
+	
+	if ( isset($test) && @is_resource($test) )
+		@mysql_close($test);
+	
+	if ( isset($test_root) && @is_resource($test_root) )
+		@mysql_close($test_root);
+	
+	echo enano_json_encode($return);
+	
+	exit();
 }
 
 $ui->add_header('<script type="text/javascript" src="includes/js/formutils.js"></script>');
@@ -219,7 +219,7 @@
 ?>
 
 <div style="float: right; padding: 10px 0 10px 10px;">
-  <img alt="MySQL logo" src="../images/about-powered-mysql.png" />
+	<img alt="MySQL logo" src="../images/about-powered-mysql.png" />
 </div>
 
 <p><?php echo $lang->get('dbmysql_blurb_needdb'); ?></p>
@@ -227,173 +227,173 @@
 <?php
 if ( @file_exists('/etc/enano-is-virt-appliance') )
 {
-  echo '<p>
-          ' . $lang->get('database_vm_login_info', array( 'host' => 'localhost', 'user' => 'enano', 'pass' => 'clurichaun', 'name' => 'enano_www1' )) . '
-        </p>';
+	echo '<p>
+					' . $lang->get('database_vm_login_info', array( 'host' => 'localhost', 'user' => 'enano', 'pass' => 'clurichaun', 'name' => 'enano_www1' )) . '
+				</p>';
 }
 ?>
 
 <script type="text/javascript">
 
-  var tested = false;
+	var tested = false;
 
-  function verify(field)
-  {
-    if ( tested && !field )
-      return true;
-    tested = false;
-    if ( document.getElementById('verify_error').className != '' )
-    {
-      document.getElementById('verify_error').className = '';
-      document.getElementById('verify_error').innerHTML = '';
-    }
-    var frm = document.forms.database_info;
-    // List of fields
-    var fields = {
-      db_host: frm.db_host,
-      db_port: frm.db_port,
-      db_name: frm.db_name,
-      db_user: frm.db_user,
-      db_pass: frm.db_pass,
-      table_prefix: frm.table_prefix,
-      db_root_user: frm.db_root_user,
-      db_root_pass: frm.db_root_pass
-    };
-    var passed = true;
-    // Main validation
-    if ( field == fields.db_host || !field )
-    {
-      var matches = fields.db_host.value.match(/^(([a-z0-9_-]+)((\.([a-z0-9_-]+))*)|:[A-z0-9_:\.\/-]+)$/);
-      document.getElementById('s_db_host').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.db_port || !field )
-    {
-      var matches = fields.db_port.value.match(/^[0-9]+$/);
-      document.getElementById('s_db_port').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.db_name || !field )
-    {
-      var matches = fields.db_name.value.match(/^[A-z0-9_-]+$/);
-      document.getElementById('s_db_name').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.db_user || field == fields.db_pass || !field )
-    {
-      var matches = fields.db_user.value.match(/^[A-z0-9_-]+$/);
-      document.getElementById('s_db_auth').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.table_prefix || !field )
-    {
-      var matches = fields.table_prefix.value.match(/^[a-z0-9_]*$/);
-      document.getElementById('s_table_prefix').src = ( matches ) ? img_good : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.db_root_user || field == fields.db_root_pass || !field )
-    {
-      var matches = ( ( fields.db_root_user.value.match(/^[A-z0-9_-]+$/) && fields.db_root_pass.value.match(/^.+$/) ) || fields.db_root_user.value == '' );
-      document.getElementById('s_db_root').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    return passed;
-  }
-  
-  function ajaxTestConnection()
-  {
-    if ( !verify() )
-    {
-      document.body.scrollTop = 0;
-      $('enano-body').effect('shake', {}, 750);
-      document.getElementById('verify_error').className = 'error-box-mini';
-      document.getElementById('verify_error').innerHTML = $lang.get('meta_msg_err_verification');
-      return false;
-    }
-    install_set_ajax_loading();
-    
-    var frm = document.forms.database_info;
-    var connection_info = 'info=' + ajaxEscape(toJSONString({
-        db_host: frm.db_host.value,
-        db_port: frm.db_port.value,
-        db_name: frm.db_name.value,
-        db_user: frm.db_user.value,
-        db_pass: frm.db_pass.value,
-        db_root_user: frm.db_root_user.value,
-        db_root_pass: frm.db_root_pass.value
-      }));
-    
-    ajaxPost(scriptPath + '/install/install.php?stage=database', connection_info + '&driver=mysql&ajax_test=on&language=' + enano_lang_code[ENANO_LANG_ID], function(ajax)
-      {
-        if ( ajax.readyState == 4 && ajax.status == 200 )
-        {
-          setTimeout('install_unset_ajax_loading();', 750);
-          // Process response
-          var response = String(ajax.responseText + '');
-          if ( response.substr(0, 1) != '{' )
-          {
-            alert('Received an invalid JSON response from the server.');
-            return false;
-          }
-          response = parseJSON(response);
-          if ( response.mode == 'error' )
-          {
-            return false;
-          }
-          document.getElementById('e_db_host').innerHTML = '';
-          document.getElementById('e_db_name').innerHTML = '';
-          document.getElementById('e_db_auth').innerHTML = '';
-          document.getElementById('e_db_root').innerHTML = '';
-          if ( response.can_install )
-          {
-            tested = true;
-            var statuses = ['s_db_host', 's_db_name', 's_db_auth', 's_table_prefix', 's_db_root', 's_mysql_version'];
-            for ( var i in statuses )
-            {
-              var img = document.getElementById(statuses[i]);
-              if ( img )
-                img.src = img_good;
-            }
-            document.getElementById('e_mysql_version').innerHTML = $lang.get('dbmysql_msg_info_mysql_good');
-            document.getElementById('verify_error').className = 'info-box-mini';
-            document.getElementById('verify_error').innerHTML = $lang.get('dbmysql_msg_test_success');
-            if ( response.creating_db )
-            {
-              document.getElementById('e_db_name').innerHTML = $lang.get('dbmysql_msg_warn_creating_db');
-            }
-            if ( response.creating_user )
-            {
-              document.getElementById('e_db_auth').innerHTML = $lang.get('dbmysql_msg_warn_creating_user');
-            }
-          }
-          else
-          {
-            // Oh dear, oh dear, oh dear, oh dear, oh dear...
-            if ( response.creating_db )
-            {
-              document.getElementById('e_db_name').innerHTML = $lang.get('dbmysql_msg_err_mysql_dbexist', { mysql_error: response.last_error });
-              document.getElementById('s_db_name').src = img_bad;
-            }
-            if ( response.creating_user )
-            {
-              document.getElementById('e_db_auth').innerHTML = $lang.get('dbmysql_msg_err_mysql_auth', { mysql_error: response.last_error });
-              document.getElementById('s_db_auth').src = img_bad;
-            }
-            if ( !response.host_good )
-            {
-              document.getElementById('e_db_host').innerHTML = $lang.get('dbmysql_msg_err_mysql_connect', { db_host: frm.db_host.value, mysql_error: response.last_error });
-              document.getElementById('s_db_host').src = img_bad;
-            }
-          }
-        }
-      });
-  }
+	function verify(field)
+	{
+		if ( tested && !field )
+			return true;
+		tested = false;
+		if ( document.getElementById('verify_error').className != '' )
+		{
+			document.getElementById('verify_error').className = '';
+			document.getElementById('verify_error').innerHTML = '';
+		}
+		var frm = document.forms.database_info;
+		// List of fields
+		var fields = {
+			db_host: frm.db_host,
+			db_port: frm.db_port,
+			db_name: frm.db_name,
+			db_user: frm.db_user,
+			db_pass: frm.db_pass,
+			table_prefix: frm.table_prefix,
+			db_root_user: frm.db_root_user,
+			db_root_pass: frm.db_root_pass
+		};
+		var passed = true;
+		// Main validation
+		if ( field == fields.db_host || !field )
+		{
+			var matches = fields.db_host.value.match(/^(([a-z0-9_-]+)((\.([a-z0-9_-]+))*)|:[A-z0-9_:\.\/-]+)$/);
+			document.getElementById('s_db_host').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.db_port || !field )
+		{
+			var matches = fields.db_port.value.match(/^[0-9]+$/);
+			document.getElementById('s_db_port').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.db_name || !field )
+		{
+			var matches = fields.db_name.value.match(/^[A-z0-9_-]+$/);
+			document.getElementById('s_db_name').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.db_user || field == fields.db_pass || !field )
+		{
+			var matches = fields.db_user.value.match(/^[A-z0-9_-]+$/);
+			document.getElementById('s_db_auth').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.table_prefix || !field )
+		{
+			var matches = fields.table_prefix.value.match(/^[a-z0-9_]*$/);
+			document.getElementById('s_table_prefix').src = ( matches ) ? img_good : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.db_root_user || field == fields.db_root_pass || !field )
+		{
+			var matches = ( ( fields.db_root_user.value.match(/^[A-z0-9_-]+$/) && fields.db_root_pass.value.match(/^.+$/) ) || fields.db_root_user.value == '' );
+			document.getElementById('s_db_root').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		return passed;
+	}
+	
+	function ajaxTestConnection()
+	{
+		if ( !verify() )
+		{
+			document.body.scrollTop = 0;
+			$('enano-body').effect('shake', {}, 750);
+			document.getElementById('verify_error').className = 'error-box-mini';
+			document.getElementById('verify_error').innerHTML = $lang.get('meta_msg_err_verification');
+			return false;
+		}
+		install_set_ajax_loading();
+		
+		var frm = document.forms.database_info;
+		var connection_info = 'info=' + ajaxEscape(toJSONString({
+				db_host: frm.db_host.value,
+				db_port: frm.db_port.value,
+				db_name: frm.db_name.value,
+				db_user: frm.db_user.value,
+				db_pass: frm.db_pass.value,
+				db_root_user: frm.db_root_user.value,
+				db_root_pass: frm.db_root_pass.value
+			}));
+		
+		ajaxPost(scriptPath + '/install/install.php?stage=database', connection_info + '&driver=mysql&ajax_test=on&language=' + enano_lang_code[ENANO_LANG_ID], function(ajax)
+			{
+				if ( ajax.readyState == 4 && ajax.status == 200 )
+				{
+					setTimeout('install_unset_ajax_loading();', 750);
+					// Process response
+					var response = String(ajax.responseText + '');
+					if ( response.substr(0, 1) != '{' )
+					{
+						alert('Received an invalid JSON response from the server.');
+						return false;
+					}
+					response = parseJSON(response);
+					if ( response.mode == 'error' )
+					{
+						return false;
+					}
+					document.getElementById('e_db_host').innerHTML = '';
+					document.getElementById('e_db_name').innerHTML = '';
+					document.getElementById('e_db_auth').innerHTML = '';
+					document.getElementById('e_db_root').innerHTML = '';
+					if ( response.can_install )
+					{
+						tested = true;
+						var statuses = ['s_db_host', 's_db_name', 's_db_auth', 's_table_prefix', 's_db_root', 's_mysql_version'];
+						for ( var i in statuses )
+						{
+							var img = document.getElementById(statuses[i]);
+							if ( img )
+								img.src = img_good;
+						}
+						document.getElementById('e_mysql_version').innerHTML = $lang.get('dbmysql_msg_info_mysql_good');
+						document.getElementById('verify_error').className = 'info-box-mini';
+						document.getElementById('verify_error').innerHTML = $lang.get('dbmysql_msg_test_success');
+						if ( response.creating_db )
+						{
+							document.getElementById('e_db_name').innerHTML = $lang.get('dbmysql_msg_warn_creating_db');
+						}
+						if ( response.creating_user )
+						{
+							document.getElementById('e_db_auth').innerHTML = $lang.get('dbmysql_msg_warn_creating_user');
+						}
+					}
+					else
+					{
+						// Oh dear, oh dear, oh dear, oh dear, oh dear...
+						if ( response.creating_db )
+						{
+							document.getElementById('e_db_name').innerHTML = $lang.get('dbmysql_msg_err_mysql_dbexist', { mysql_error: response.last_error });
+							document.getElementById('s_db_name').src = img_bad;
+						}
+						if ( response.creating_user )
+						{
+							document.getElementById('e_db_auth').innerHTML = $lang.get('dbmysql_msg_err_mysql_auth', { mysql_error: response.last_error });
+							document.getElementById('s_db_auth').src = img_bad;
+						}
+						if ( !response.host_good )
+						{
+							document.getElementById('e_db_host').innerHTML = $lang.get('dbmysql_msg_err_mysql_connect', { db_host: frm.db_host.value, mysql_error: response.last_error });
+							document.getElementById('s_db_host').src = img_bad;
+						}
+					}
+				}
+			});
+	}
 
 </script>
 
@@ -402,144 +402,144 @@
 <input type="hidden" name="driver" value="mysql" />
 
 <table border="0" cellspacing="0" cellpadding="10" width="100%">
-  <tr>
-    <td colspan="3" style="text-align: center">
-      <h3><?php echo $lang->get('dbmysql_table_title'); ?></h3>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbmysql_field_hostname_title'); ?></b>
-      <br /><?php echo $lang->get('dbmysql_field_hostname_body'); ?>
-      <br /><span style="color: #993300" id="e_db_host"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="1" name="db_host" size="30" type="text" />
-    </td>
-    <td>
-      <img id="s_db_host" alt="Good/bad icon" src="../images/checkbad.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbmysql_field_port_title'); ?></b>
-      <br /><?php echo $lang->get('dbmysql_field_port_body'); ?>
-      <br /><span style="color: #993300" id="e_db_port"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="2" name="db_port" size="5" type="text" value="3306" />
-    </td>
-    <td>
-      <img id="s_db_port" alt="Good/bad icon" src="../images/checkbad.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbmysql_field_dbname_title'); ?></b><br />
-      <?php echo $lang->get('dbmysql_field_dbname_body'); ?><br />
-      <span style="color: #993300" id="e_db_name"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="3" name="db_name" size="30" type="text" />
-    </td>
-    <td>
-      <img id="s_db_name" alt="Good/bad icon" src="../images/checkbad.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbmysql_field_dbauth_title'); ?></b><br />
-      <?php echo $lang->get('dbmysql_field_dbauth_body'); ?><br />
-      <span style="color: #993300" id="e_db_auth"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="4" name="db_user" size="30" type="text" /><br />
-      <br />
-      <input name="db_pass" tabindex="5" size="30" type="password" />
-    </td>
-    <td>
-      <img id="s_db_auth" alt="Good/bad icon" src="../images/checkbad.png" />
-    </td>
-  </tr>
-  <tr>
-    <td colspan="3" style="text-align: center">
-      <h3><?php echo $lang->get('database_heading_optionalinfo'); ?></h3>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbmysql_field_tableprefix_title'); ?></b><br />
-      <?php echo $lang->get('dbmysql_field_tableprefix_body'); ?>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="6" name="table_prefix" size="30" type="text" />
-    </td>
-    <td>
-      <img id="s_table_prefix" alt="Good/bad icon" src="../images/check.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbmysql_field_rootauth_title'); ?></b><br />
-      <?php echo $lang->get('dbmysql_field_rootauth_body'); ?><br />
-      <span style="color: #993300" id="e_db_root"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="7" name="db_root_user" size="30" type="text" /><br />
-      <br />
-      <input onkeyup="verify(this);" tabindex="8" name="db_root_pass" size="30" type="password" />
-    </td>
-    <td>
-      <img id="s_db_root" alt="Good/bad icon" src="../images/check.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbmysql_field_mysqlversion_title'); ?></b>
-    </td>
-    <td id="e_mysql_version">
-      <?php echo $lang->get('dbmysql_field_mysqlversion_blurb_willbechecked'); ?>
-    </td>
-    <td>
-      <img id="s_mysql_version" alt="Good/bad icon" src="../images/checkunk.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbmysql_field_droptables_title'); ?></b><br />
-      <?php echo $lang->get('dbmysql_field_droptables_body'); ?>
-    </td>
-    <td colspan="2">
-      <input type="checkbox" tabindex="9" name="drop_tables" id="dtcheck" />  <label for="dtcheck"><?php echo $lang->get('dbmysql_field_droptables_lbl'); ?></label>
-    </td>
-  </tr>
-  <tr>
-    <td colspan="3" style="text-align: center">
-      <input type="button" tabindex="10" value="<?php echo $lang->get('dbmysql_btn_testconnection'); ?>" onclick="ajaxTestConnection();" />
-      <div id="verify_error"></div>
-    </td>
-  </tr>
+	<tr>
+		<td colspan="3" style="text-align: center">
+			<h3><?php echo $lang->get('dbmysql_table_title'); ?></h3>
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbmysql_field_hostname_title'); ?></b>
+			<br /><?php echo $lang->get('dbmysql_field_hostname_body'); ?>
+			<br /><span style="color: #993300" id="e_db_host"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="1" name="db_host" size="30" type="text" />
+		</td>
+		<td>
+			<img id="s_db_host" alt="Good/bad icon" src="../images/checkbad.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbmysql_field_port_title'); ?></b>
+			<br /><?php echo $lang->get('dbmysql_field_port_body'); ?>
+			<br /><span style="color: #993300" id="e_db_port"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="2" name="db_port" size="5" type="text" value="3306" />
+		</td>
+		<td>
+			<img id="s_db_port" alt="Good/bad icon" src="../images/checkbad.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbmysql_field_dbname_title'); ?></b><br />
+			<?php echo $lang->get('dbmysql_field_dbname_body'); ?><br />
+			<span style="color: #993300" id="e_db_name"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="3" name="db_name" size="30" type="text" />
+		</td>
+		<td>
+			<img id="s_db_name" alt="Good/bad icon" src="../images/checkbad.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbmysql_field_dbauth_title'); ?></b><br />
+			<?php echo $lang->get('dbmysql_field_dbauth_body'); ?><br />
+			<span style="color: #993300" id="e_db_auth"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="4" name="db_user" size="30" type="text" /><br />
+			<br />
+			<input name="db_pass" tabindex="5" size="30" type="password" />
+		</td>
+		<td>
+			<img id="s_db_auth" alt="Good/bad icon" src="../images/checkbad.png" />
+		</td>
+	</tr>
+	<tr>
+		<td colspan="3" style="text-align: center">
+			<h3><?php echo $lang->get('database_heading_optionalinfo'); ?></h3>
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbmysql_field_tableprefix_title'); ?></b><br />
+			<?php echo $lang->get('dbmysql_field_tableprefix_body'); ?>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="6" name="table_prefix" size="30" type="text" />
+		</td>
+		<td>
+			<img id="s_table_prefix" alt="Good/bad icon" src="../images/check.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbmysql_field_rootauth_title'); ?></b><br />
+			<?php echo $lang->get('dbmysql_field_rootauth_body'); ?><br />
+			<span style="color: #993300" id="e_db_root"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="7" name="db_root_user" size="30" type="text" /><br />
+			<br />
+			<input onkeyup="verify(this);" tabindex="8" name="db_root_pass" size="30" type="password" />
+		</td>
+		<td>
+			<img id="s_db_root" alt="Good/bad icon" src="../images/check.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbmysql_field_mysqlversion_title'); ?></b>
+		</td>
+		<td id="e_mysql_version">
+			<?php echo $lang->get('dbmysql_field_mysqlversion_blurb_willbechecked'); ?>
+		</td>
+		<td>
+			<img id="s_mysql_version" alt="Good/bad icon" src="../images/checkunk.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbmysql_field_droptables_title'); ?></b><br />
+			<?php echo $lang->get('dbmysql_field_droptables_body'); ?>
+		</td>
+		<td colspan="2">
+			<input type="checkbox" tabindex="9" name="drop_tables" id="dtcheck" />  <label for="dtcheck"><?php echo $lang->get('dbmysql_field_droptables_lbl'); ?></label>
+		</td>
+	</tr>
+	<tr>
+		<td colspan="3" style="text-align: center">
+			<input type="button" tabindex="10" value="<?php echo $lang->get('dbmysql_btn_testconnection'); ?>" onclick="ajaxTestConnection();" />
+			<div id="verify_error"></div>
+		</td>
+	</tr>
 
 </table>
 
 <table border="0">
-  <tr>
-    <td>
-      <input type="submit" tabindex="11" value="<?php echo $lang->get('meta_btn_continue'); ?>" onclick="return verify();" name="_cont" />
-    </td>
-    <td>
-      <p>
-        <span style="font-weight: bold;"><?php echo $lang->get('meta_lbl_before_continue'); ?></span><br />
-        &bull; <?php echo $lang->get('database_objective_test'); ?><br />
-        &bull; <?php echo $lang->get('database_objective_uncrypt'); ?>
-      </p>
-    </td>
-  </tr>
+	<tr>
+		<td>
+			<input type="submit" tabindex="11" value="<?php echo $lang->get('meta_btn_continue'); ?>" onclick="return verify();" name="_cont" />
+		</td>
+		<td>
+			<p>
+				<span style="font-weight: bold;"><?php echo $lang->get('meta_lbl_before_continue'); ?></span><br />
+				&bull; <?php echo $lang->get('database_objective_test'); ?><br />
+				&bull; <?php echo $lang->get('database_objective_uncrypt'); ?>
+			</p>
+		</td>
+	</tr>
 </table>
 
 </form>
 
 <script type="text/javascript">
-  verify();
+	verify();
 </script>
 
--- a/install/includes/stages/database_post.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/database_post.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,7 +14,7 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 // Start up the DBAL
 require( ENANO_ROOT . '/includes/dbal.php' );
@@ -31,9 +31,9 @@
 
 if ( !preg_match('/^[a-z0-9_]*$/', $db_prefix) )
 {
-  $ui->show_header();
-  echo '<p>That table prefix isn\'t going to work.</p>';
-  return true;
+	$ui->show_header();
+	echo '<p>That table prefix isn\'t going to work.</p>';
+	return true;
 }
 
 $result = $dbal->connect(true, $db_host, $db_user, $db_pass, $db_name, $db_port);
@@ -41,83 +41,83 @@
 // If connection failed, we have the root login, AND we're on MySQL, try to force our way in
 if ( !$result && !empty($_POST['db_root_user']) && !empty($_POST['db_root_pass']) && $driver == 'mysql' )
 {
-  // Allow a jump / breakout
-  switch ( 'foo' ) { case 'foo':
-      
-    // Try to connect to the DB as root
-    $result_root = $dbal->connect(true, $db_host, $db_root_user, $db_root_pass, 'mysql', $db_port);
-    if ( !$result_root )
-      break;
-    
-    $q = $dbal->sql_query('CREATE DATABASE IF NOT EXISTS `' . $dbal->escape($db_name) . '`;');
-    if ( !$q )
-      break;
-    
-    if ( $db_host == 'localhost' || $db_host == '127.0.0.1' )
-    {
-      $q = $dbal->sql_query('GRANT ALL PRIVILEGES ON `' . $dbal->escape($db_name) . '`.* TO \'' . $dbal->escape($db_user) . '\'@\'localhost\'' . "\n" .
-                            '  IDENTIFIED BY \'' . $dbal->escape($db_pass) . '\' WITH GRANT OPTION');
-    }
-    else
-    {
-      $q = $dbal->sql_query('GRANT ALL PRIVILEGES ON `' . $dbal->escape($db_name) . '`.* TO \'' . $dbal->escape($db_user) . '\'@\'%\'' . "\n" .
-                            '  IDENTIFIED BY \'' . $dbal->escape($db_pass) . '\' WITH GRANT OPTION');
-    }
-    
-    if ( !$q )
-      break;
-    
-    $dbal->close();
-    $result = $dbal->connect(true, $db_host, $db_user, $db_pass, $db_name, $db_port);
-      
-    break;
-  }
+	// Allow a jump / breakout
+	switch ( 'foo' ) { case 'foo':
+			
+		// Try to connect to the DB as root
+		$result_root = $dbal->connect(true, $db_host, $db_root_user, $db_root_pass, 'mysql', $db_port);
+		if ( !$result_root )
+			break;
+		
+		$q = $dbal->sql_query('CREATE DATABASE IF NOT EXISTS `' . $dbal->escape($db_name) . '`;');
+		if ( !$q )
+			break;
+		
+		if ( $db_host == 'localhost' || $db_host == '127.0.0.1' )
+		{
+			$q = $dbal->sql_query('GRANT ALL PRIVILEGES ON `' . $dbal->escape($db_name) . '`.* TO \'' . $dbal->escape($db_user) . '\'@\'localhost\'' . "\n" .
+														'  IDENTIFIED BY \'' . $dbal->escape($db_pass) . '\' WITH GRANT OPTION');
+		}
+		else
+		{
+			$q = $dbal->sql_query('GRANT ALL PRIVILEGES ON `' . $dbal->escape($db_name) . '`.* TO \'' . $dbal->escape($db_user) . '\'@\'%\'' . "\n" .
+														'  IDENTIFIED BY \'' . $dbal->escape($db_pass) . '\' WITH GRANT OPTION');
+		}
+		
+		if ( !$q )
+			break;
+		
+		$dbal->close();
+		$result = $dbal->connect(true, $db_host, $db_user, $db_pass, $db_name, $db_port);
+			
+		break;
+	}
 }
 
 $ui->show_header();
 
 if ( $result )
 {
-  // We're good, do table drop if requested
-  if ( isset($_POST['drop_tables']) )
-  {
-    global $system_table_list;
-    foreach ( $system_table_list as $table )
-    {
-      $dbal->sql_query("DROP TABLE {$db_prefix}$table");
-    }
-  }
-  // Write out a config file
-  $ch = @fopen( ENANO_ROOT . '/config.new.php', 'w' );
-  if ( !$ch )
-  {
-    ?>
-    <form action="install.php?stage=database" method="post" name="database_info">
-      <h3>Configuration file generation failed.</h3>
-      <p>Couldn't open the configuration file to write out database settings. Check your file permissions.</p>
-      <p>
-        <input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
-      </p>
-    </form>
-    <?php
-    return true;
-  }
-  $db_host = str_replace("'", "\\'", $db_host);
-  $db_user = str_replace("'", "\\'", $db_user);
-  $db_pass = str_replace("'", "\\'", $db_pass);
-  $db_name = str_replace("'", "\\'", $db_name);
-  $db_prefix = str_replace("'", "\\'", $db_prefix);
-  if ( !preg_match('/^[a-z0-9_]*$/', $db_prefix) )
-  {
-    echo '<p>That table prefix isn\'t going to work.</p>';
-    return true;
-  }
-  if ( !preg_match('/^[0-9]*$/', $db_port) )
-  {
-    echo '<p>That port isn\'t going to work.</p>';
-    return true;
-  }
-  fwrite($ch, "<?php
+	// We're good, do table drop if requested
+	if ( isset($_POST['drop_tables']) )
+	{
+		global $system_table_list;
+		foreach ( $system_table_list as $table )
+		{
+			$dbal->sql_query("DROP TABLE {$db_prefix}$table");
+		}
+	}
+	// Write out a config file
+	$ch = @fopen( ENANO_ROOT . '/config.new.php', 'w' );
+	if ( !$ch )
+	{
+		?>
+		<form action="install.php?stage=database" method="post" name="database_info">
+			<h3>Configuration file generation failed.</h3>
+			<p>Couldn't open the configuration file to write out database settings. Check your file permissions.</p>
+			<p>
+				<input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
+			</p>
+		</form>
+		<?php
+		return true;
+	}
+	$db_host = str_replace("'", "\\'", $db_host);
+	$db_user = str_replace("'", "\\'", $db_user);
+	$db_pass = str_replace("'", "\\'", $db_pass);
+	$db_name = str_replace("'", "\\'", $db_name);
+	$db_prefix = str_replace("'", "\\'", $db_prefix);
+	if ( !preg_match('/^[a-z0-9_]*$/', $db_prefix) )
+	{
+		echo '<p>That table prefix isn\'t going to work.</p>';
+		return true;
+	}
+	if ( !preg_match('/^[0-9]*$/', $db_port) )
+	{
+		echo '<p>That port isn\'t going to work.</p>';
+		return true;
+	}
+	fwrite($ch, "<?php
 // Enano temporary configuration file, will be OVERWRITTEN after installation.
 
 \$dbdriver = '$driver';
@@ -130,97 +130,97 @@
 
 @define('ENANO_INSTALL_HAVE_CONFIG', 1);
 ");
-  fclose($ch);
-  // Create the config table
-  try
-  {
-    $sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$driver}_stage1.sql" );
-  }
-  catch ( Exception $e )
-  {
-    ?>
-    <h3>Can't load schema file</h3>
-    <p>The SQL schema file couldn't be loaded.</p>
-    <?php echo "<pre>$e</pre>"; ?>
-    <?php
-    return true;
-  }
-  // Check to see if the config table already exists
-  $q = $dbal->sql_query('SELECT config_name, config_value FROM ' . $db_prefix . 'config LIMIT 1;');
-  if ( !$q )
-  {
-    $sql_parser->assign_vars(array(
-        'TABLE_PREFIX' => $db_prefix
-      ));
-    $sql = $sql_parser->parse();
-    foreach ( $sql as $q )
-    {
-      if ( !$dbal->sql_query($q) )
-      {
-        ?>
-        <form action="install.php?stage=database" method="post" name="database_info">
-          <input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
-          <input type="hidden" name="driver" value="<?php echo $driver; ?>" />
-          <h3><?php echo $lang->get('database_msg_sql_fail_title'); ?></h3>
-          <p><?php echo $lang->get('database_msg_sql_fail_body'); ?></p>
-          <p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
-            <?php
-            echo $dbal->sql_error();
-            ?>
-          </p>
-          <p>
-            <input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
-          </p>
-        </form>
-        <?php
-        return true;
-      }
-    }
-  }
-  else
-  {
-    $dbal->free_result();
-    if ( !$dbal->sql_query('DELETE FROM ' . $db_prefix . 'config WHERE config_name = \'install_aes_key\';') )
-    {
-      $dbal->_die('install database_post.php trying to remove old AES installer key');
-    }
-  }
-  $dbal->close();
-  ?>
-  <form action="install.php?stage=website" method="post" name="install_db_post" onsubmit="return verify();">
-  <input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
-  <?php
-  ?>
-  <h3><?php echo $lang->get('database_msg_success_title'); ?></h3>
-  <p><?php echo $lang->get('database_msg_success_body'); ?></p>
-  <p><input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" />  <?php echo $lang->get('database_msg_success_redirect'); ?></p>
-  </form>
-  <script type="text/javascript">
-    setTimeout(function()
-      {
-        var frm = document.forms.install_db_post;
-        frm.submit();
-      }, 200);
-  </script>
-  <?php
+	fclose($ch);
+	// Create the config table
+	try
+	{
+		$sql_parser = new SQL_Parser( ENANO_ROOT . "/install/schemas/{$driver}_stage1.sql" );
+	}
+	catch ( Exception $e )
+	{
+		?>
+		<h3>Can't load schema file</h3>
+		<p>The SQL schema file couldn't be loaded.</p>
+		<?php echo "<pre>$e</pre>"; ?>
+		<?php
+		return true;
+	}
+	// Check to see if the config table already exists
+	$q = $dbal->sql_query('SELECT config_name, config_value FROM ' . $db_prefix . 'config LIMIT 1;');
+	if ( !$q )
+	{
+		$sql_parser->assign_vars(array(
+				'TABLE_PREFIX' => $db_prefix
+			));
+		$sql = $sql_parser->parse();
+		foreach ( $sql as $q )
+		{
+			if ( !$dbal->sql_query($q) )
+			{
+				?>
+				<form action="install.php?stage=database" method="post" name="database_info">
+					<input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
+					<input type="hidden" name="driver" value="<?php echo $driver; ?>" />
+					<h3><?php echo $lang->get('database_msg_sql_fail_title'); ?></h3>
+					<p><?php echo $lang->get('database_msg_sql_fail_body'); ?></p>
+					<p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
+						<?php
+						echo $dbal->sql_error();
+						?>
+					</p>
+					<p>
+						<input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
+					</p>
+				</form>
+				<?php
+				return true;
+			}
+		}
+	}
+	else
+	{
+		$dbal->free_result();
+		if ( !$dbal->sql_query('DELETE FROM ' . $db_prefix . 'config WHERE config_name = \'install_aes_key\';') )
+		{
+			$dbal->_die('install database_post.php trying to remove old AES installer key');
+		}
+	}
+	$dbal->close();
+	?>
+	<form action="install.php?stage=website" method="post" name="install_db_post" onsubmit="return verify();">
+	<input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
+	<?php
+	?>
+	<h3><?php echo $lang->get('database_msg_success_title'); ?></h3>
+	<p><?php echo $lang->get('database_msg_success_body'); ?></p>
+	<p><input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" />  <?php echo $lang->get('database_msg_success_redirect'); ?></p>
+	</form>
+	<script type="text/javascript">
+		setTimeout(function()
+			{
+				var frm = document.forms.install_db_post;
+				frm.submit();
+			}, 200);
+	</script>
+	<?php
 }
 else
 {
-  ?>
-  <form action="install.php?stage=database" method="post" name="database_info">
-    <input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
-    <input type="hidden" name="driver" value="<?php echo $driver; ?>" />
-    <h3><?php echo $lang->get('database_msg_post_fail_title'); ?></h3>
-    <p><?php echo $lang->get('database_msg_post_fail_body'); ?></p>
-    <p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
-      <?php
-      echo $dbal->sql_error();
-      ?>
-    </p>
-    <p>
-      <input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
-    </p>
-  </form>
-  <?php
+	?>
+	<form action="install.php?stage=database" method="post" name="database_info">
+		<input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
+		<input type="hidden" name="driver" value="<?php echo $driver; ?>" />
+		<h3><?php echo $lang->get('database_msg_post_fail_title'); ?></h3>
+		<p><?php echo $lang->get('database_msg_post_fail_body'); ?></p>
+		<p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
+			<?php
+			echo $dbal->sql_error();
+			?>
+		</p>
+		<p>
+			<input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
+		</p>
+	</form>
+	<?php
 }
 
--- a/install/includes/stages/database_postgresql.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/database_postgresql.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,152 +14,152 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 if ( isset($_POST['_cont']) )
 {
-  $allow_go = true;
-  // Do we have everything? If so, continue with installation.
-  foreach ( array('db_host', 'db_name', 'db_user', 'db_pass', 'db_port') as $field )
-  {
-    if ( empty($_POST[$field]) )
-    {
-      $allow_go = false;
-    }
-  }
-  if ( $allow_go )
-  {
-    require( ENANO_ROOT . '/install/includes/stages/database_post.php' );
-    return true;
-  }
+	$allow_go = true;
+	// Do we have everything? If so, continue with installation.
+	foreach ( array('db_host', 'db_name', 'db_user', 'db_pass', 'db_port') as $field )
+	{
+		if ( empty($_POST[$field]) )
+		{
+			$allow_go = false;
+		}
+	}
+	if ( $allow_go )
+	{
+		require( ENANO_ROOT . '/install/includes/stages/database_post.php' );
+		return true;
+	}
 }
 
 if ( isset($_POST['ajax_test']) )
 {
-  // Test the database connection
-  $return = array(
-      'can_install' => false,
-      'host_good' => true,
-      'creating_user' => false,
-      'db_exist' => false,
-      'creating_db' => false,
-      'creating_db_grant' => false,
-      'root_fail' => false,
-      'version' => array(
-        'version' => 'unknown',
-        'good' => 'indeterminate'
-      ),
-      'last_error' => ''
-    );
-  
-  if ( !isset($_POST['info']) )
-    die();
-  
-  $info = $_POST['info'];
-  
-  // From here on out will be JSON responses
-  header('Content-type: application/json');
-  
-  try
-  {
-    $info = @enano_json_decode($info);
-  }
-  catch ( Zend_Json_Exception $e )
-  {
-    die(enano_json_encode(array(
-        'mode' => 'error',
-        'error' => 'Exception in JSON decoder'
-      )));
-  }
-  
-  if ( !is_int($info['db_port']) )
-  {
-    $return['host_good'] = false;
-    echo enano_json_encode($return);
-    exit;
-  }
-  
-  $port = $info['db_port'] ? $info['db_port'] : 5432;
-  
-  // Try to connect as the normal user
-  // generate connection string
-  $conn_string = "dbname = '" . addslashes($info['db_name']) . "' port = '$port' host = '" . addslashes($info['db_host']) . "' " . 
-                 "user= '" . addslashes($info['db_user']) . "' password = '" . addslashes($info['db_pass']) . "'";
-  $test = @pg_connect($conn_string);
-  if ( !$test )
-  {
-    // Connection as normal user failed. PgSQL doesn't give us an error string so
-    // just try to connect as root. If even that fails, exit with an error
-    $return['creating_user'] = true;
-    if ( !empty($info['db_root_user']) && !empty($info['db_root_pass']) )
-    {
-      $conn_string_root = "dbname = '" . addslashes($info['db_name']) . "' port = '$port' host = '" . addslashes($info['db_host']) . "' " . 
-                          "user= '" . addslashes($info['db_root_user']) . "' password = '" . addslashes($info['db_root_pass']) . "'";
-      // Attempt connection as root
-      $test_root = @pg_connect($conn_string_root);
-      if ( !$test_root )
-      {
-        $return['root_fail'] = true;
-      }
-      else
-      {
-        $return['can_install'] = true;
-      }
-    }
-  }
-  else
-  {
-    $return['can_install'] = true;
-  }
-  
-  $did_version_check = false;
-  
-  if ( isset($test) && @is_resource($test) )
-  {
-    $server_info = @pg_version($test);
-    if ( isset($server_info['server']) )
-    {
-      $did_version_check = true;
-      $return['version'] = array(
-          'version' => $server_info['server'],
-          'good' => ( version_compare($server_info['server'], '8.2.5', '>=') )
-        );
-    }
-    @pg_close($test);
-  }
-  
-  if ( isset($test_root) && @is_resource($test_root) )
-  {
-    $server_info = @pg_version($test_root);
-    if ( isset($server_info['server']) )
-    {
-      $did_version_check = true;
-      $return['version'] = array(
-          'version' => $server_info['server'],
-          'good' => ( version_compare($server_info['server'], '8.2.5', '>=') )
-        );
-    }
-    @pg_close($test_root);
-  }
-  
-  if ( !$did_version_check )
-  {
-    $return['version'] = array(
-        'version' => 'indeterminate',
-        'good' => false
-      );
-  }
-  else
-  {
-    if ( !$return['version']['good'] )
-    {
-      $return['can_install'] = false;
-    }
-  }
-  
-  echo enano_json_encode($return);
-  
-  exit();
+	// Test the database connection
+	$return = array(
+			'can_install' => false,
+			'host_good' => true,
+			'creating_user' => false,
+			'db_exist' => false,
+			'creating_db' => false,
+			'creating_db_grant' => false,
+			'root_fail' => false,
+			'version' => array(
+				'version' => 'unknown',
+				'good' => 'indeterminate'
+			),
+			'last_error' => ''
+		);
+	
+	if ( !isset($_POST['info']) )
+		die();
+	
+	$info = $_POST['info'];
+	
+	// From here on out will be JSON responses
+	header('Content-type: application/json');
+	
+	try
+	{
+		$info = @enano_json_decode($info);
+	}
+	catch ( Zend_Json_Exception $e )
+	{
+		die(enano_json_encode(array(
+				'mode' => 'error',
+				'error' => 'Exception in JSON decoder'
+			)));
+	}
+	
+	if ( !is_int($info['db_port']) )
+	{
+		$return['host_good'] = false;
+		echo enano_json_encode($return);
+		exit;
+	}
+	
+	$port = $info['db_port'] ? $info['db_port'] : 5432;
+	
+	// Try to connect as the normal user
+	// generate connection string
+	$conn_string = "dbname = '" . addslashes($info['db_name']) . "' port = '$port' host = '" . addslashes($info['db_host']) . "' " . 
+ 								"user= '" . addslashes($info['db_user']) . "' password = '" . addslashes($info['db_pass']) . "'";
+	$test = @pg_connect($conn_string);
+	if ( !$test )
+	{
+		// Connection as normal user failed. PgSQL doesn't give us an error string so
+		// just try to connect as root. If even that fails, exit with an error
+		$return['creating_user'] = true;
+		if ( !empty($info['db_root_user']) && !empty($info['db_root_pass']) )
+		{
+			$conn_string_root = "dbname = '" . addslashes($info['db_name']) . "' port = '$port' host = '" . addslashes($info['db_host']) . "' " . 
+													"user= '" . addslashes($info['db_root_user']) . "' password = '" . addslashes($info['db_root_pass']) . "'";
+			// Attempt connection as root
+			$test_root = @pg_connect($conn_string_root);
+			if ( !$test_root )
+			{
+				$return['root_fail'] = true;
+			}
+			else
+			{
+				$return['can_install'] = true;
+			}
+		}
+	}
+	else
+	{
+		$return['can_install'] = true;
+	}
+	
+	$did_version_check = false;
+	
+	if ( isset($test) && @is_resource($test) )
+	{
+		$server_info = @pg_version($test);
+		if ( isset($server_info['server']) )
+		{
+			$did_version_check = true;
+			$return['version'] = array(
+					'version' => $server_info['server'],
+					'good' => ( version_compare($server_info['server'], '8.2.5', '>=') )
+				);
+		}
+		@pg_close($test);
+	}
+	
+	if ( isset($test_root) && @is_resource($test_root) )
+	{
+		$server_info = @pg_version($test_root);
+		if ( isset($server_info['server']) )
+		{
+			$did_version_check = true;
+			$return['version'] = array(
+					'version' => $server_info['server'],
+					'good' => ( version_compare($server_info['server'], '8.2.5', '>=') )
+				);
+		}
+		@pg_close($test_root);
+	}
+	
+	if ( !$did_version_check )
+	{
+		$return['version'] = array(
+				'version' => 'indeterminate',
+				'good' => false
+			);
+	}
+	else
+	{
+		if ( !$return['version']['good'] )
+		{
+			$return['can_install'] = false;
+		}
+	}
+	
+	echo enano_json_encode($return);
+	
+	exit();
 }
 
 $ui->add_header('<script type="text/javascript" src="includes/js/formutils.js"></script>');
@@ -169,173 +169,173 @@
 
 <script type="text/javascript">
 
-  var img_bad = '../images/checkbad.png';
-  var img_good = '../images/check.png';
-  var img_neu = '../images/checkunk.png';
-  
-  var tested = false;
+	var img_bad = '../images/checkbad.png';
+	var img_good = '../images/check.png';
+	var img_neu = '../images/checkunk.png';
+	
+	var tested = false;
 
-  function verify(field)
-  {
-    if ( tested && !field )
-      return true;
-    tested = false;
-    if ( document.getElementById('verify_error').className != '' )
-    {
-      document.getElementById('verify_error').className = '';
-      document.getElementById('verify_error').innerHTML = '';
-    }
-    var frm = document.forms.database_info;
-    // List of fields
-    var fields = {
-      db_host: frm.db_host,
-      db_port: frm.db_port,
-      db_name: frm.db_name,
-      db_user: frm.db_user,
-      db_pass: frm.db_pass,
-      table_prefix: frm.table_prefix,
-      db_root_user: frm.db_root_user,
-      db_root_pass: frm.db_root_pass
-    };
-    var passed = true;
-    // Main validation
-    if ( field == fields.db_host || !field )
-    {
-      var matches = fields.db_host.value.match(/^([a-z0-9_-]+)((\.([a-z0-9_-]+))*)?$/);
-      document.getElementById('s_db_host').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.db_port || !field )
-    {
-      var matches = fields.db_port.value.match(/^[0-9]+$/);
-      document.getElementById('s_db_port').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.db_name || !field )
-    {
-      var matches = fields.db_name.value.match(/^[A-z0-9_-]+$/);
-      document.getElementById('s_db_name').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.db_user || field == fields.db_pass || !field )
-    {
-      var matches = fields.db_user.value.match(/^[A-z0-9_-]+$/);
-      document.getElementById('s_db_auth').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.table_prefix || !field )
-    {
-      var matches = fields.table_prefix.value.match(/^[a-z0-9_]*$/);
-      document.getElementById('s_table_prefix').src = ( matches ) ? img_good : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    if ( field == fields.db_root_user || field == fields.db_root_pass || !field )
-    {
-      var matches = ( ( fields.db_root_user.value.match(/^[A-z0-9_-]+$/) && fields.db_root_pass.value.match(/^.+$/) ) || fields.db_root_user.value == '' );
-      document.getElementById('s_db_root').src = ( matches ) ? img_neu : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    return passed;
-  }
-  
-  function ajaxTestConnection()
-  {
-    if ( !verify() )
-    {
-      document.body.scrollTop = 0;
-      $('enano-body').effect('shake', {}, 750);
-      document.getElementById('verify_error').className = 'error-box-mini';
-      document.getElementById('verify_error').innerHTML = $lang.get('meta_msg_err_verification');
-      return false;
-    }
-    install_set_ajax_loading();
-    
-    var frm = document.forms.database_info;
-    var connection_info = 'info=' + ajaxEscape(toJSONString({
-        db_host: frm.db_host.value,
-        db_port: parseInt(frm.db_port.value),
-        db_name: frm.db_name.value,
-        db_user: frm.db_user.value,
-        db_pass: frm.db_pass.value,
-        db_root_user: frm.db_root_user.value,
-        db_root_pass: frm.db_root_pass.value
-      }));
-    
-    ajaxPost(scriptPath + '/install/install.php?stage=database', connection_info + '&driver=postgresql&ajax_test=on&language=' + enano_lang_code[ENANO_LANG_ID], function(ajax)
-      {
-        if ( ajax.readyState == 4 && ajax.status == 200 )
-        {
-          setTimeout('install_unset_ajax_loading();', 750);
-          // Process response
-          var response = String(ajax.responseText + '');
-          if ( response.substr(0, 1) != '{' )
-          {
-            alert('Received an invalid JSON response from the server.');
-            return false;
-          }
-          response = parseJSON(response);
-          document.getElementById('e_db_host').innerHTML = '';
-          document.getElementById('e_db_name').innerHTML = '';
-          document.getElementById('e_db_auth').innerHTML = '';
-          document.getElementById('e_db_root').innerHTML = '';
-          if ( response.can_install )
-          {
-            tested = true;
-            var statuses = ['s_db_host', 's_db_name', 's_db_auth', 's_table_prefix', 's_db_root', 's_pgsql_version'];
-            for ( var i in statuses )
-            {
-              var img = document.getElementById(statuses[i]);
-              if ( img )
-                img.src = img_good;
-            }
-            document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_info_version_good');
-            document.getElementById('verify_error').className = 'info-box-mini';
-            document.getElementById('verify_error').innerHTML = $lang.get('dbpgsql_msg_test_success');
-            if ( response.creating_db )
-            {
-              document.getElementById('e_db_name').innerHTML = $lang.get('dbpgsql_msg_warn_creating_db');
-            }
-            if ( response.creating_user )
-            {
-              document.getElementById('e_db_auth').innerHTML = $lang.get('dbpgsql_msg_warn_creating_user');
-            }
-          }
-          else
-          {
-            // Oh dear, oh dear, oh dear, oh dear, oh dear...
-            if ( response.creating_db )
-            {
-              document.getElementById('e_db_name').innerHTML = $lang.get('dbpgsql_msg_err_dbexist', { pg_error: response.last_error });
-              document.getElementById('s_db_name').src = img_bad;
-            }
-            if ( response.creating_user )
-            {
-              document.getElementById('e_db_auth').innerHTML = $lang.get('dbpgsql_msg_err_auth', { pg_error: response.last_error });
-              document.getElementById('s_db_auth').src = img_bad;
-            }
-            if ( !response.host_good )
-            {
-              document.getElementById('e_db_host').innerHTML = $lang.get('dbpgsql_msg_err_connect', { db_host: frm.db_host.value, pg_error: response.last_error });
-              document.getElementById('s_db_host').src = img_bad;
-            }
-            if ( !response.version.good )
-            {
-              if ( response.version.version == 'indeterminate' )
-                document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_warn_pg_version');
-              else
-                document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_err_version', { pg_version: response.version.version });
-              document.getElementById('s_pgsql_version').src = img_bad;
-            }
-          }
-        }
-      });
-  }
+	function verify(field)
+	{
+		if ( tested && !field )
+			return true;
+		tested = false;
+		if ( document.getElementById('verify_error').className != '' )
+		{
+			document.getElementById('verify_error').className = '';
+			document.getElementById('verify_error').innerHTML = '';
+		}
+		var frm = document.forms.database_info;
+		// List of fields
+		var fields = {
+			db_host: frm.db_host,
+			db_port: frm.db_port,
+			db_name: frm.db_name,
+			db_user: frm.db_user,
+			db_pass: frm.db_pass,
+			table_prefix: frm.table_prefix,
+			db_root_user: frm.db_root_user,
+			db_root_pass: frm.db_root_pass
+		};
+		var passed = true;
+		// Main validation
+		if ( field == fields.db_host || !field )
+		{
+			var matches = fields.db_host.value.match(/^([a-z0-9_-]+)((\.([a-z0-9_-]+))*)?$/);
+			document.getElementById('s_db_host').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.db_port || !field )
+		{
+			var matches = fields.db_port.value.match(/^[0-9]+$/);
+			document.getElementById('s_db_port').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.db_name || !field )
+		{
+			var matches = fields.db_name.value.match(/^[A-z0-9_-]+$/);
+			document.getElementById('s_db_name').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.db_user || field == fields.db_pass || !field )
+		{
+			var matches = fields.db_user.value.match(/^[A-z0-9_-]+$/);
+			document.getElementById('s_db_auth').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.table_prefix || !field )
+		{
+			var matches = fields.table_prefix.value.match(/^[a-z0-9_]*$/);
+			document.getElementById('s_table_prefix').src = ( matches ) ? img_good : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		if ( field == fields.db_root_user || field == fields.db_root_pass || !field )
+		{
+			var matches = ( ( fields.db_root_user.value.match(/^[A-z0-9_-]+$/) && fields.db_root_pass.value.match(/^.+$/) ) || fields.db_root_user.value == '' );
+			document.getElementById('s_db_root').src = ( matches ) ? img_neu : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		return passed;
+	}
+	
+	function ajaxTestConnection()
+	{
+		if ( !verify() )
+		{
+			document.body.scrollTop = 0;
+			$('enano-body').effect('shake', {}, 750);
+			document.getElementById('verify_error').className = 'error-box-mini';
+			document.getElementById('verify_error').innerHTML = $lang.get('meta_msg_err_verification');
+			return false;
+		}
+		install_set_ajax_loading();
+		
+		var frm = document.forms.database_info;
+		var connection_info = 'info=' + ajaxEscape(toJSONString({
+				db_host: frm.db_host.value,
+				db_port: parseInt(frm.db_port.value),
+				db_name: frm.db_name.value,
+				db_user: frm.db_user.value,
+				db_pass: frm.db_pass.value,
+				db_root_user: frm.db_root_user.value,
+				db_root_pass: frm.db_root_pass.value
+			}));
+		
+		ajaxPost(scriptPath + '/install/install.php?stage=database', connection_info + '&driver=postgresql&ajax_test=on&language=' + enano_lang_code[ENANO_LANG_ID], function(ajax)
+			{
+				if ( ajax.readyState == 4 && ajax.status == 200 )
+				{
+					setTimeout('install_unset_ajax_loading();', 750);
+					// Process response
+					var response = String(ajax.responseText + '');
+					if ( response.substr(0, 1) != '{' )
+					{
+						alert('Received an invalid JSON response from the server.');
+						return false;
+					}
+					response = parseJSON(response);
+					document.getElementById('e_db_host').innerHTML = '';
+					document.getElementById('e_db_name').innerHTML = '';
+					document.getElementById('e_db_auth').innerHTML = '';
+					document.getElementById('e_db_root').innerHTML = '';
+					if ( response.can_install )
+					{
+						tested = true;
+						var statuses = ['s_db_host', 's_db_name', 's_db_auth', 's_table_prefix', 's_db_root', 's_pgsql_version'];
+						for ( var i in statuses )
+						{
+							var img = document.getElementById(statuses[i]);
+							if ( img )
+								img.src = img_good;
+						}
+						document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_info_version_good');
+						document.getElementById('verify_error').className = 'info-box-mini';
+						document.getElementById('verify_error').innerHTML = $lang.get('dbpgsql_msg_test_success');
+						if ( response.creating_db )
+						{
+							document.getElementById('e_db_name').innerHTML = $lang.get('dbpgsql_msg_warn_creating_db');
+						}
+						if ( response.creating_user )
+						{
+							document.getElementById('e_db_auth').innerHTML = $lang.get('dbpgsql_msg_warn_creating_user');
+						}
+					}
+					else
+					{
+						// Oh dear, oh dear, oh dear, oh dear, oh dear...
+						if ( response.creating_db )
+						{
+							document.getElementById('e_db_name').innerHTML = $lang.get('dbpgsql_msg_err_dbexist', { pg_error: response.last_error });
+							document.getElementById('s_db_name').src = img_bad;
+						}
+						if ( response.creating_user )
+						{
+							document.getElementById('e_db_auth').innerHTML = $lang.get('dbpgsql_msg_err_auth', { pg_error: response.last_error });
+							document.getElementById('s_db_auth').src = img_bad;
+						}
+						if ( !response.host_good )
+						{
+							document.getElementById('e_db_host').innerHTML = $lang.get('dbpgsql_msg_err_connect', { db_host: frm.db_host.value, pg_error: response.last_error });
+							document.getElementById('s_db_host').src = img_bad;
+						}
+						if ( !response.version.good )
+						{
+							if ( response.version.version == 'indeterminate' )
+								document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_warn_pg_version');
+							else
+								document.getElementById('e_pgsql_version').innerHTML = $lang.get('dbpgsql_msg_err_version', { pg_version: response.version.version });
+							document.getElementById('s_pgsql_version').src = img_bad;
+						}
+					}
+				}
+			});
+	}
 
 </script>
 
@@ -344,144 +344,144 @@
 <input type="hidden" name="driver" value="postgresql" />
 
 <table border="0" cellspacing="0" cellpadding="10" width="100%">
-  <tr>
-    <td colspan="3" style="text-align: center">
-      <h3><?php echo $lang->get('dbpgsql_table_title'); ?></h3>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbpgsql_field_hostname_title'); ?></b>
-      <br /><?php echo $lang->get('dbpgsql_field_hostname_body'); ?>
-      <br /><span style="color: #993300" id="e_db_host"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="1" name="db_host" size="30" type="text" />
-    </td>
-    <td>
-      <img id="s_db_host" alt="Good/bad icon" src="../images/checkbad.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbpgsql_field_port_title'); ?></b>
-      <br /><?php echo $lang->get('dbpgsql_field_port_body'); ?>
-      <br /><span style="color: #993300" id="e_db_port"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="2" name="db_port" size="5" type="text" value="5432" />
-    </td>
-    <td>
-      <img id="s_db_port" alt="Good/bad icon" src="../images/checkbad.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbpgsql_field_dbname_title'); ?></b><br />
-      <?php echo $lang->get('dbpgsql_field_dbname_body'); ?><br />
-      <span style="color: #993300" id="e_db_name"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="2" name="db_name" size="30" type="text" />
-    </td>
-    <td>
-      <img id="s_db_name" alt="Good/bad icon" src="../images/checkbad.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbpgsql_field_dbauth_title'); ?></b><br />
-      <?php echo $lang->get('dbpgsql_field_dbauth_body'); ?><br />
-      <span style="color: #993300" id="e_db_auth"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="3" name="db_user" size="30" type="text" /><br />
-      <br />
-      <input name="db_pass" size="30" tabindex="4" type="password" />
-    </td>
-    <td>
-      <img id="s_db_auth" alt="Good/bad icon" src="../images/checkbad.png" />
-    </td>
-  </tr>
-  <tr>
-    <td colspan="3" style="text-align: center">
-      <h3><?php echo $lang->get('database_heading_optionalinfo'); ?></h3>
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbpgsql_field_tableprefix_title'); ?></b><br />
-      <?php echo $lang->get('dbpgsql_field_tableprefix_body'); ?>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="5" name="table_prefix" size="30" type="text" />
-    </td>
-    <td>
-      <img id="s_table_prefix" alt="Good/bad icon" src="../images/check.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbpgsql_field_rootauth_title'); ?></b><br />
-      <?php echo $lang->get('dbpgsql_field_rootauth_body'); ?><br />
-      <span style="color: #993300" id="e_db_root"></span>
-    </td>
-    <td>
-      <input onkeyup="verify(this);" tabindex="6" name="db_root_user" size="30" type="text" /><br />
-      <br />
-      <input onkeyup="verify(this);" tabindex="7" name="db_root_pass" size="30" type="password" />
-    </td>
-    <td>
-      <img id="s_db_root" alt="Good/bad icon" src="../images/check.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbpgsql_field_pgsqlversion_title'); ?></b>
-    </td>
-    <td id="e_pgsql_version">
-      <?php echo $lang->get('dbpgsql_field_pgsqlversion_blurb_willbechecked'); ?>
-    </td>
-    <td>
-      <img id="s_pgsql_version" alt="Good/bad icon" src="../images/checkunk.png" />
-    </td>
-  </tr>
-  <tr>
-    <td>
-      <b><?php echo $lang->get('dbpgsql_field_droptables_title'); ?></b><br />
-      <?php echo $lang->get('dbpgsql_field_droptables_body'); ?>
-    </td>
-    <td colspan="2">
-      <input type="checkbox" tabindex="8" name="drop_tables" id="dtcheck" />  <label for="dtcheck"><?php echo $lang->get('dbpgsql_field_droptables_lbl'); ?></label>
-    </td>
-  </tr>
-  <tr>
-    <td colspan="3" style="text-align: center">
-      <input type="button" value="<?php echo $lang->get('dbpgsql_btn_testconnection'); ?>" onclick="ajaxTestConnection();" />
-      <div id="verify_error"></div>
-    </td>
-  </tr>
+	<tr>
+		<td colspan="3" style="text-align: center">
+			<h3><?php echo $lang->get('dbpgsql_table_title'); ?></h3>
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbpgsql_field_hostname_title'); ?></b>
+			<br /><?php echo $lang->get('dbpgsql_field_hostname_body'); ?>
+			<br /><span style="color: #993300" id="e_db_host"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="1" name="db_host" size="30" type="text" />
+		</td>
+		<td>
+			<img id="s_db_host" alt="Good/bad icon" src="../images/checkbad.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbpgsql_field_port_title'); ?></b>
+			<br /><?php echo $lang->get('dbpgsql_field_port_body'); ?>
+			<br /><span style="color: #993300" id="e_db_port"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="2" name="db_port" size="5" type="text" value="5432" />
+		</td>
+		<td>
+			<img id="s_db_port" alt="Good/bad icon" src="../images/checkbad.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbpgsql_field_dbname_title'); ?></b><br />
+			<?php echo $lang->get('dbpgsql_field_dbname_body'); ?><br />
+			<span style="color: #993300" id="e_db_name"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="2" name="db_name" size="30" type="text" />
+		</td>
+		<td>
+			<img id="s_db_name" alt="Good/bad icon" src="../images/checkbad.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbpgsql_field_dbauth_title'); ?></b><br />
+			<?php echo $lang->get('dbpgsql_field_dbauth_body'); ?><br />
+			<span style="color: #993300" id="e_db_auth"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="3" name="db_user" size="30" type="text" /><br />
+			<br />
+			<input name="db_pass" size="30" tabindex="4" type="password" />
+		</td>
+		<td>
+			<img id="s_db_auth" alt="Good/bad icon" src="../images/checkbad.png" />
+		</td>
+	</tr>
+	<tr>
+		<td colspan="3" style="text-align: center">
+			<h3><?php echo $lang->get('database_heading_optionalinfo'); ?></h3>
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbpgsql_field_tableprefix_title'); ?></b><br />
+			<?php echo $lang->get('dbpgsql_field_tableprefix_body'); ?>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="5" name="table_prefix" size="30" type="text" />
+		</td>
+		<td>
+			<img id="s_table_prefix" alt="Good/bad icon" src="../images/check.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbpgsql_field_rootauth_title'); ?></b><br />
+			<?php echo $lang->get('dbpgsql_field_rootauth_body'); ?><br />
+			<span style="color: #993300" id="e_db_root"></span>
+		</td>
+		<td>
+			<input onkeyup="verify(this);" tabindex="6" name="db_root_user" size="30" type="text" /><br />
+			<br />
+			<input onkeyup="verify(this);" tabindex="7" name="db_root_pass" size="30" type="password" />
+		</td>
+		<td>
+			<img id="s_db_root" alt="Good/bad icon" src="../images/check.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbpgsql_field_pgsqlversion_title'); ?></b>
+		</td>
+		<td id="e_pgsql_version">
+			<?php echo $lang->get('dbpgsql_field_pgsqlversion_blurb_willbechecked'); ?>
+		</td>
+		<td>
+			<img id="s_pgsql_version" alt="Good/bad icon" src="../images/checkunk.png" />
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<b><?php echo $lang->get('dbpgsql_field_droptables_title'); ?></b><br />
+			<?php echo $lang->get('dbpgsql_field_droptables_body'); ?>
+		</td>
+		<td colspan="2">
+			<input type="checkbox" tabindex="8" name="drop_tables" id="dtcheck" />  <label for="dtcheck"><?php echo $lang->get('dbpgsql_field_droptables_lbl'); ?></label>
+		</td>
+	</tr>
+	<tr>
+		<td colspan="3" style="text-align: center">
+			<input type="button" value="<?php echo $lang->get('dbpgsql_btn_testconnection'); ?>" onclick="ajaxTestConnection();" />
+			<div id="verify_error"></div>
+		</td>
+	</tr>
 
 </table>
 
 <table border="0">
-  <tr>
-    <td>
-      <input type="submit" tabindex="9" value="<?php echo $lang->get('meta_btn_continue'); ?>" onclick="return verify();" name="_cont" />
-    </td>
-    <td>
-      <p>
-        <span style="font-weight: bold;"><?php echo $lang->get('meta_lbl_before_continue'); ?></span><br />
-        &bull; <?php echo $lang->get('database_objective_test'); ?><br />
-        &bull; <?php echo $lang->get('database_objective_uncrypt'); ?>
-      </p>
-    </td>
-  </tr>
+	<tr>
+		<td>
+			<input type="submit" tabindex="9" value="<?php echo $lang->get('meta_btn_continue'); ?>" onclick="return verify();" name="_cont" />
+		</td>
+		<td>
+			<p>
+				<span style="font-weight: bold;"><?php echo $lang->get('meta_lbl_before_continue'); ?></span><br />
+				&bull; <?php echo $lang->get('database_objective_test'); ?><br />
+				&bull; <?php echo $lang->get('database_objective_uncrypt'); ?>
+			</p>
+		</td>
+	</tr>
 </table>
 
 </form>
 
 <script type="text/javascript">
-  verify();
+	verify();
 </script>
 
--- a/install/includes/stages/finish.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/finish.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,7 +14,7 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 require ( ENANO_ROOT . '/install/includes/libenanoinstall.php' );
 require ( ENANO_ROOT . '/includes/sql_parse.php' );
@@ -22,10 +22,10 @@
 
 if ( !in_array($dbdriver, $supported_drivers) )
 {
-  $ui->show_header();
-  echo '<h3>Installation error</h3>
-         <p>ERROR: That database driver is not supported.</p>';
-  return true;
+	$ui->show_header();
+	echo '<h3>Installation error</h3>
+ 				<p>ERROR: That database driver is not supported.</p>';
+	return true;
 }
 
 $ui->show_header();
@@ -41,11 +41,11 @@
 
 function stg_load_files()
 {
-  global $dbdriver;
-  if ( !@include( ENANO_ROOT . "/install/includes/payload.php" ) )
-    return false;
-  
-  return true;
+	global $dbdriver;
+	if ( !@include( ENANO_ROOT . "/install/includes/payload.php" ) )
+		return false;
+	
+	return true;
 }
 
 start_install_table();
@@ -61,8 +61,8 @@
 <h3><?php echo $lang->get('finish_msg_success_title'); ?></h3>
 <p><?php echo $lang->get('finish_msg_success_body', array('mainpage_link' => makeUrlNS('Article', 'Main_Page'))); ?></p>
 <?php 
-  echo $lang->get('finish_body');
-  echo '<p>' . $lang->get('finish_link_mainpage', array('mainpage_link' => scriptPath . '/index.php')) . '</p>';
+	echo $lang->get('finish_body');
+	echo '<p>' . $lang->get('finish_link_mainpage', array('mainpage_link' => scriptPath . '/index.php')) . '</p>';
 ?>
 <?php
 
--- a/install/includes/stages/install.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/install.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,7 +14,7 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 require ( ENANO_ROOT . '/install/includes/libenanoinstall.php' );
 require ( ENANO_ROOT . '/includes/sql_parse.php' );
@@ -23,36 +23,36 @@
 
 if ( !in_array($dbdriver, $supported_drivers) )
 {
-  $ui->show_header();
-  echo '<h3>Installation error</h3>
-         <p>ERROR: That database driver is not supported.</p>';
-  return true;
+	$ui->show_header();
+	echo '<h3>Installation error</h3>
+ 				<p>ERROR: That database driver is not supported.</p>';
+	return true;
 }
 
 $db = new $dbdriver();
 $result = $db->connect();
 if ( !$result )
 {
-  $ui->show_header();
-  // FIXME: l10n
-  ?>
-  <form action="install.php?stage=database" method="post" name="database_info">
-    <input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
-    <input type="hidden" name="driver" value="<?php echo $dbdriver; ?>" />
-    <h3><?php echo $lang->get('database_msg_post_fail_title'); ?></h3>
-    <p><?php echo $lang->get('database_msg_post_fail_body'); ?></p>
-    <p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
-      <?php
-      echo $db->sql_error();
-      ?>
-    </p>
-    <p>
-      <!-- FIXME: l10n -->
-      <input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
-    </p>
-  </form>
-  <?php
-  return true;
+	$ui->show_header();
+	// FIXME: l10n
+	?>
+	<form action="install.php?stage=database" method="post" name="database_info">
+		<input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
+		<input type="hidden" name="driver" value="<?php echo $dbdriver; ?>" />
+		<h3><?php echo $lang->get('database_msg_post_fail_title'); ?></h3>
+		<p><?php echo $lang->get('database_msg_post_fail_body'); ?></p>
+		<p><?php echo $lang->get('database_msg_post_fail_desc'); ?>
+			<?php
+			echo $db->sql_error();
+			?>
+		</p>
+		<p>
+			<!-- FIXME: l10n -->
+			<input type="submit" name="_cont" value="<?php echo $lang->get('database_btn_go_back'); ?>" />
+		</p>
+	</form>
+	<?php
+	return true;
 }
 
 // we're connected to the database now.
@@ -72,11 +72,11 @@
 
 function stg_load_files()
 {
-  global $dbdriver;
-  if ( !@include( ENANO_ROOT . "/install/includes/payload.php" ) )
-    return false;
-  
-  return true;
+	global $dbdriver;
+	if ( !@include( ENANO_ROOT . "/install/includes/payload.php" ) )
+		return false;
+	
+	return true;
 }
 
 start_install_table();
@@ -93,14 +93,14 @@
 
 @define('ENANO_ALLOW_LOAD_NOLANG', 1);
 require(ENANO_ROOT . '/includes/common.php');
-        
+				
 if ( is_object($db) && is_object($session) )
 {
-  run_installer_stage('startapi', $lang->get('install_stg_startapi_title'), 'stg_sim_good', '...', false);
+	run_installer_stage('startapi', $lang->get('install_stg_startapi_title'), 'stg_sim_good', '...', false);
 }
 else
 {
-  run_installer_stage('startapi', $lang->get('install_stg_startapi_title'), 'stg_sim_bad', $lang->get('install_stg_startapi_body'), false);
+	run_installer_stage('startapi', $lang->get('install_stg_startapi_title'), 'stg_sim_bad', $lang->get('install_stg_startapi_body'), false);
 }
 
 // Import languages
@@ -117,10 +117,10 @@
 
 ?>
 <form action="install.php?stage=finish" method="post" style="margin-top: 12px;">
-  <input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
-  <div style="text-align: center;">
-    <input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" tabindex="1" />
-  </div>
+	<input type="hidden" name="language" value="<?php echo $lang_id; ?>" />
+	<div style="text-align: center;">
+		<input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" tabindex="1" />
+	</div>
 </form>
 <?php
 
--- a/install/includes/stages/license.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/license.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,90 +14,90 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 function show_license($fb = false)
 {
-  global $lang;
-  global $installer_version;
-  ?>
-  <div class="scroller">
-  <?php
-    if ( !file_exists('./GPL') || !file_exists('./language/english/install/license-deed.html') )
-    {
-      echo 'Cannot find the license files.';
-    }
-    echo file_get_contents('./language/english/install/license-deed.html');
-    if ( $installer_version['type'] != 'stable' )
-    {
-      ?>
-      <h3><?php echo $lang->get('license_info_unstable_title'); ?></h3>
-      <p><?php echo $lang->get('license_info_unstable_body'); ?></p>
-      <?php
-    }
-    ?>
-    <h3><?php echo $lang->get('license_section_gpl_heading'); ?></h3>
-    <?php if ( $lang->lang_code != 'eng' ): ?>
-    <p><i><?php echo $lang->get('license_gpl_blurb_inenglish'); ?></i></p>
-    <?php endif; ?>
-    <?php echo wikiFormat(file_get_contents(ENANO_ROOT . '/GPL')); ?>
-   <?php
-   global $template;
-   if ( $fb )
-   {
-     echo '<p style="text-align: center;">Because I could never find the Create a Page button in PHP-Nuke.</p>';
-     echo '<p>' . str_replace('http://enanocms.org/', 'http://www.2robots.com/2003/10/15/web-portals-suck/', $template->fading_button) . '</p>';
-     echo '<p style="text-align: center;">It\'s not a portal, my friends.</p>';
-   }
-   ?>
+	global $lang;
+	global $installer_version;
+	?>
+	<div class="scroller">
+	<?php
+		if ( !file_exists('./GPL') || !file_exists('./language/english/install/license-deed.html') )
+		{
+			echo 'Cannot find the license files.';
+		}
+		echo file_get_contents('./language/english/install/license-deed.html');
+		if ( $installer_version['type'] != 'stable' )
+		{
+			?>
+			<h3><?php echo $lang->get('license_info_unstable_title'); ?></h3>
+			<p><?php echo $lang->get('license_info_unstable_body'); ?></p>
+			<?php
+		}
+		?>
+		<h3><?php echo $lang->get('license_section_gpl_heading'); ?></h3>
+		<?php if ( $lang->lang_code != 'eng' ): ?>
+		<p><i><?php echo $lang->get('license_gpl_blurb_inenglish'); ?></i></p>
+		<?php endif; ?>
+		<?php echo wikiFormat(file_get_contents(ENANO_ROOT . '/GPL')); ?>
+ 	<?php
+ 	global $template;
+ 	if ( $fb )
+ 	{
+ 		echo '<p style="text-align: center;">Because I could never find the Create a Page button in PHP-Nuke.</p>';
+ 		echo '<p>' . str_replace('http://enanocms.org/', 'http://www.2robots.com/2003/10/15/web-portals-suck/', $template->fading_button) . '</p>';
+ 		echo '<p style="text-align: center;">It\'s not a portal, my friends.</p>';
+ 	}
+ 	?>
  </div>
  <?php
 }
 
 function wikiFormat($text)
 {
-  require_once( ENANO_ROOT . '/includes/render.php' );
-  require_once( ENANO_ROOT . '/includes/wikiformat.php' );
-  require_once( ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php' );
-  require_once( ENANO_ROOT . '/includes/wikiengine/Tables.php' );
-  
-  $carpenter = new Carpenter();
-  // disable rules that require the DB
-  $carpenter->disable_rule('templates');
-  $carpenter->disable_rule('internallink');
-  $carpenter->disable_rule('image');
-  
-  $text = $carpenter->render($text);
-  
-  return $text;
+	require_once( ENANO_ROOT . '/includes/render.php' );
+	require_once( ENANO_ROOT . '/includes/wikiformat.php' );
+	require_once( ENANO_ROOT . '/includes/wikiengine/TagSanitizer.php' );
+	require_once( ENANO_ROOT . '/includes/wikiengine/Tables.php' );
+	
+	$carpenter = new Carpenter();
+	// disable rules that require the DB
+	$carpenter->disable_rule('templates');
+	$carpenter->disable_rule('internallink');
+	$carpenter->disable_rule('image');
+	
+	$text = $carpenter->render($text);
+	
+	return $text;
 }
 
 ?>
-    <h3><?php echo $lang->get('license_heading'); ?></h3>
-     <p><?php echo $lang->get('license_blurb_thankyou'); ?></p>
-     <p><?php echo $lang->get('license_blurb_pleaseread'); ?></p>
-     <?php show_license(); ?>
-     <div class="pagenav">
-       <form action="install.php?stage=sysreqs" method="post">
-       <?php
-       echo '<input type="hidden" name="language" value="' . $lang_id . '" />';
-       ?>
-         <table border="0">
-         <tr>
-           <td>
-             <input type="submit" value="<?php echo $lang->get('license_btn_i_agree'); ?>" />
-           </td>
-           <td>
-             <p>
-               <span style="font-weight: bold;"><?php echo $lang->get('meta_lbl_before_continue'); ?></span><br />
-               &bull; <?php echo $lang->get('license_objective_ensure_agree'); ?><br />
-               &bull; <?php echo $lang->get('license_objective_have_db_info'); ?>
-             </p>
-           </td>
-         </tr>
-         </table>
-       </form>
-     </div>
-    <?php
+		<h3><?php echo $lang->get('license_heading'); ?></h3>
+ 		<p><?php echo $lang->get('license_blurb_thankyou'); ?></p>
+ 		<p><?php echo $lang->get('license_blurb_pleaseread'); ?></p>
+ 		<?php show_license(); ?>
+ 		<div class="pagenav">
+ 			<form action="install.php?stage=sysreqs" method="post">
+ 			<?php
+ 			echo '<input type="hidden" name="language" value="' . $lang_id . '" />';
+ 			?>
+ 				<table border="0">
+ 				<tr>
+ 					<td>
+ 						<input type="submit" value="<?php echo $lang->get('license_btn_i_agree'); ?>" />
+ 					</td>
+ 					<td>
+ 						<p>
+ 							<span style="font-weight: bold;"><?php echo $lang->get('meta_lbl_before_continue'); ?></span><br />
+ 							&bull; <?php echo $lang->get('license_objective_ensure_agree'); ?><br />
+ 							&bull; <?php echo $lang->get('license_objective_have_db_info'); ?>
+ 						</p>
+ 					</td>
+ 				</tr>
+ 				</table>
+ 			</form>
+ 		</div>
+		<?php
 
 ?>
--- a/install/includes/stages/login.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/login.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,7 +14,7 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 // AES functions required
 require_once( ENANO_ROOT . '/includes/rijndael.php' );
@@ -25,28 +25,28 @@
 require( ENANO_ROOT . '/config.new.php' );
 if ( !defined('ENANO_INSTALL_HAVE_CONFIG') )
 {
-  die('Config file is corrupt');
+	die('Config file is corrupt');
 }
 $db = new $dbdriver();
 $result = $db->connect();
 if ( !$result )
-  die('DB privileges were revoked');
+	die('DB privileges were revoked');
 
 // Is the key in the database?
 $q = $db->sql_query('SELECT config_value FROM ' . table_prefix . 'config WHERE config_name = \'install_aes_key\';');
 if ( !$q )
-  $db->_die();
+	$db->_die();
 if ( $db->numrows() > 0 )
 {
-  list($install_aes_key) = $db->fetchrow_num();
+	list($install_aes_key) = $db->fetchrow_num();
 }
 else
 {
-  $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-  $install_aes_key = $aes->gen_readymade_key();
-  
-  if ( ! $db->sql_query('INSERT INTO ' . table_prefix . 'config ( config_name, config_value ) VALUES ( \'install_aes_key\', \'' . $install_aes_key .'\' ); ') )
-    $db->_die();
+	$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+	$install_aes_key = $aes->gen_readymade_key();
+	
+	if ( ! $db->sql_query('INSERT INTO ' . table_prefix . 'config ( config_name, config_value ) VALUES ( \'install_aes_key\', \'' . $install_aes_key .'\' ); ') )
+		$db->_die();
 }
 $db->free_result($q);
 
@@ -60,174 +60,174 @@
 
 <script type="text/javascript">
 
-  // <![CDATA[
-  
-  function verify(target)
-  {
-    var frm = document.forms [ 'install_login' ];
-    var undefined;
-    var passed = true;
-    
-    var data = {
-      username: frm.username.value,
-      password: frm.password.value,
-      password_confirm: frm.password_confirm.value,
-      email: frm.email.value
-    };
-    
-    if ( !target )
-      target = { name: undefined };
-    
-    if ( target.name == undefined || target.name == 'username' )
-    {
-      var matches = validateUsername(data.username);
-      document.getElementById('s_username').src = ( matches ) ? img_good : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    
-    if ( target.name == undefined || target.name == 'password' || target.name == 'password_confirm' )
-    {
-      var matches = ( data.password.length >= 6 && data.password == data.password_confirm ) ;
-      document.getElementById('s_password').src = ( matches ) ? img_good : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    
-    if ( target.name == undefined || target.name == 'email' )
-    {
-      var matches = validateEmail(data.email);
-      document.getElementById('s_email').src = ( matches ) ? img_good : img_bad;
-      if ( !matches )
-        passed = false;
-    }
-    
-    return passed;
-  }
-  
-  function verify_submit()
-  {
-    if ( verify() )
-      return true;
-    alert($lang.get('login_err_verify_failure'));
-    return false;
-  }
-  
-  function submit_encrypt()
-  {
-    var frm = document.forms [ 'install_login' ];
-    var password = frm.password.value;
-    var pass_conf = frm.password_confirm.value;
-    var crypt_key = frm.crypt_key.value;
-    
-    if ( password != pass_conf )
-      return false;
-    
-    if ( !aes_self_test() )
-      // Return true to prevent form from failing
-      return true;
-      
-    if ( frm.crypt_key.KeyBak )
-    {
-      crypt_key = frm.crypt_key.KeyBak;
-    }
-    frm.crypt_key.KeyBak = crypt_key;
-    
-    password = stringToByteArray(password);
-    crypt_key = hexToByteArray(crypt_key);
-    
-    var crypt_data = rijndaelEncrypt(password, crypt_key, 'ECB');
-    
-    if ( !crypt_data )
-    {
-      alert($lang.get('login_err_rijndael_failed'));
-      return false;
-    }
-  
-    crypt_data = byteArrayToHex(crypt_data);
-    
-    frm.password.value = '';
-    frm.password_confirm.value = '';
-    frm.crypt_key.value = '';
-    frm.crypt_data.value = crypt_data;
-    
-    return true;
-  }
-  
-  addOnloadHook(function()
-    {
-      load_component('crypto');
-      load_component('l10n');
-    });
-  
-  // ]]>
+	// <![CDATA[
+	
+	function verify(target)
+	{
+		var frm = document.forms [ 'install_login' ];
+		var undefined;
+		var passed = true;
+		
+		var data = {
+			username: frm.username.value,
+			password: frm.password.value,
+			password_confirm: frm.password_confirm.value,
+			email: frm.email.value
+		};
+		
+		if ( !target )
+			target = { name: undefined };
+		
+		if ( target.name == undefined || target.name == 'username' )
+		{
+			var matches = validateUsername(data.username);
+			document.getElementById('s_username').src = ( matches ) ? img_good : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		
+		if ( target.name == undefined || target.name == 'password' || target.name == 'password_confirm' )
+		{
+			var matches = ( data.password.length >= 6 && data.password == data.password_confirm ) ;
+			document.getElementById('s_password').src = ( matches ) ? img_good : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		
+		if ( target.name == undefined || target.name == 'email' )
+		{
+			var matches = validateEmail(data.email);
+			document.getElementById('s_email').src = ( matches ) ? img_good : img_bad;
+			if ( !matches )
+				passed = false;
+		}
+		
+		return passed;
+	}
+	
+	function verify_submit()
+	{
+		if ( verify() )
+			return true;
+		alert($lang.get('login_err_verify_failure'));
+		return false;
+	}
+	
+	function submit_encrypt()
+	{
+		var frm = document.forms [ 'install_login' ];
+		var password = frm.password.value;
+		var pass_conf = frm.password_confirm.value;
+		var crypt_key = frm.crypt_key.value;
+		
+		if ( password != pass_conf )
+			return false;
+		
+		if ( !aes_self_test() )
+			// Return true to prevent form from failing
+			return true;
+			
+		if ( frm.crypt_key.KeyBak )
+		{
+			crypt_key = frm.crypt_key.KeyBak;
+		}
+		frm.crypt_key.KeyBak = crypt_key;
+		
+		password = stringToByteArray(password);
+		crypt_key = hexToByteArray(crypt_key);
+		
+		var crypt_data = rijndaelEncrypt(password, crypt_key, 'ECB');
+		
+		if ( !crypt_data )
+		{
+			alert($lang.get('login_err_rijndael_failed'));
+			return false;
+		}
+	
+		crypt_data = byteArrayToHex(crypt_data);
+		
+		frm.password.value = '';
+		frm.password_confirm.value = '';
+		frm.crypt_key.value = '';
+		frm.crypt_data.value = crypt_data;
+		
+		return true;
+	}
+	
+	addOnloadHook(function()
+		{
+			load_component('crypto');
+			load_component('l10n');
+		});
+	
+	// ]]>
 
 </script>
 
 <form action="install.php?stage=confirm" method="post" name="install_login" onsubmit="return ( verify_submit() && submit_encrypt() );"><?php
-  foreach ( $_POST as $key => &$value )
-  {
-    if ( !preg_match('/^[a-z0-9_]+$/', $key) )
-      die('You idiot hacker...');
-    if ( $key == '_cont' )
-      continue;
-    $value_clean = str_replace(array('\\', '"', '<', '>'), array('\\\\', '\\"', '&lt;', '&gt;'), $value);
-    echo "\n  <input type=\"hidden\" name=\"$key\" value=\"$value_clean\" />";
-  }
-  
-  $https = ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' );
-  $scriptpath_full = 'http' . ( $https ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST'] . scriptPath . '/';
-  ?>
-  
-  <input type="hidden" name="crypt_key" value="<?php echo $install_aes_key; ?>" />
-  <input type="hidden" name="crypt_data" value="" />
-  
-  <table border="0" cellspacing="0" cellpadding="10" style="width: 100%;">
-  
-    <tr>
-      <td style="width: 50%;">
-        <b><?php echo $lang->get('login_field_username'); ?></b>
-      </td>
-      <td style="width: 50%;">
-        <input type="text" tabindex="1" name="username" size="15" onkeyup="verify(this);" />
-      </td>
-      <td>
-        <img id="s_username" alt="Good/bad icon" src="../images/checkbad.png" />
-      </td>
-    </tr>
-    
-    <tr>
-      <td>
-        <b><?php echo $lang->get('login_field_password'); ?></b><br />
-        <?php echo $lang->get('login_aes_blurb'); ?>
-      </td>
-      <td>
-        <input type="password" tabindex="2" name="password" size="15" onkeyup="password_score_field(this); verify(this);" /><br />
-        <br />
-        <div id="pwmeter"></div>
-        <br />
-        <input type="password" tabindex="3" name="password_confirm" size="15" onkeyup="verify(this);" /> <small><?php echo $lang->get('login_field_password_confirm'); ?></small>
-      </td>
-      <td>
-        <img id="s_password" alt="Good/bad icon" src="../images/checkbad.png" />
-      </td>
-    </tr>
-    
-    <tr>
-      <td style="width: 50%;">
-        <b><?php echo $lang->get('login_field_email'); ?></b>
-      </td>
-      <td style="width: 50%;">
-        <input type="text" tabindex="4" name="email" size="30" onkeyup="verify(this);" />
-      </td>
-      <td>
-        <img id="s_email" alt="Good/bad icon" src="../images/checkbad.png" />
-      </td>
-    </tr>
-  
-  </table>
-  
-  <div style="text-align: center;">
-    <input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" />
-  </div>
+	foreach ( $_POST as $key => &$value )
+	{
+		if ( !preg_match('/^[a-z0-9_]+$/', $key) )
+			die('You idiot hacker...');
+		if ( $key == '_cont' )
+			continue;
+		$value_clean = str_replace(array('\\', '"', '<', '>'), array('\\\\', '\\"', '&lt;', '&gt;'), $value);
+		echo "\n  <input type=\"hidden\" name=\"$key\" value=\"$value_clean\" />";
+	}
+	
+	$https = ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' );
+	$scriptpath_full = 'http' . ( $https ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST'] . scriptPath . '/';
+	?>
+	
+	<input type="hidden" name="crypt_key" value="<?php echo $install_aes_key; ?>" />
+	<input type="hidden" name="crypt_data" value="" />
+	
+	<table border="0" cellspacing="0" cellpadding="10" style="width: 100%;">
+	
+		<tr>
+			<td style="width: 50%;">
+				<b><?php echo $lang->get('login_field_username'); ?></b>
+			</td>
+			<td style="width: 50%;">
+				<input type="text" tabindex="1" name="username" size="15" onkeyup="verify(this);" />
+			</td>
+			<td>
+				<img id="s_username" alt="Good/bad icon" src="../images/checkbad.png" />
+			</td>
+		</tr>
+		
+		<tr>
+			<td>
+				<b><?php echo $lang->get('login_field_password'); ?></b><br />
+				<?php echo $lang->get('login_aes_blurb'); ?>
+			</td>
+			<td>
+				<input type="password" tabindex="2" name="password" size="15" onkeyup="password_score_field(this); verify(this);" /><br />
+				<br />
+				<div id="pwmeter"></div>
+				<br />
+				<input type="password" tabindex="3" name="password_confirm" size="15" onkeyup="verify(this);" /> <small><?php echo $lang->get('login_field_password_confirm'); ?></small>
+			</td>
+			<td>
+				<img id="s_password" alt="Good/bad icon" src="../images/checkbad.png" />
+			</td>
+		</tr>
+		
+		<tr>
+			<td style="width: 50%;">
+				<b><?php echo $lang->get('login_field_email'); ?></b>
+			</td>
+			<td style="width: 50%;">
+				<input type="text" tabindex="4" name="email" size="30" onkeyup="verify(this);" />
+			</td>
+			<td>
+				<img id="s_email" alt="Good/bad icon" src="../images/checkbad.png" />
+			</td>
+		</tr>
+	
+	</table>
+	
+	<div style="text-align: center;">
+		<input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" />
+	</div>
 </form>
--- a/install/includes/stages/sysreqs.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/sysreqs.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,7 +14,7 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 require_once(ENANO_ROOT . '/install/includes/libenanoinstall.php');
 
@@ -25,28 +25,28 @@
 
 function run_test($code, $desc, $extended_desc, $warn = false)
 {
-  global $failed, $warned;
-  static $cv = true;
-  $cv = !$cv;
-  $val = eval($code);
-  if($val)
-  {
-    if($cv) $color='CCFFCC'; else $color='AAFFAA';
-    echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc</td><td style='padding-left: 10px;'><img alt='Test passed' src='../images/check.png' /></td></tr>";
-  } elseif(!$val && $warn) {
-    if($cv) $color='FFFFCC'; else $color='FFFFAA';
-    echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc<br /><b>$extended_desc</b></td><td style='padding-left: 10px;'><img alt='Test passed with warning' src='../images/checkunk.png' /></td></tr>";
-    $warned = true;
-  } else {
-    if($cv) $color='FFCCCC'; else $color='FFAAAA';
-    echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc<br /><b>$extended_desc</b></td><td style='padding-left: 10px;'><img alt='Test failed' src='../images/checkbad.png' /></td></tr>";
-    $failed = true;
-  }
+	global $failed, $warned;
+	static $cv = true;
+	$cv = !$cv;
+	$val = eval($code);
+	if($val)
+	{
+		if($cv) $color='CCFFCC'; else $color='AAFFAA';
+		echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc</td><td style='padding-left: 10px;'><img alt='Test passed' src='../images/check.png' /></td></tr>";
+	} elseif(!$val && $warn) {
+		if($cv) $color='FFFFCC'; else $color='FFFFAA';
+		echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc<br /><b>$extended_desc</b></td><td style='padding-left: 10px;'><img alt='Test passed with warning' src='../images/checkunk.png' /></td></tr>";
+		$warned = true;
+	} else {
+		if($cv) $color='FFCCCC'; else $color='FFAAAA';
+		echo "<tr><td style='background-color: #$color; width: 500px; padding: 5px;'>$desc<br /><b>$extended_desc</b></td><td style='padding-left: 10px;'><img alt='Test failed' src='../images/checkbad.png' /></td></tr>";
+		$failed = true;
+	}
 }
 function is_apache()
 {
-  $r = strstr($_SERVER['SERVER_SOFTWARE'], 'Apache') ? true : false;
-  return $r;
+	$r = strstr($_SERVER['SERVER_SOFTWARE'], 'Apache') ? true : false;
+	return $r;
 }
 
 $warnings = array();
@@ -59,39 +59,39 @@
 // Test: PHP
 if ( version_compare(PHP_VERSION, '5.2.0', '>=') )
 {
-  $req_php = 'good';
+	$req_php = 'good';
 }
 else if ( version_compare(PHP_VERSION, '5.0.0', '>=') )
 {
-  $warnings[] = $lang->get('sysreqs_req_help_php', array('php_version' => PHP_VERSION));
-  $req_php = 'warn';
+	$warnings[] = $lang->get('sysreqs_req_help_php', array('php_version' => PHP_VERSION));
+	$req_php = 'warn';
 }
 else
 {
-  $failed = true;
-  $req_php = 'bad';
+	$failed = true;
+	$req_php = 'bad';
 }
 
 // Test: Safe Mode
 $req_safemode = !intval(@ini_get('safe_mode'));
 if ( !$req_safemode )
 {
-  $warnings[] = $lang->get('sysreqs_req_help_safemode');
-  $failed = true;
+	$warnings[] = $lang->get('sysreqs_req_help_safemode');
+	$failed = true;
 }
 
 // Test: MySQL
 $req_mysql = function_exists('mysql_connect');
 if ( $req_mysql )
-  $have_dbms = true;
+	$have_dbms = true;
 
 // Test: PostgreSQL
 $req_pgsql = function_exists('pg_connect');
 if ( $req_pgsql )
-  $have_dbms = true;
+	$have_dbms = true;
 
 if ( !$have_dbms )
-  $failed = true;
+	$failed = true;
 
 // Test: File uploads
 $req_uploads = intval(@ini_get('file_uploads'));
@@ -99,7 +99,7 @@
 // Test: ctype validation
 $req_ctype = function_exists('ctype_digit');
 if ( !$req_ctype )
-  $failed = true;
+	$failed = true;
 
 // Writability test: config
 $req_config_w = write_test('config.new.php');
@@ -114,40 +114,40 @@
 $req_cache_w = write_test('cache');
 
 if ( !$req_config_w || !$req_htaccess_w || !$req_files_w || !$req_cache_w )
-  $warnings[] = $lang->get('sysreqs_req_help_writable');
+	$warnings[] = $lang->get('sysreqs_req_help_writable');
 
 if ( !$req_config_w )
-  $failed = true;
+	$failed = true;
 
 // Extension test: GD
 $req_gd = function_exists('imagecreatefrompng') && function_exists('getimagesize') && function_exists('imagecreatetruecolor') && function_exists('imagecopyresampled');
 if ( !$req_gd )
-  $warnings[] = $lang->get('sysreqs_req_help_gd2');
+	$warnings[] = $lang->get('sysreqs_req_help_gd2');
 
 // FS test: ImageMagick
 $req_imagick = which('convert');
 if ( !$req_imagick )
-  $warnings[] = $lang->get('sysreqs_req_help_imagemagick');
+	$warnings[] = $lang->get('sysreqs_req_help_imagemagick');
 
 $crypto_backend = install_get_crypto_backend();
 
 if ( $crypto_backend == 'none' )
-  $warnings[] = $lang->get('sysreqs_req_help_crypto_none');
+	$warnings[] = $lang->get('sysreqs_req_help_crypto_none');
 else if ( $crypto_backend == 'bcmath' )
-  $warnings[] = $lang->get('sysreqs_req_help_crypto_bcmath');
+	$warnings[] = $lang->get('sysreqs_req_help_crypto_bcmath');
 
 ?>
 
 <div style="float: right; padding-top: 10px;">
-  <form action="install.php?stage=sysreqs" method="post">
-  <?php
-    echo '<input type="hidden" name="language" value="' . $lang_id . '" />';
-  ?>
-  <button style="display: block; padding-bottom: 3px;">
-  <img alt=" " src="images/recheck.png" style="position: relative; top: 3px; left: -2px;" />
-    <?php echo $lang->get('sysreqs_btn_refresh'); ?>
-  </button>
-  </form>
+	<form action="install.php?stage=sysreqs" method="post">
+	<?php
+		echo '<input type="hidden" name="language" value="' . $lang_id . '" />';
+	?>
+	<button style="display: block; padding-bottom: 3px;">
+	<img alt=" " src="images/recheck.png" style="position: relative; top: 3px; left: -2px;" />
+		<?php echo $lang->get('sysreqs_btn_refresh'); ?>
+	</button>
+	</form>
 </div>
 
 <h3><?php echo $lang->get('sysreqs_heading'); ?></h3>
@@ -157,48 +157,48 @@
 
 <form action="install.php?stage=database" method="post">
 <?php
-  echo '<input type="hidden" name="language" value="' . $lang_id . '" />';
+	echo '<input type="hidden" name="language" value="' . $lang_id . '" />';
 ?>
 
 <?php
 if ( !empty($warnings) ):
 ?>
-  <div class="sysreqs_warning">
-    <h3><?php echo $lang->get('sysreqs_summary_warn_title'); ?></h3>
-    <p><?php echo $lang->get('sysreqs_summary_warn_body'); ?></p>
-    <ul>
-      <li><?php echo implode("</li>\n      <li>", $warnings); ?></li>
-    </ul>
-  </div>
+	<div class="sysreqs_warning">
+		<h3><?php echo $lang->get('sysreqs_summary_warn_title'); ?></h3>
+		<p><?php echo $lang->get('sysreqs_summary_warn_body'); ?></p>
+		<ul>
+			<li><?php echo implode("</li>\n      <li>", $warnings); ?></li>
+		</ul>
+	</div>
 <?php
 endif;
 
 if ( !$have_dbms ):
 ?>
-  <div class="sysreqs_error">
-    <h3><?php echo $lang->get('sysreqs_err_no_dbms_title'); ?></h3>
-    <p><?php echo $lang->get('sysreqs_err_no_dbms_body'); ?></p>
-  </div>
+	<div class="sysreqs_error">
+		<h3><?php echo $lang->get('sysreqs_err_no_dbms_title'); ?></h3>
+		<p><?php echo $lang->get('sysreqs_err_no_dbms_body'); ?></p>
+	</div>
 <?php
 endif;
 if ( empty($warnings) && !$failed ):
 ?>
-  <div class="sysreqs_success">
-    <h3><?php echo $lang->get('sysreqs_summary_pass_title'); ?></h3>
-    <p><?php echo $lang->get('sysreqs_summary_pass_body'); ?></p>
-  </div>
-  <div style="text-align: center;">
-    <input type="submit" value="<?php echo $lang->get('meta_btn_continue'); ?>" />
-  </div>
+	<div class="sysreqs_success">
+		<h3><?php echo $lang->get('sysreqs_summary_pass_title'); ?></h3>
+		<p><?php echo $lang->get('sysreqs_summary_pass_body'); ?></p>
+	</div>
+	<div style="text-align: center;">
+		<input type="submit" value="<?php echo $lang->get('meta_btn_continue'); ?>" />
+	</div>
 <?php
 endif;
 
 if ( $failed ):
 ?>
-  <div class="sysreqs_error">
-    <h3><?php echo $lang->get('sysreqs_summary_fail_title'); ?></h3>
-    <p><?php echo $lang->get('sysreqs_summary_fail_body'); ?></p>
-  </div>
+	<div class="sysreqs_error">
+		<h3><?php echo $lang->get('sysreqs_summary_fail_title'); ?></h3>
+		<p><?php echo $lang->get('sysreqs_summary_fail_body'); ?></p>
+	</div>
 <?php
 endif;        
 ?>
@@ -206,189 +206,189 @@
 <table border="0" cellspacing="0" cellpadding="0" class="sysreqs">
 
 <tr>
-  <th colspan="2"><?php echo $lang->get('sysreqs_heading_serverenv'); ?></th>
+	<th colspan="2"><?php echo $lang->get('sysreqs_heading_serverenv'); ?></th>
 </tr>
 
 <tr>
-  <td><?php echo $lang->get('sysreqs_req_apache'); ?></td>
-  <?php
-  if ( $req_apache ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_found') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
-  endif;
-  ?>
+	<td><?php echo $lang->get('sysreqs_req_apache'); ?></td>
+	<?php
+	if ( $req_apache ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_found') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td><?php echo $lang->get('sysreqs_req_php'); ?></td>
-  <td class="<?php echo $req_php; ?>">v<?php echo PHP_VERSION; ?></td>
+	<td><?php echo $lang->get('sysreqs_req_php'); ?></td>
+	<td class="<?php echo $req_php; ?>">v<?php echo PHP_VERSION; ?></td>
 </tr>
 
 <tr>
-  <td><?php echo $lang->get('sysreqs_req_safemode'); ?></td>
-  <?php
-  if ( $req_safemode ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_disabled') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_enabled') . '</td>';
-  endif;
-  ?>
+	<td><?php echo $lang->get('sysreqs_req_safemode'); ?></td>
+	<?php
+	if ( $req_safemode ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_disabled') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_enabled') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td><?php echo $lang->get('sysreqs_req_uploads'); ?></td>
-  <?php
-  if ( $req_uploads ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_enabled') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_disabled') . '</td>';
-  endif;
-  ?>
+	<td><?php echo $lang->get('sysreqs_req_uploads'); ?></td>
+	<?php
+	if ( $req_uploads ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_enabled') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_disabled') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td><?php echo $lang->get('sysreqs_req_ctype'); ?></td>
-  <?php
-  if ( $req_ctype ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_unsupported') . '</td>';
-  endif;
-  ?>
+	<td><?php echo $lang->get('sysreqs_req_ctype'); ?></td>
+	<?php
+	if ( $req_ctype ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_unsupported') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td>
-    <?php echo $lang->get('sysreqs_req_crypto'); ?>
-  </td>
-  <?php
-  if ( in_array($crypto_backend, array('bcmath', 'bigint', 'gmp')) )
-  {
-    echo '<td class="good">' . $lang->get("sysreqs_req_{$crypto_backend}") . '</td>';
-  }
-  else
-  {
-    echo '<td class="bad">' . $lang->get("sysreqs_req_notfound") . '</td>';
-  }
-  ?>
+	<td>
+		<?php echo $lang->get('sysreqs_req_crypto'); ?>
+	</td>
+	<?php
+	if ( in_array($crypto_backend, array('bcmath', 'bigint', 'gmp')) )
+	{
+		echo '<td class="good">' . $lang->get("sysreqs_req_{$crypto_backend}") . '</td>';
+	}
+	else
+	{
+		echo '<td class="bad">' . $lang->get("sysreqs_req_notfound") . '</td>';
+	}
+	?>
 </tr>
 
 <!-- Database -->
 
 <tr>
-  <th colspan="2"><?php echo $lang->get('sysreqs_heading_dbms'); ?></th>
+	<th colspan="2"><?php echo $lang->get('sysreqs_heading_dbms'); ?></th>
 </tr>
 
 <tr>
-  <td><?php echo $lang->get('sysreqs_req_mysql'); ?></td>
-  <?php
-  if ( $req_mysql ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
-  endif;
-  ?>
+	<td><?php echo $lang->get('sysreqs_req_mysql'); ?></td>
+	<?php
+	if ( $req_mysql ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td><?php echo $lang->get('sysreqs_req_postgresql'); ?></td>
-  <?php
-  if ( $req_pgsql ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
-  endif;
-  ?>
+	<td><?php echo $lang->get('sysreqs_req_postgresql'); ?></td>
+	<?php
+	if ( $req_pgsql ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <th colspan="2"><?php echo $lang->get('sysreqs_heading_files'); ?></th>
+	<th colspan="2"><?php echo $lang->get('sysreqs_heading_files'); ?></th>
 </tr>
 
 <tr>
-  <td>
-    <?php echo $lang->get('sysreqs_req_config_writable'); ?>
-  </td>
-  <?php
-  if ( $req_config_w ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_writable') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_unwritable') . '</td>';
-  endif;
-  ?>
+	<td>
+		<?php echo $lang->get('sysreqs_req_config_writable'); ?>
+	</td>
+	<?php
+	if ( $req_config_w ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_writable') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_unwritable') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td>
-    <?php echo $lang->get('sysreqs_req_htaccess_writable'); ?><br />
-    <small><?php echo $lang->get('sysreqs_req_hint_htaccess_writable'); ?></small>
-  </td>
-  <?php
-  if ( $req_htaccess_w ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_writable') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_unwritable') . '</td>';
-  endif;
-  ?>
+	<td>
+		<?php echo $lang->get('sysreqs_req_htaccess_writable'); ?><br />
+		<small><?php echo $lang->get('sysreqs_req_hint_htaccess_writable'); ?></small>
+	</td>
+	<?php
+	if ( $req_htaccess_w ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_writable') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_unwritable') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td>
-    <?php echo $lang->get('sysreqs_req_files_writable'); ?>
-  </td>
-  <?php
-  if ( $req_files_w ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_writable') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_unwritable') . '</td>';
-  endif;
-  ?>
+	<td>
+		<?php echo $lang->get('sysreqs_req_files_writable'); ?>
+	</td>
+	<?php
+	if ( $req_files_w ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_writable') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_unwritable') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td>
-    <?php echo $lang->get('sysreqs_req_cache_writable'); ?>
-  </td>
-  <?php
-  if ( $req_cache_w ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_writable') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_unwritable') . '</td>';
-  endif;
-  ?>
+	<td>
+		<?php echo $lang->get('sysreqs_req_cache_writable'); ?>
+	</td>
+	<?php
+	if ( $req_cache_w ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_writable') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_unwritable') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <th colspan="2"><?php echo $lang->get('sysreqs_heading_images'); ?></th>
+	<th colspan="2"><?php echo $lang->get('sysreqs_heading_images'); ?></th>
 </tr>
 
 <tr>
-  <td>
-    <?php echo $lang->get('sysreqs_req_gd2'); ?><br />
-    <small><?php echo $lang->get('sysreqs_req_hint_gd2'); ?></small>
-  </td>
-  <?php
-  if ( $req_gd ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
-  endif;
-  ?>
+	<td>
+		<?php echo $lang->get('sysreqs_req_gd2'); ?><br />
+		<small><?php echo $lang->get('sysreqs_req_hint_gd2'); ?></small>
+	</td>
+	<?php
+	if ( $req_gd ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_supported') . '</td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
+	endif;
+	?>
 </tr>
 
 <tr>
-  <td>
-    <?php echo $lang->get('sysreqs_req_imagemagick'); ?><br />
-    <small><?php echo $lang->get('sysreqs_req_hint_imagemagick'); ?></small>
-  </td>
-  <?php
-  if ( $req_imagick ):
-    echo '<td class="good">' . $lang->get('sysreqs_req_found') . ' <small>(' . htmlspecialchars($req_imagick) . ')</small></td>';
-  else:
-    echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
-  endif;
-  ?>
+	<td>
+		<?php echo $lang->get('sysreqs_req_imagemagick'); ?><br />
+		<small><?php echo $lang->get('sysreqs_req_hint_imagemagick'); ?></small>
+	</td>
+	<?php
+	if ( $req_imagick ):
+		echo '<td class="good">' . $lang->get('sysreqs_req_found') . ' <small>(' . htmlspecialchars($req_imagick) . ')</small></td>';
+	else:
+		echo '<td class="bad">' . $lang->get('sysreqs_req_notfound') . '</td>';
+	endif;
+	?>
 </tr>
 
 </table>
@@ -396,20 +396,20 @@
 <?php
 if ( !$failed ):
 ?>
-    <table border="0">
-    <tr>
-      <td>
-        <input type="submit" value="<?php echo $lang->get('meta_btn_continue'); ?>" />
-      </td>
-      <td>
-        <p>
-          <span style="font-weight: bold;"><?php echo $lang->get('meta_lbl_before_continue'); ?></span><br />
-          &bull; <?php echo $lang->get('sysreqs_objective_scalebacks'); ?><br />
-          &bull; <?php echo $lang->get('license_objective_have_db_info'); ?>
-        </p>
-      </td>
-    </tr>
-    </table>
+		<table border="0">
+		<tr>
+			<td>
+				<input type="submit" value="<?php echo $lang->get('meta_btn_continue'); ?>" />
+			</td>
+			<td>
+				<p>
+					<span style="font-weight: bold;"><?php echo $lang->get('meta_lbl_before_continue'); ?></span><br />
+					&bull; <?php echo $lang->get('sysreqs_objective_scalebacks'); ?><br />
+					&bull; <?php echo $lang->get('license_objective_have_db_info'); ?>
+				</p>
+			</td>
+		</tr>
+		</table>
 <?php
 endif;
 ?>
--- a/install/includes/stages/website.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/stages/website.php	Sun Mar 28 23:10:46 2010 -0400
@@ -14,7 +14,7 @@
  */
 
 if ( !defined('IN_ENANO_INSTALL') )
-  die();
+	die();
 
 // Note: this is called from database_*.php, not directly from install.php
 
@@ -24,329 +24,329 @@
 ?>
 
 <script type="text/javascript">
-  // <![CDATA[
-  function ajaxMrwTest()
-  {
-    install_set_ajax_loading();
-    // Send a series of tests to the server, and if we get an "expected" response
-    setTimeout("ajaxGet(scriptPath + '/install/rewrite', __ajaxMrwTest_chain_rewrite);", 750);
-  }
-  var __ajaxMrwTest_chain_rewrite = function()
-  {
-    if ( ajax.readyState == 4 )
-    {
-      if ( ajax.responseText == 'good_rewrite' )
-      {
-        ajaxMrwSet('rewrite');
-      }
-      else
-      {
-        ajaxGet(scriptPath + '/install/install.php/shortened?do=modrewrite_test', __ajaxMrwTest_chain_shortened);
-      }
-    }
-  }
-  var __ajaxMrwTest_chain_shortened = function()
-  {
-    if ( ajax.readyState == 4 )
-    {
-      if ( ajax.responseText == 'good_shortened' )
-      {
-        ajaxMrwSet('shortened');
-      }
-      else
-      {
-        ajaxGet(scriptPath + '/install/install.php?/tiny&do=modrewrite_test', __ajaxMrwTest_chain_tiny);
-      }
-    }
-  }
-  var __ajaxMrwTest_chain_tiny = function()
-  {
-    if ( ajax.readyState == 4 )
-    {
-      if ( ajax.responseText == 'good_tiny' )
-      {
-        ajaxMrwSet('tiny');
-      }
-      else
-      {
-        ajaxGet(scriptPath + '/install/install.php?do=modrewrite_test&str=standard', __ajaxMrwTest_chain_standard);
-      }
-    }
-  }
-  var __ajaxMrwTest_chain_standard = function()
-  {
-    if ( ajax.readyState == 4 )
-    {
-      if ( ajax.responseText == 'good_standard' )
-      {
-        ajaxMrwSet('standard');
-      }
-      else
-      {
-        install_unset_ajax_loading();
-        new messagebox(MB_OK | MB_ICONSTOP, $lang.get('website_msg_ajax_test_fail_title'), $lang.get('website_msg_ajax_test_fail_body'));
-      }
-    }
-  }
-  function ajaxMrwSet(level)
-  {
-    install_unset_ajax_loading();
-    if ( !in_array(level, ['rewrite', 'shortened', 'standard', 'tiny']) )
-      return false;
-    
-    document.getElementById('url_radio_rewrite').checked = false;
-    document.getElementById('url_radio_shortened').checked = false;
-    document.getElementById('url_radio_standard').checked = false;
-    document.getElementById('url_radio_tiny').checked = false;
-    document.getElementById('url_radio_' + level).checked = true;
-    document.getElementById('url_radio_' + level).focus();
-    
-    switch ( level )
-    {
-      case 'rewrite':
-        var str = $lang.get('website_msg_bestmethod_rewrite');
-        break;
-      case 'shortened':
-        var str = $lang.get('website_msg_bestmethod_shortened');
-        break;
-      case 'tiny':
-        var str = $lang.get('website_msg_bestmethod_tiny');
-        break;
-      case 'standard':
-        var str = $lang.get('website_msg_bestmethod_standard');
-        break;
-    }
-    document.getElementById('mrw_report').className = 'info-box-mini';
-    document.getElementById('mrw_report').innerHTML = str;
-  }
-  
-  function verify()
-  {
-    var frm = document.forms['install_website'];
-    var fail = false;
-    if ( frm.site_name.value == '' )
-    {
-      fail = true;
-      $(frm.site_name).effect("shake", {}, 750);
-      frm.site_name.focus();
-    }
-    if ( frm.site_desc.value == '' )
-    {
-      $(frm.site_desc).effect("shake", {}, 750);
-      if ( !fail )
-        frm.site_desc.focus();
-      fail = true;
-    }
-    if ( frm.copyright.value == '' )
-    {
-      $(frm.copyright).effect("shake", {}, 750);
-      if ( !fail )
-        frm.copyright.focus();
-      fail = true;
-    }
-    return ( !fail );
-  }
-  // ]]>
+	// <![CDATA[
+	function ajaxMrwTest()
+	{
+		install_set_ajax_loading();
+		// Send a series of tests to the server, and if we get an "expected" response
+		setTimeout("ajaxGet(scriptPath + '/install/rewrite', __ajaxMrwTest_chain_rewrite);", 750);
+	}
+	var __ajaxMrwTest_chain_rewrite = function()
+	{
+		if ( ajax.readyState == 4 )
+		{
+			if ( ajax.responseText == 'good_rewrite' )
+			{
+				ajaxMrwSet('rewrite');
+			}
+			else
+			{
+				ajaxGet(scriptPath + '/install/install.php/shortened?do=modrewrite_test', __ajaxMrwTest_chain_shortened);
+			}
+		}
+	}
+	var __ajaxMrwTest_chain_shortened = function()
+	{
+		if ( ajax.readyState == 4 )
+		{
+			if ( ajax.responseText == 'good_shortened' )
+			{
+				ajaxMrwSet('shortened');
+			}
+			else
+			{
+				ajaxGet(scriptPath + '/install/install.php?/tiny&do=modrewrite_test', __ajaxMrwTest_chain_tiny);
+			}
+		}
+	}
+	var __ajaxMrwTest_chain_tiny = function()
+	{
+		if ( ajax.readyState == 4 )
+		{
+			if ( ajax.responseText == 'good_tiny' )
+			{
+				ajaxMrwSet('tiny');
+			}
+			else
+			{
+				ajaxGet(scriptPath + '/install/install.php?do=modrewrite_test&str=standard', __ajaxMrwTest_chain_standard);
+			}
+		}
+	}
+	var __ajaxMrwTest_chain_standard = function()
+	{
+		if ( ajax.readyState == 4 )
+		{
+			if ( ajax.responseText == 'good_standard' )
+			{
+				ajaxMrwSet('standard');
+			}
+			else
+			{
+				install_unset_ajax_loading();
+				new messagebox(MB_OK | MB_ICONSTOP, $lang.get('website_msg_ajax_test_fail_title'), $lang.get('website_msg_ajax_test_fail_body'));
+			}
+		}
+	}
+	function ajaxMrwSet(level)
+	{
+		install_unset_ajax_loading();
+		if ( !in_array(level, ['rewrite', 'shortened', 'standard', 'tiny']) )
+			return false;
+		
+		document.getElementById('url_radio_rewrite').checked = false;
+		document.getElementById('url_radio_shortened').checked = false;
+		document.getElementById('url_radio_standard').checked = false;
+		document.getElementById('url_radio_tiny').checked = false;
+		document.getElementById('url_radio_' + level).checked = true;
+		document.getElementById('url_radio_' + level).focus();
+		
+		switch ( level )
+		{
+			case 'rewrite':
+				var str = $lang.get('website_msg_bestmethod_rewrite');
+				break;
+			case 'shortened':
+				var str = $lang.get('website_msg_bestmethod_shortened');
+				break;
+			case 'tiny':
+				var str = $lang.get('website_msg_bestmethod_tiny');
+				break;
+			case 'standard':
+				var str = $lang.get('website_msg_bestmethod_standard');
+				break;
+		}
+		document.getElementById('mrw_report').className = 'info-box-mini';
+		document.getElementById('mrw_report').innerHTML = str;
+	}
+	
+	function verify()
+	{
+		var frm = document.forms['install_website'];
+		var fail = false;
+		if ( frm.site_name.value == '' )
+		{
+			fail = true;
+			$(frm.site_name).effect("shake", {}, 750);
+			frm.site_name.focus();
+		}
+		if ( frm.site_desc.value == '' )
+		{
+			$(frm.site_desc).effect("shake", {}, 750);
+			if ( !fail )
+				frm.site_desc.focus();
+			fail = true;
+		}
+		if ( frm.copyright.value == '' )
+		{
+			$(frm.copyright).effect("shake", {}, 750);
+			if ( !fail )
+				frm.copyright.focus();
+			fail = true;
+		}
+		return ( !fail );
+	}
+	// ]]>
 </script>
 
 <form action="install.php?stage=login" method="post" name="install_website" onsubmit="return verify();"><?php
-  foreach ( $_POST as $key => &$value )
-  {
-    if ( !preg_match('/^[a-z0-9_]+$/', $key) )
-      die('You idiot hacker...');
-    if ( $key == '_cont' )
-      continue;
-    $value_clean = str_replace(array('\\', '"', '<', '>'), array('\\\\', '\\"', '&lt;', '&gt;'), $value);
-    echo "\n  <input type=\"hidden\" name=\"$key\" value=\"$value_clean\" />";
-  }
-  
-  $https = ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' );
-  $scriptpath_full = 'http' . ( $https ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST'] . scriptPath . '/';
-  ?>
-  
-  <?php
-  $patch_necessary = ( ( strtolower(PHP_OS) == 'win32' || strtolower(PHP_OS) == 'winnt' ) && strstr(@$_SERVER['SERVER_SOFTWARE'], 'Apache/2.2') );
-  if ( defined('WINDOWS_MOD_REWRITE_WORKAROUNDS') )
-  {
-    ?>
-      <div class="usermessage">
-        <b><?php echo $lang->get('website_msg_modrewrite_enabled'); ?></b><br />
-        <?php if ( $patch_necessary ): ?>
-          <?php echo $lang->get('website_msg_modrewrite_necessary'); ?>
-        <?php else: ?>
-          <?php echo $lang->get('website_msg_modrewrite_unnecessary'); ?>
-        <?php endif; ?>
-      </div>
-    <?php
-  }
-  else
-  {
-    if ( $patch_necessary ):
-    ?>
-      <div class="usermessage">
-        <b><?php echo $lang->get('website_msg_modrewrite_disabled'); ?></b><br />
-        <?php echo $lang->get('website_msg_modrewrite_maybeneeded'); ?>
-      </div>
-    <?php
-    endif;
-  }
-  ?>
-  
-  <table border="0" cellspacing="0" cellpadding="10">
-    <tr>
-      <td>
-        <b><?php echo $lang->get('website_field_name'); ?></b><br />
-        <span id="hint_site_name" class="fieldtip"><?php echo $lang->get('website_field_name_hint'); ?></span>
-      </td>
-      <td style="width: 50%;">
-        <input type="text" name="site_name" size="50" tabindex="1" />
-      </td>
-    </tr>
-    
-    <tr>
-      <td>
-        <b><?php echo $lang->get('website_field_desc'); ?></b><br />
-        <span id="hint_site_desc" class="fieldtip"><?php echo $lang->get('website_field_desc_hint'); ?></span>
-      </td>
-      <td>
-        <input type="text" name="site_desc" size="50" tabindex="2" />
-      </td>
-    </tr>
-    
-    <tr>
-      <td>
-        <b><?php echo $lang->get('website_field_copyright'); ?></b><br />
-        <span id="hint_copyright" class="fieldtip"><?php echo $lang->get('website_field_copyright_hint'); ?></span>
-      </td>
-      <td>
-        <input type="text" name="copyright" size="50" tabindex="3" />
-      </td>
-    </tr>
-    
-    <tr>
-      <td>
-        <b><?php echo $lang->get('website_field_startwith'); ?></b>
-      </td>
-      <td>
-      
-        <table border="0">
-          <tr>
-            <td>
-      
-              <label>
-                <input type="radio" name="default_content_type" value="blank" checked="checked" tabindex="4" />
-                <?php echo $lang->get('website_field_startwith_blank'); ?>
-              </label>
-              <span class="fieldtip" id="hint_default_content_type_blank">
-                <p><?php echo $lang->get('website_field_startwith_blank_hint'); ?></p>
-              </span>
-              
-              <br />
-              
-              <label>
-                <input type="radio" name="default_content_type" value="tutorial" tabindex="4" />
-                <?php echo $lang->get('website_field_startwith_tutorial'); ?>
-              </label>
-              <span class="fieldtip" id="hint_default_content_type_tutorial">
-                <p><?php echo $lang->get('website_field_startwith_tutorial_hint'); ?></p>
-              </span>
-              
-            </td>
-          </tr>
-        </table>
-        
-      </td>
-    </tr>
-    
-    <tr>
-      <td valign="top">
-        <b><?php echo $lang->get('website_field_urlscheme'); ?></b><br />
-        <?php echo $lang->get('website_field_urlscheme_hint'); ?>
-      </td>
-      <td>
-      
-        <table border="0" cellpadding="10" cellspacing="0">
-          <tr>
-            <td valign="top">
-              <input type="radio" name="url_scheme" value="standard" id="url_radio_standard" tabindex="6" />
-            </td>
-            <td>
-              <label for="url_radio_standard">
-                <b><?php echo $lang->get('website_field_urlscheme_opt_standard'); ?></b>
-              </label>
-              <span class="fieldtip" id="hint_url_scheme_standard">
-                <p><?php echo $lang->get('website_field_urlscheme_opt_standard_hint'); ?></p>
-                <p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'index.php?title=Page'; ?></tt></small></p>
-              </span>
-            </td>
-          </tr>
-        </table>
-        
-        <table border="0" cellpadding="10" cellspacing="0">
-          <tr>
-            <td valign="top">
-              <input type="radio" checked="checked" name="url_scheme" value="shortened" id="url_radio_shortened" tabindex="6" />
-            </td>
-            <td>
-              <label for="url_radio_shortened">
-                <b><?php echo $lang->get('website_field_urlscheme_opt_shortened'); ?></b>
-              </label>
-              <span class="fieldtip" id="hint_url_scheme_shortened">
-                <p><?php echo $lang->get('website_field_urlscheme_opt_shortened_hint'); ?></p>
-                <p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'index.php/Page'; ?></tt></small></p>
-              </span>
-            </td>
-          </tr>
-        </table>
-        
-        <table border="0" cellpadding="10" cellspacing="0">
-          <tr>
-            <td valign="top">
-              <input type="radio" name="url_scheme" value="rewrite" id="url_radio_rewrite" tabindex="6" />
-            </td>
-            <td>
-              <label for="url_radio_rewrite">
-                <b><?php echo $lang->get('website_field_urlscheme_opt_rewrite'); ?></b>
-              </label>
-              <span id="hint_url_scheme_rewrite" class="fieldtip">
-                <p><?php echo $lang->get('website_field_urlscheme_opt_rewrite_hint'); ?></p>
-                <p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'Page'; ?></tt></small></p>
-              </span>
-            </td>
-          </tr>
-        </table>
-        
-        <table border="0" cellpadding="10" cellspacing="0">
-          <tr>
-            <td valign="top">
-              <input type="radio" name="url_scheme" value="tiny" id="url_radio_tiny" tabindex="6" />
-            </td>
-            <td>
-              <label for="url_radio_tiny">
-                <b><?php echo $lang->get('website_field_urlscheme_opt_tiny'); ?></b>
-              </label>
-              <span id="hint_url_scheme_tiny" class="fieldtip">
-                <p><?php echo $lang->get('website_field_urlscheme_opt_tiny_hint'); ?></p>
-                <p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . '?/Page'; ?></tt></small></p>
-              </span>
-            </td>
-          </tr>
-        </table>
-        
-        <p>
-          <a href="#mrw_scan" onclick="ajaxMrwTest(); return false;" tabindex="5"><?php echo $lang->get('website_btn_urlscheme_detect'); ?></a>
-        </p>
-        
-        <div id="mrw_report"></div>
-        
-      </td>
-    </tr>
-    
-  </table>
-  
-  <div style="text-align: center;">
-    <input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" tabindex="7" />
-  </div>
-  
+	foreach ( $_POST as $key => &$value )
+	{
+		if ( !preg_match('/^[a-z0-9_]+$/', $key) )
+			die('You idiot hacker...');
+		if ( $key == '_cont' )
+			continue;
+		$value_clean = str_replace(array('\\', '"', '<', '>'), array('\\\\', '\\"', '&lt;', '&gt;'), $value);
+		echo "\n  <input type=\"hidden\" name=\"$key\" value=\"$value_clean\" />";
+	}
+	
+	$https = ( isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' );
+	$scriptpath_full = 'http' . ( $https ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST'] . scriptPath . '/';
+	?>
+	
+	<?php
+	$patch_necessary = ( ( strtolower(PHP_OS) == 'win32' || strtolower(PHP_OS) == 'winnt' ) && strstr(@$_SERVER['SERVER_SOFTWARE'], 'Apache/2.2') );
+	if ( defined('WINDOWS_MOD_REWRITE_WORKAROUNDS') )
+	{
+		?>
+			<div class="usermessage">
+				<b><?php echo $lang->get('website_msg_modrewrite_enabled'); ?></b><br />
+				<?php if ( $patch_necessary ): ?>
+					<?php echo $lang->get('website_msg_modrewrite_necessary'); ?>
+				<?php else: ?>
+					<?php echo $lang->get('website_msg_modrewrite_unnecessary'); ?>
+				<?php endif; ?>
+			</div>
+		<?php
+	}
+	else
+	{
+		if ( $patch_necessary ):
+		?>
+			<div class="usermessage">
+				<b><?php echo $lang->get('website_msg_modrewrite_disabled'); ?></b><br />
+				<?php echo $lang->get('website_msg_modrewrite_maybeneeded'); ?>
+			</div>
+		<?php
+		endif;
+	}
+	?>
+	
+	<table border="0" cellspacing="0" cellpadding="10">
+		<tr>
+			<td>
+				<b><?php echo $lang->get('website_field_name'); ?></b><br />
+				<span id="hint_site_name" class="fieldtip"><?php echo $lang->get('website_field_name_hint'); ?></span>
+			</td>
+			<td style="width: 50%;">
+				<input type="text" name="site_name" size="50" tabindex="1" />
+			</td>
+		</tr>
+		
+		<tr>
+			<td>
+				<b><?php echo $lang->get('website_field_desc'); ?></b><br />
+				<span id="hint_site_desc" class="fieldtip"><?php echo $lang->get('website_field_desc_hint'); ?></span>
+			</td>
+			<td>
+				<input type="text" name="site_desc" size="50" tabindex="2" />
+			</td>
+		</tr>
+		
+		<tr>
+			<td>
+				<b><?php echo $lang->get('website_field_copyright'); ?></b><br />
+				<span id="hint_copyright" class="fieldtip"><?php echo $lang->get('website_field_copyright_hint'); ?></span>
+			</td>
+			<td>
+				<input type="text" name="copyright" size="50" tabindex="3" />
+			</td>
+		</tr>
+		
+		<tr>
+			<td>
+				<b><?php echo $lang->get('website_field_startwith'); ?></b>
+			</td>
+			<td>
+			
+				<table border="0">
+					<tr>
+						<td>
+			
+							<label>
+								<input type="radio" name="default_content_type" value="blank" checked="checked" tabindex="4" />
+								<?php echo $lang->get('website_field_startwith_blank'); ?>
+							</label>
+							<span class="fieldtip" id="hint_default_content_type_blank">
+								<p><?php echo $lang->get('website_field_startwith_blank_hint'); ?></p>
+							</span>
+							
+							<br />
+							
+							<label>
+								<input type="radio" name="default_content_type" value="tutorial" tabindex="4" />
+								<?php echo $lang->get('website_field_startwith_tutorial'); ?>
+							</label>
+							<span class="fieldtip" id="hint_default_content_type_tutorial">
+								<p><?php echo $lang->get('website_field_startwith_tutorial_hint'); ?></p>
+							</span>
+							
+						</td>
+					</tr>
+				</table>
+				
+			</td>
+		</tr>
+		
+		<tr>
+			<td valign="top">
+				<b><?php echo $lang->get('website_field_urlscheme'); ?></b><br />
+				<?php echo $lang->get('website_field_urlscheme_hint'); ?>
+			</td>
+			<td>
+			
+				<table border="0" cellpadding="10" cellspacing="0">
+					<tr>
+						<td valign="top">
+							<input type="radio" name="url_scheme" value="standard" id="url_radio_standard" tabindex="6" />
+						</td>
+						<td>
+							<label for="url_radio_standard">
+								<b><?php echo $lang->get('website_field_urlscheme_opt_standard'); ?></b>
+							</label>
+							<span class="fieldtip" id="hint_url_scheme_standard">
+								<p><?php echo $lang->get('website_field_urlscheme_opt_standard_hint'); ?></p>
+								<p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'index.php?title=Page'; ?></tt></small></p>
+							</span>
+						</td>
+					</tr>
+				</table>
+				
+				<table border="0" cellpadding="10" cellspacing="0">
+					<tr>
+						<td valign="top">
+							<input type="radio" checked="checked" name="url_scheme" value="shortened" id="url_radio_shortened" tabindex="6" />
+						</td>
+						<td>
+							<label for="url_radio_shortened">
+								<b><?php echo $lang->get('website_field_urlscheme_opt_shortened'); ?></b>
+							</label>
+							<span class="fieldtip" id="hint_url_scheme_shortened">
+								<p><?php echo $lang->get('website_field_urlscheme_opt_shortened_hint'); ?></p>
+								<p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'index.php/Page'; ?></tt></small></p>
+							</span>
+						</td>
+					</tr>
+				</table>
+				
+				<table border="0" cellpadding="10" cellspacing="0">
+					<tr>
+						<td valign="top">
+							<input type="radio" name="url_scheme" value="rewrite" id="url_radio_rewrite" tabindex="6" />
+						</td>
+						<td>
+							<label for="url_radio_rewrite">
+								<b><?php echo $lang->get('website_field_urlscheme_opt_rewrite'); ?></b>
+							</label>
+							<span id="hint_url_scheme_rewrite" class="fieldtip">
+								<p><?php echo $lang->get('website_field_urlscheme_opt_rewrite_hint'); ?></p>
+								<p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . 'Page'; ?></tt></small></p>
+							</span>
+						</td>
+					</tr>
+				</table>
+				
+				<table border="0" cellpadding="10" cellspacing="0">
+					<tr>
+						<td valign="top">
+							<input type="radio" name="url_scheme" value="tiny" id="url_radio_tiny" tabindex="6" />
+						</td>
+						<td>
+							<label for="url_radio_tiny">
+								<b><?php echo $lang->get('website_field_urlscheme_opt_tiny'); ?></b>
+							</label>
+							<span id="hint_url_scheme_tiny" class="fieldtip">
+								<p><?php echo $lang->get('website_field_urlscheme_opt_tiny_hint'); ?></p>
+								<p><small><b><?php echo $lang->get('website_field_urlscheme_lbl_example'); ?></b> <tt><?php echo $scriptpath_full . '?/Page'; ?></tt></small></p>
+							</span>
+						</td>
+					</tr>
+				</table>
+				
+				<p>
+					<a href="#mrw_scan" onclick="ajaxMrwTest(); return false;" tabindex="5"><?php echo $lang->get('website_btn_urlscheme_detect'); ?></a>
+				</p>
+				
+				<div id="mrw_report"></div>
+				
+			</td>
+		</tr>
+		
+	</table>
+	
+	<div style="text-align: center;">
+		<input type="submit" name="_cont" value="<?php echo $lang->get('meta_btn_continue'); ?>" tabindex="7" />
+	</div>
+	
 </form>
 
--- a/install/includes/ui.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/includes/ui.php	Sun Mar 28 23:10:46 2010 -0400
@@ -22,227 +22,227 @@
 
 class Enano_Installer_UI
 {
-  /**
-   * The list of installer stages.
-   * @var array
-   */
-  
-  var $stages = array();
-  
-  /**
-   * The GUID of the active stage
-   * @var string
-   */
-  
-  var $current_stage = '';
-  
-  /**
-   * The application name, or the name displayed after the stage name in the title bar. Should be localized.
-   * @var string
-   */
-  
-  var $app_name = '';
-  
-  /**
-   * If the header should be simplified (stripped of the Enano logo and top heading), this will be true.
-   * @var bool
-   */
-  
-  var $simple = false;
-  
-  /**
-   * Text inserted into the header on the right.
-   * @var string
-   */
-  
-  var $step = '';
-  
-  /**
-   * Extra text to add to the HTML <head> section
-   * @var array Will be implode()'ed
-   */
-  
-  var $additional_headers = array();
-  
-  /**
-   * Constructor.
-   * @param string The name displayed in the <title> tag
-   * @param bool If true, the simplified header format is displayed.
-   */
-  
-  function __construct($app_name, $simple_header)
-  {
-    $this->stages = array(
-        'main' => array(),
-        'hide' => array()
-      );
-    $this->app_name = $app_name;
-    $this->simple = ( $simple_header ) ? true : false;
-  }
-  
-  /**
-   * Adds more text to the HTML header.
-   * @param string
-   */
-  
-  function add_header($html)
-  {
-    $this->additional_headers[] = $html;
-  }
-  
-  /**
-   * Adds a stage to the installer.
-   * @param string Title of the stage, should be already put through $lang->get()
-   * @param bool If true, the stage is shown among possible stages at the top of the window. If false, acts as a hidden stage
-   * @return string Unique identifier for stage, used later on set_visible_stage()
-   */
-  
-  function add_stage($stage, $visible = true)
-  {
-    $key = ( $visible ) ? 'main' : 'hide';
-    $guid = md5(microtime() . mt_rand());
-    $this->stages[$key][$guid] = $stage;
-    if ( empty($this->current_stage) )
-      $this->current_stage = $guid;
-    return $guid;
-  }
-  
-  /**
-   * Resets the active stage of installation. This is for the UI only; it doesn't actually change how the backend works.
-   * @param string GUID of stage, returned from add_stage()
-   * @return bool true on success, false if stage GUID not found
-   */
-  
-  function set_visible_stage($guid)
-  {
-    foreach ( $this->stages['main'] as $key => $stage_name )
-    {
-      if ( $key == $guid )
-      {
-        $this->current_stage = $guid;
-        return true;
-      }
-    }
-    foreach ( $this->stages['hide'] as $key => $stage_name )
-    {
-      if ( $key == $guid )
-      {
-        $this->current_stage = $guid;
-        return true;
-      }
-    }
-    return false;
-  }
-  
-  /**
-   * Outputs the HTML headers and start of the <body>, including stage indicator
-   */
-  
-  function show_header()
-  {
-    // Determine the name of the current stage
-    $stage_name = false;
-    
-    if ( isset($this->stages['main'][$this->current_stage]) )
-      $stage_name = $this->stages['main'][$this->current_stage];
-    else if ( isset($this->stages['hide'][$this->current_stage]) )
-      $stage_name = $this->stages['hide'][$this->current_stage];
-    else
-      // Can't determine name of stage
-      return false;
-      
-    $this->app_name = htmlspecialchars($this->app_name);
-    $stage_name = htmlspecialchars($stage_name);
-    
-    global $lang;
-    if ( is_object($lang) && isset($GLOBALS['lang_uri']) )
-    {
-      $lang_uri = sprintf($GLOBALS['lang_uri'], $lang->lang_code);
-      $this->add_header('<script type="text/javascript" src="' . $lang_uri . '"></script>');
-    }
-    
-    $additional_headers = implode("\n    ", $this->additional_headers);
-    $title = addslashes(str_replace(' ', '_', $stage_name));
-    $js_dynamic = '<script type="text/javascript">
-        var title="' . $title . '";
-        var scriptPath="'.scriptPath.'";
-        var cdnPath="'.scriptPath.'";
-        var ENANO_SID="";
-        var AES_BITS='.AES_BITS.';
-        var AES_BLOCKSIZE=' . AES_BLOCKSIZE . ';
-        var pagepass=\'\';
-        var ENANO_LANG_ID = 1;
-        var DISABLE_MCE = true;
+	/**
+ 	* The list of installer stages.
+ 	* @var array
+ 	*/
+	
+	var $stages = array();
+	
+	/**
+ 	* The GUID of the active stage
+ 	* @var string
+ 	*/
+	
+	var $current_stage = '';
+	
+	/**
+ 	* The application name, or the name displayed after the stage name in the title bar. Should be localized.
+ 	* @var string
+ 	*/
+	
+	var $app_name = '';
+	
+	/**
+ 	* If the header should be simplified (stripped of the Enano logo and top heading), this will be true.
+ 	* @var bool
+ 	*/
+	
+	var $simple = false;
+	
+	/**
+ 	* Text inserted into the header on the right.
+ 	* @var string
+ 	*/
+	
+	var $step = '';
+	
+	/**
+ 	* Extra text to add to the HTML <head> section
+ 	* @var array Will be implode()'ed
+ 	*/
+	
+	var $additional_headers = array();
+	
+	/**
+ 	* Constructor.
+ 	* @param string The name displayed in the <title> tag
+ 	* @param bool If true, the simplified header format is displayed.
+ 	*/
+	
+	function __construct($app_name, $simple_header)
+	{
+		$this->stages = array(
+				'main' => array(),
+				'hide' => array()
+			);
+		$this->app_name = $app_name;
+		$this->simple = ( $simple_header ) ? true : false;
+	}
+	
+	/**
+ 	* Adds more text to the HTML header.
+ 	* @param string
+ 	*/
+	
+	function add_header($html)
+	{
+		$this->additional_headers[] = $html;
+	}
+	
+	/**
+ 	* Adds a stage to the installer.
+ 	* @param string Title of the stage, should be already put through $lang->get()
+ 	* @param bool If true, the stage is shown among possible stages at the top of the window. If false, acts as a hidden stage
+ 	* @return string Unique identifier for stage, used later on set_visible_stage()
+ 	*/
+	
+	function add_stage($stage, $visible = true)
+	{
+		$key = ( $visible ) ? 'main' : 'hide';
+		$guid = md5(microtime() . mt_rand());
+		$this->stages[$key][$guid] = $stage;
+		if ( empty($this->current_stage) )
+			$this->current_stage = $guid;
+		return $guid;
+	}
+	
+	/**
+ 	* Resets the active stage of installation. This is for the UI only; it doesn't actually change how the backend works.
+ 	* @param string GUID of stage, returned from add_stage()
+ 	* @return bool true on success, false if stage GUID not found
+ 	*/
+	
+	function set_visible_stage($guid)
+	{
+		foreach ( $this->stages['main'] as $key => $stage_name )
+		{
+			if ( $key == $guid )
+			{
+				$this->current_stage = $guid;
+				return true;
+			}
+		}
+		foreach ( $this->stages['hide'] as $key => $stage_name )
+		{
+			if ( $key == $guid )
+			{
+				$this->current_stage = $guid;
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/**
+ 	* Outputs the HTML headers and start of the <body>, including stage indicator
+ 	*/
+	
+	function show_header()
+	{
+		// Determine the name of the current stage
+		$stage_name = false;
+		
+		if ( isset($this->stages['main'][$this->current_stage]) )
+			$stage_name = $this->stages['main'][$this->current_stage];
+		else if ( isset($this->stages['hide'][$this->current_stage]) )
+			$stage_name = $this->stages['hide'][$this->current_stage];
+		else
+			// Can't determine name of stage
+			return false;
+			
+		$this->app_name = htmlspecialchars($this->app_name);
+		$stage_name = htmlspecialchars($stage_name);
+		
+		global $lang;
+		if ( is_object($lang) && isset($GLOBALS['lang_uri']) )
+		{
+			$lang_uri = sprintf($GLOBALS['lang_uri'], $lang->lang_code);
+			$this->add_header('<script type="text/javascript" src="' . $lang_uri . '"></script>');
+		}
+		
+		$additional_headers = implode("\n    ", $this->additional_headers);
+		$title = addslashes(str_replace(' ', '_', $stage_name));
+		$js_dynamic = '<script type="text/javascript">
+				var title="' . $title . '";
+				var scriptPath="'.scriptPath.'";
+				var cdnPath="'.scriptPath.'";
+				var ENANO_SID="";
+				var AES_BITS='.AES_BITS.';
+				var AES_BLOCKSIZE=' . AES_BLOCKSIZE . ';
+				var pagepass=\'\';
+				var ENANO_LANG_ID = 1;
+				var DISABLE_MCE = true;
 	var enano_version = \'' . installer_enano_version() . '\';
-        var msg_loading_component = \'Loading %component%...\';
-      </script>';
-    
-    echo <<<EOF
+				var msg_loading_component = \'Loading %component%...\';
+			</script>';
+		
+		echo <<<EOF
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>{$stage_name} &bull; {$this->app_name}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="../includes/clientside/css/enano-shared.css" />
-    <link rel="stylesheet" type="text/css" href="images/css/installer.css" id="mdgCss" />
-    $js_dynamic
-    <script type="text/javascript" src="../includes/clientside/static/enano-lib-basic.js"></script>
-    $additional_headers
-  </head>
-  <body>
-    <div id="enano">
+	<head>
+		<title>{$stage_name} &bull; {$this->app_name}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		<link rel="stylesheet" type="text/css" href="../includes/clientside/css/enano-shared.css" />
+		<link rel="stylesheet" type="text/css" href="images/css/installer.css" id="mdgCss" />
+		$js_dynamic
+		<script type="text/javascript" src="../includes/clientside/static/enano-lib-basic.js"></script>
+		$additional_headers
+	</head>
+	<body>
+		<div id="enano">
 
 EOF;
-    if ( !$this->simple )
-    {
-      $step = ( !empty($this->step) ) ? '<div id="step">' . htmlspecialchars($this->step) . '</div>' : '';
-      echo <<<EOF
-      <div id="header">
-        $step
-        <img alt="Enano logo" src="images/enano-artwork/installer-header-blue.png" />
-      </div>
+		if ( !$this->simple )
+		{
+			$step = ( !empty($this->step) ) ? '<div id="step">' . htmlspecialchars($this->step) . '</div>' : '';
+			echo <<<EOF
+			<div id="header">
+				$step
+				<img alt="Enano logo" src="images/enano-artwork/installer-header-blue.png" />
+			</div>
 
 EOF;
-    }
-    $stages_class = ( $this->simple ) ? 'stages' : 'stages stages-fixed';
-    echo <<<EOF
-      <div class="stages-holder">
-        <ul class="$stages_class">
-    
+		}
+		$stages_class = ( $this->simple ) ? 'stages' : 'stages stages-fixed';
+		echo <<<EOF
+			<div class="stages-holder">
+				<ul class="$stages_class">
+		
 EOF;
-    foreach ( $this->stages['main'] as $guid => $stage )
-    {
-      $class = ( $guid == $this->current_stage ) ? 'stage stage-active' : 'stage';
-      $stage = htmlspecialchars($stage);
-      echo "      <li class=\"$class\">$stage</li>\n    ";
-    }
-    echo "    </ul>\n      <div style=\"clear: both;\"></div>\n      </div>\n";
-    echo "      <div id=\"enano-fill\">\n      ";
-    echo "  <div id=\"enano-body\">\n            ";
-  }
-  
-  /**
-   * Displays the page footer.
-   */
-  
-  function show_footer()
-  {
-    $scriptpath = scriptPath;
-    $year = date('Y');
-    echo <<<EOF
-          <div id="copyright">
-            Enano and its various components, related documentation, and artwork are copyright &copy; 2006-$year Dan Fuhry.<br />
-            Copyrights for <a href="{$scriptpath}/licenses/">third-party components</a> are held by their respective authors.<br />
-            This program is Free Software; see the file "GPL" included with this package for details.
-          </div>
-        </div> <!-- div#enano-body -->
-      </div> <!-- div#enano-fill -->
-    </div> <!-- div#enano -->
-  </body>
+		foreach ( $this->stages['main'] as $guid => $stage )
+		{
+			$class = ( $guid == $this->current_stage ) ? 'stage stage-active' : 'stage';
+			$stage = htmlspecialchars($stage);
+			echo "      <li class=\"$class\">$stage</li>\n    ";
+		}
+		echo "    </ul>\n      <div style=\"clear: both;\"></div>\n      </div>\n";
+		echo "      <div id=\"enano-fill\">\n      ";
+		echo "  <div id=\"enano-body\">\n            ";
+	}
+	
+	/**
+ 	* Displays the page footer.
+ 	*/
+	
+	function show_footer()
+	{
+		$scriptpath = scriptPath;
+		$year = date('Y');
+		echo <<<EOF
+					<div id="copyright">
+						Enano and its various components, related documentation, and artwork are copyright &copy; 2006-$year Dan Fuhry.<br />
+						Copyrights for <a href="{$scriptpath}/licenses/">third-party components</a> are held by their respective authors.<br />
+						This program is Free Software; see the file "GPL" included with this package for details.
+					</div>
+				</div> <!-- div#enano-body -->
+			</div> <!-- div#enano-fill -->
+		</div> <!-- div#enano -->
+	</body>
 </html>
 EOF;
-  }
-  
+	}
+	
 }
  
 ?>
--- a/install/index.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/index.php	Sun Mar 28 23:10:46 2010 -0400
@@ -18,7 +18,7 @@
 $ui = new Enano_Installer_UI('Enano installation', true);
 if ( version_compare(PHP_VERSION, '5.0.0', '<') )
 {
-  $ui->__construct('Enano installation', true);
+	$ui->__construct('Enano installation', true);
 }
 $ui->add_stage('Welcome', true);
 $ui->add_stage('Installation', true);
@@ -29,11 +29,11 @@
 
 if ( defined('ENANO_INSTALLED') )
 {
-  // Is Enano installed? If so, load the config and check version info
-  define('IN_ENANO_UPGRADE', 'true');
-  define('ENANO_EXIT_AFTER_CONFIG', 1);
-  // common.php above calls chdir() to the ENANO_ROOT, so this loads the full Enano API.
-  require('includes/common.php');
+	// Is Enano installed? If so, load the config and check version info
+	define('IN_ENANO_UPGRADE', 'true');
+	define('ENANO_EXIT_AFTER_CONFIG', 1);
+	// common.php above calls chdir() to the ENANO_ROOT, so this loads the full Enano API.
+	require('includes/common.php');
 }
 
 // are we in PHP5?
@@ -41,101 +41,101 @@
 
 ?>
 
-          <div id="installnotice">
-          <?php
-            if ( !defined('ENANO_INSTALLED') ):
-            ?>
-            <div class="info-box-mini">
-              <b>Enano hasn't been installed yet!</b><br />
-              You'll need to install the Enano database before you can use your site. To get started, click the Install button below.
-            </div>
-          <?php
-            if ( file_exists('./config.php') )
-            {
-            ?>
-            <div class="warning-box-mini">
-              <b>A configuration file (config.php) exists but doesn't set the ENANO_INSTALLED constant.</b><br />
-              <p>Didn't expect to see this message?
-              It's possible that your configuration file has become corrupted and no longer sets information that Enano needs to connect
-              to the database. You should have a look at your config.php by downloading it with FTP or viewing it over SSH.
-              If the file appears to have been tampered with, please <a href="http://forum.enanocms.org/">contact the Enano team</a>
-              for support immediately.</p>
-              <p><b>Most importantly, if you suspect a security breach, you should contact the Enano team
-                 <a href="http://enanocms.org/Contact_us">via e-mail</a>. If you have the capability to use PGP encryption, you should do
-                 so; our public key is available <a href="http://enanocms.org/bin/enanocms-signkey.asc">here</a>.</b></p>
-            </div>
-            <?php
-            }
-            endif;
-            ?></div>
-          <table border="0" cellspacing="10" cellpadding="0" width="100%" id="installmenu">
-            <tr>
-              <td style="text-align: right; width: 50%;">
-                <!-- Enano logo -->
-                <img alt="Enano CMS" src="images/enano-artwork/installer-greeting.png" />
-              </td>
-              <td class="balancer">
-              </td>
-              <td>
-                <ul class="icons">
-                  <li><a href="readme.php" class="readme icon">Readme</a></li>
+					<div id="installnotice">
+					<?php
+						if ( !defined('ENANO_INSTALLED') ):
+						?>
+						<div class="info-box-mini">
+							<b>Enano hasn't been installed yet!</b><br />
+							You'll need to install the Enano database before you can use your site. To get started, click the Install button below.
+						</div>
+					<?php
+						if ( file_exists('./config.php') )
+						{
+						?>
+						<div class="warning-box-mini">
+							<b>A configuration file (config.php) exists but doesn't set the ENANO_INSTALLED constant.</b><br />
+							<p>Didn't expect to see this message?
+							It's possible that your configuration file has become corrupted and no longer sets information that Enano needs to connect
+							to the database. You should have a look at your config.php by downloading it with FTP or viewing it over SSH.
+							If the file appears to have been tampered with, please <a href="http://forum.enanocms.org/">contact the Enano team</a>
+							for support immediately.</p>
+							<p><b>Most importantly, if you suspect a security breach, you should contact the Enano team
+ 								<a href="http://enanocms.org/Contact_us">via e-mail</a>. If you have the capability to use PGP encryption, you should do
+ 								so; our public key is available <a href="http://enanocms.org/bin/enanocms-signkey.asc">here</a>.</b></p>
+						</div>
+						<?php
+						}
+						endif;
+						?></div>
+					<table border="0" cellspacing="10" cellpadding="0" width="100%" id="installmenu">
+						<tr>
+							<td style="text-align: right; width: 50%;">
+								<!-- Enano logo -->
+								<img alt="Enano CMS" src="images/enano-artwork/installer-greeting.png" />
+							</td>
+							<td class="balancer">
+							</td>
+							<td>
+								<ul class="icons">
+									<li><a href="readme.php" class="readme icon">Readme</a></li>
 <?php
-                  if ( !defined('ENANO_INSTALLED') ):
-                  ?>
-                  <li><a href="install.php" class="install icon">Install</a></li>
-                  <li>
-                    <a class="upgrade-disabled icon icon-disabled" title="You need to install Enano before you can do this.">
-                      Upgrade
-                      <small>
-                        Enano isn't installed yet. You can use this option when you want to upgrade to a newer release of Enano.
-                      </small>
-                    </a>
-                  </li>
+									if ( !defined('ENANO_INSTALLED') ):
+									?>
+									<li><a href="install.php" class="install icon">Install</a></li>
+									<li>
+										<a class="upgrade-disabled icon icon-disabled" title="You need to install Enano before you can do this.">
+											Upgrade
+											<small>
+												Enano isn't installed yet. You can use this option when you want to upgrade to a newer release of Enano.
+											</small>
+										</a>
+									</li>
 <?php
-                  else:
-                  ?>
-                  <li>
-                    <a class="install-disabled icon icon-disabled" title="Enano is already installed.">
-                      Install
-                      <small>Enano is already installed.</small> <!-- CSS takes care of making this position properly -->
-                    </a>
-                  </li>
-                  <?php
-                  if ( installer_enano_version() == enano_version(true) )
-                  {
-                    echo '<li>
-                    <a class="upgrade-disabled icon icon-disabled">
-                      Upgrade
-                      <small>
-                        You\'re already running the version of Enano included in this installer. Use the administration panel to check
-                        for updates.
-                      </small> <!-- CSS takes care of making this position properly -->
-                    </a>
-                  </li>';
-                  }
-                  else
-                  {
-                    $upgrade_script = file_exists('./install/onlineupgrade.php') ? 'onlineupgrade.php' : 'upgrade.php';
-                    if ( HAVE_PHP5 && !isset($_GET['debug_warn_php4']) )
-                      echo '<li><a href="' . $upgrade_script . '" class="upgrade icon">Upgrade</a></li>';
-                    else
-                      echo '<li>
-                    <span class="upgrade-disabled icon icon-disabled">
-                      Upgrade
-                      <small>
-                        Your server doesn\'t have PHP 5 or later installed. Enano 1.2 does not have support for PHP 4.
-                        <a href="install.php?debug_warn_php4">Learn more &raquo;</a>
-                      </small> <!-- CSS takes care of making this position properly -->
-                    </span>
-                  </li>';
-                  }
-                  endif;
-                  ?>
-                
-                </ul>
-              </td>
-            </tr>
-          </table>
+									else:
+									?>
+									<li>
+										<a class="install-disabled icon icon-disabled" title="Enano is already installed.">
+											Install
+											<small>Enano is already installed.</small> <!-- CSS takes care of making this position properly -->
+										</a>
+									</li>
+									<?php
+									if ( installer_enano_version() == enano_version(true) )
+									{
+										echo '<li>
+										<a class="upgrade-disabled icon icon-disabled">
+											Upgrade
+											<small>
+												You\'re already running the version of Enano included in this installer. Use the administration panel to check
+												for updates.
+											</small> <!-- CSS takes care of making this position properly -->
+										</a>
+									</li>';
+									}
+									else
+									{
+										$upgrade_script = file_exists('./install/onlineupgrade.php') ? 'onlineupgrade.php' : 'upgrade.php';
+										if ( HAVE_PHP5 && !isset($_GET['debug_warn_php4']) )
+											echo '<li><a href="' . $upgrade_script . '" class="upgrade icon">Upgrade</a></li>';
+										else
+											echo '<li>
+										<span class="upgrade-disabled icon icon-disabled">
+											Upgrade
+											<small>
+												Your server doesn\'t have PHP 5 or later installed. Enano 1.2 does not have support for PHP 4.
+												<a href="install.php?debug_warn_php4">Learn more &raquo;</a>
+											</small> <!-- CSS takes care of making this position properly -->
+										</span>
+									</li>';
+									}
+									endif;
+									?>
+								
+								</ul>
+							</td>
+						</tr>
+					</table>
 
 <?php
 
--- a/install/install.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/install.php	Sun Mar 28 23:10:46 2010 -0400
@@ -19,10 +19,10 @@
 // DEFINE THIS BEFORE RELEASE!
 $dangerous = true;
 if ( !empty($_GET['do']) )
-  if ( $_GET['do'] === 'lang_js' )
-    $dangerous = false;
+	if ( $_GET['do'] === 'lang_js' )
+		$dangerous = false;
 if ( $dangerous )
-  define('ENANO_DANGEROUS', 1);
+	define('ENANO_DANGEROUS', 1);
 
 require_once('includes/common.php');
 @ini_set('display_errors', 'on');
@@ -32,256 +32,256 @@
 
 if ( isset($_POST['language']) )
 {
-  // Include language lib and additional PHP5-only JSON functions
-  require_once( ENANO_ROOT . '/includes/json2.php' );
-  require_once( ENANO_ROOT . '/includes/lang.php' );
-  
-  // We have a language ID - init language
-  $lang_id = $_POST['language'];
-  if ( !isset($languages[$lang_id]) )
-  {
-    die('Invalid language selection - can\'t load metadata');
-  }
-  
-  $language_dir = $languages[$lang_id]['dir'];
-  
-  // Initialize language support
-  $lang = new Language($lang_id);
-  $lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/install.json');
-  $lang_uri = 'install.php?do=lang_js&language=%s';
-  
-  // Init UI
-  $ui = new Enano_Installer_UI($lang->get('meta_site_name'), false);
-  
-  // Add stages
-  foreach ( $stages as $stage )
-  {
-    $stage_ids[$stage] = $ui->add_stage($lang->get("{$stage}_modetitle"), true);
-  }
-  
-  // Determine stage
-  if ( isset($_REQUEST['stage']) && isset($stage_ids[$_REQUEST['stage']]) )
-  {
-    $ui->set_visible_stage($stage_ids[$_REQUEST['stage']]);
-    $stage = $_REQUEST['stage'];
-  }
-  else
-  {
-    $stage = 'license';
-  }
-  
-  $stage_num = array_search($stage, $stages);
-  if ( $stage_num )
-  {
-    $stage_num++;
-    $ui->step = $lang->get('meta_step', array('step' => $stage_num, 'title' => $lang->get("{$stage}_modetitle_long")));
-  }
+	// Include language lib and additional PHP5-only JSON functions
+	require_once( ENANO_ROOT . '/includes/json2.php' );
+	require_once( ENANO_ROOT . '/includes/lang.php' );
+	
+	// We have a language ID - init language
+	$lang_id = $_POST['language'];
+	if ( !isset($languages[$lang_id]) )
+	{
+		die('Invalid language selection - can\'t load metadata');
+	}
+	
+	$language_dir = $languages[$lang_id]['dir'];
+	
+	// Initialize language support
+	$lang = new Language($lang_id);
+	$lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/install.json');
+	$lang_uri = 'install.php?do=lang_js&language=%s';
+	
+	// Init UI
+	$ui = new Enano_Installer_UI($lang->get('meta_site_name'), false);
+	
+	// Add stages
+	foreach ( $stages as $stage )
+	{
+		$stage_ids[$stage] = $ui->add_stage($lang->get("{$stage}_modetitle"), true);
+	}
+	
+	// Determine stage
+	if ( isset($_REQUEST['stage']) && isset($stage_ids[$_REQUEST['stage']]) )
+	{
+		$ui->set_visible_stage($stage_ids[$_REQUEST['stage']]);
+		$stage = $_REQUEST['stage'];
+	}
+	else
+	{
+		$stage = 'license';
+	}
+	
+	$stage_num = array_search($stage, $stages);
+	if ( $stage_num )
+	{
+		$stage_num++;
+		$ui->step = $lang->get('meta_step', array('step' => $stage_num, 'title' => $lang->get("{$stage}_modetitle_long")));
+	}
 }
 else
 {
-  $ui = new Enano_Installer_UI('Enano installation', false);
-  
-  if ( version_compare(PHP_VERSION, '5.0.0', '<') )
-  {
-    $ui->__construct('Enano installation', false);
-  }
-  
-  $ui->step = 'Step 1: Choose language';
-  
-  $stage = 'language';
-  $stage_ids['language'] = $ui->add_stage('Language', true);
-  $stage_ids['license'] = $ui->add_stage('License', true);
-  $stage_ids['sysreqs'] = $ui->add_stage('Requirements', true);
-  $stage_ids['database'] = $ui->add_stage('Database', true);
-  $stage_ids['website'] = $ui->add_stage('Site info', true);
-  $stage_ids['login'] = $ui->add_stage('Admin login', true);
-  $stage_ids['confirm'] = $ui->add_stage('Review', true);
-  $stage_ids['install'] = $ui->add_stage('Install', true);
-  $stage_ids['finish'] = $ui->add_stage('Finish', true);
+	$ui = new Enano_Installer_UI('Enano installation', false);
+	
+	if ( version_compare(PHP_VERSION, '5.0.0', '<') )
+	{
+		$ui->__construct('Enano installation', false);
+	}
+	
+	$ui->step = 'Step 1: Choose language';
+	
+	$stage = 'language';
+	$stage_ids['language'] = $ui->add_stage('Language', true);
+	$stage_ids['license'] = $ui->add_stage('License', true);
+	$stage_ids['sysreqs'] = $ui->add_stage('Requirements', true);
+	$stage_ids['database'] = $ui->add_stage('Database', true);
+	$stage_ids['website'] = $ui->add_stage('Site info', true);
+	$stage_ids['login'] = $ui->add_stage('Admin login', true);
+	$stage_ids['confirm'] = $ui->add_stage('Review', true);
+	$stage_ids['install'] = $ui->add_stage('Install', true);
+	$stage_ids['finish'] = $ui->add_stage('Finish', true);
 }
 
 // If we don't have PHP 5, show a friendly error message and bail out
 if ( version_compare(PHP_VERSION, '5.0.0', '<') || isset($_GET['debug_warn_php4']) )
 {
-  $ui->set_visible_stage(
-    $ui->add_stage('PHP compatibility notice', false)
-  );
-  $ui->step = '';
-  $ui->show_header();
+	$ui->set_visible_stage(
+		$ui->add_stage('PHP compatibility notice', false)
+	);
+	$ui->step = '';
+	$ui->show_header();
 
-  // This isn't localized because all localization code is dependent on
-  // PHP 5 (loading lang.php will throw a parser error under PHP4). This
-  // one message probably doesn't need to be localized anyway.
-  
-  ?>
-  <h2 class="heading-error">
-    Your server doesn't have support for PHP 5.
-  </h2>
-  <p>
-    PHP 5 is the latest version of the language on which Enano was built. Its many new features have been available since early 2004, yet
-    many web hosts have not migrated to it because of the work involved. In 2007, Zend Corporation announced that support for the aging
-    PHP 4.x would be discontinued at the end of the year. An initiative called <a href="http://gophp5.org/">GoPHP5</a> was started to
-    encourage web hosts to migrate to PHP 5.
-  </p>
-  <p>
-    Because of the industry's decision to not support PHP 4 any longer, the Enano team decided that it was time to begin using the powerful
-    features of PHP 5 at the expense of PHP 4 compatibility. Therefore, this version of Enano cannot be installed on your server until it
-    is upgraded to at least PHP 5.0.0, and preferably the latest available version.
-    <!-- No, not even removing the check in this installer script will help. As soon as the PHP4 check is passed, the installer shows the
-         language selection page, after which the language code is loaded. The language code and libjson2 will trigger parse errors under
-         PHP <5.0.0. -->
-  </p>
-  <p>
-    If you need to use Enano but can't upgrade your PHP because you're on a shared or reseller hosting service, you can use the
-    <a href="http://enanocms.org/download?series=1.0">1.0.x series of Enano</a> on your site. While the Enano team attempts to make this
-    older series work on PHP 4, official support is not provided for installations of Enano on PHP 4.
-  </p>
-  <?php
-  
-  $ui->show_footer();
-  exit();
+	// This isn't localized because all localization code is dependent on
+	// PHP 5 (loading lang.php will throw a parser error under PHP4). This
+	// one message probably doesn't need to be localized anyway.
+	
+	?>
+	<h2 class="heading-error">
+		Your server doesn't have support for PHP 5.
+	</h2>
+	<p>
+		PHP 5 is the latest version of the language on which Enano was built. Its many new features have been available since early 2004, yet
+		many web hosts have not migrated to it because of the work involved. In 2007, Zend Corporation announced that support for the aging
+		PHP 4.x would be discontinued at the end of the year. An initiative called <a href="http://gophp5.org/">GoPHP5</a> was started to
+		encourage web hosts to migrate to PHP 5.
+	</p>
+	<p>
+		Because of the industry's decision to not support PHP 4 any longer, the Enano team decided that it was time to begin using the powerful
+		features of PHP 5 at the expense of PHP 4 compatibility. Therefore, this version of Enano cannot be installed on your server until it
+		is upgraded to at least PHP 5.0.0, and preferably the latest available version.
+		<!-- No, not even removing the check in this installer script will help. As soon as the PHP4 check is passed, the installer shows the
+ 				language selection page, after which the language code is loaded. The language code and libjson2 will trigger parse errors under
+ 				PHP <5.0.0. -->
+	</p>
+	<p>
+		If you need to use Enano but can't upgrade your PHP because you're on a shared or reseller hosting service, you can use the
+		<a href="http://enanocms.org/download?series=1.0">1.0.x series of Enano</a> on your site. While the Enano team attempts to make this
+		older series work on PHP 4, official support is not provided for installations of Enano on PHP 4.
+	</p>
+	<?php
+	
+	$ui->show_footer();
+	exit();
 }
 
 if ( isset($_SERVER['PATH_INFO']) && !isset($_GET['str']) && isset($_GET['do']) )
 {
-  $_GET['str'] = substr($_SERVER['PATH_INFO'], 1);
+	$_GET['str'] = substr($_SERVER['PATH_INFO'], 1);
 }
 
 if ( isset($_GET['do']) )
 {
-  switch ( $_GET['do'] )
-  {
-    case 'lang_js':
-      if ( !isset($_GET['language']) )
-        die();
-      $lang_id = $_GET['language'];
-      header('Content-type: text/javascript');
-      if ( !isset($languages[$lang_id]) )
-      {
-        die('// Bad language ID');
-      }
-      $language_dir = $languages[$lang_id]['dir'];
-      
-      // Include language lib and additional PHP5-only JSON functions
-      require_once( ENANO_ROOT . '/includes/json2.php' );
-      require_once( ENANO_ROOT . '/includes/lang.php' );
-  
-      // Initialize language support
-      $lang = new Language($lang_id);
-      $lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/install.json');
-      $lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/core.json');
-      $lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/user.json');
-      
-      $time_now = microtime_float();
-      $test = "if ( typeof(enano_lang) != 'object' )
+	switch ( $_GET['do'] )
+	{
+		case 'lang_js':
+			if ( !isset($_GET['language']) )
+				die();
+			$lang_id = $_GET['language'];
+			header('Content-type: text/javascript');
+			if ( !isset($languages[$lang_id]) )
+			{
+				die('// Bad language ID');
+			}
+			$language_dir = $languages[$lang_id]['dir'];
+			
+			// Include language lib and additional PHP5-only JSON functions
+			require_once( ENANO_ROOT . '/includes/json2.php' );
+			require_once( ENANO_ROOT . '/includes/lang.php' );
+	
+			// Initialize language support
+			$lang = new Language($lang_id);
+			$lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/install.json');
+			$lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/core.json');
+			$lang->load_file(ENANO_ROOT . '/language/' . $language_dir . '/user.json');
+			
+			$time_now = microtime_float();
+			$test = "if ( typeof(enano_lang) != 'object' )
 {
-  var enano_lang = new Object();
-  var enano_lang_code = new Object();
+	var enano_lang = new Object();
+	var enano_lang_code = new Object();
 }
 
 enano_lang[{$lang->lang_id}] = " . enano_json_encode($lang->strings) . ";
 enano_lang_code[{$lang->lang_id}] = '{$lang->lang_code}';";
-      $time_total = round(microtime_float() - $time_now, 4);
-      echo "// Generated in $time_total seconds\n";
-      echo $test;
+			$time_total = round(microtime_float() - $time_now, 4);
+			echo "// Generated in $time_total seconds\n";
+			echo $test;
 
-      exit();
-    case 'modrewrite_test':
-      // Include language lib and additional PHP5-only JSON functions
-      require_once( ENANO_ROOT . '/includes/json2.php' );
-      
-      if ( isset($_GET['str']) && in_array($_GET['str'], array('standard', 'shortened', 'rewrite')) )
-      {
-        echo 'good_' . $_GET['str'];
-      }
-      else if ( $_SERVER['QUERY_STRING'] == '/tiny&do=modrewrite_test' )
-      {
-        echo 'good_tiny';
-      }
-      else
-      {
-        echo 'bad';
-      }
-      exit();
-  }
+			exit();
+		case 'modrewrite_test':
+			// Include language lib and additional PHP5-only JSON functions
+			require_once( ENANO_ROOT . '/includes/json2.php' );
+			
+			if ( isset($_GET['str']) && in_array($_GET['str'], array('standard', 'shortened', 'rewrite')) )
+			{
+				echo 'good_' . $_GET['str'];
+			}
+			else if ( $_SERVER['QUERY_STRING'] == '/tiny&do=modrewrite_test' )
+			{
+				echo 'good_tiny';
+			}
+			else
+			{
+				echo 'bad';
+			}
+			exit();
+	}
 }
 
 switch ( $stage )
 {
-  default:
-    $ui->show_header();
-    echo '<p>Invalid stage.</p>';
-    break;
-  case 'language':
-    $ui->show_header();
-    ?>
-    <h2>Welcome to Enano.</h2>
-    <h3>Bienvenido a Enano /
-       Wilkommen in Enano /
-       Bienvenue à Enano /
-       Benvenuti a Enano /
-       欢迎 Enano /
-       Enano へようこそ。
-       </h3>
-    <p>
-       <b>Please select a language:</b> /
-       Por favor, seleccione un idioma: /
-       Bitte wählen Sie eine Sprache: /
-       S’il vous plaît choisir une langue: /
-       Selezionare una lingua: /
-       请选择一种语言: /
-       言語を選択してください:</p>
-    <form action="install.php?stage=license" method="post">
-      <select name="language" style="width: 200px;" tabindex="1">
-        <?php
-        foreach ( $languages as $code => $meta )
-        {
-          $sel = ( $code == 'eng' ) ? ' selected="selected"' : '';
-          echo '<option value="' . $code . '"' . $sel . '>' . $meta['name'] . '</option>';
-        }
-        ?>
-      </select>
-      <input tabindex="2" type="submit" value="&gt;&gt;" />
-    </form>
-    <?php
-    break;
-  case 'license':
-    $ui->show_header();
-    require( ENANO_ROOT . '/install/includes/stages/license.php' );
-    break;
-  case 'sysreqs':
-    $ui->show_header();
-    require( ENANO_ROOT . '/install/includes/stages/sysreqs.php' );
-    break;
-  case 'database':
-    if ( isset($_POST['driver']) && in_array($_POST['driver'], $supported_drivers) )
-    {
-      // This is SAFE! It's validated against the array in in_array() above.
-      $driver = $_POST['driver'];
-      require( ENANO_ROOT . "/install/includes/stages/database_{$driver}.php" );
-    }
-    else
-    {
-      $ui->show_header();
-      // No driver selected - give the DB drive selection page
-      require( ENANO_ROOT . '/install/includes/stages/database.php' );
-    }
-    break;
-  case 'website':
-    require( ENANO_ROOT . '/install/includes/stages/website.php' );
-    break;
-  case 'login':
-    require( ENANO_ROOT . '/install/includes/stages/login.php' );
-    break;
-  case 'confirm':
-    require( ENANO_ROOT . '/install/includes/stages/confirm.php' );
-    break;
-  case 'install':
-    require( ENANO_ROOT . '/install/includes/stages/install.php' );
-    break;
-  case 'finish':
-    require( ENANO_ROOT . '/install/includes/stages/finish.php' );
-    break;
+	default:
+		$ui->show_header();
+		echo '<p>Invalid stage.</p>';
+		break;
+	case 'language':
+		$ui->show_header();
+		?>
+		<h2>Welcome to Enano.</h2>
+		<h3>Bienvenido a Enano /
+ 			Wilkommen in Enano /
+ 			Bienvenue à Enano /
+ 			Benvenuti a Enano /
+ 			欢迎 Enano /
+ 			Enano へようこそ。
+ 			</h3>
+		<p>
+ 			<b>Please select a language:</b> /
+ 			Por favor, seleccione un idioma: /
+ 			Bitte wählen Sie eine Sprache: /
+ 			S’il vous plaît choisir une langue: /
+ 			Selezionare una lingua: /
+ 			请选择一种语言: /
+ 			言語を選択してください:</p>
+		<form action="install.php?stage=license" method="post">
+			<select name="language" style="width: 200px;" tabindex="1">
+				<?php
+				foreach ( $languages as $code => $meta )
+				{
+					$sel = ( $code == 'eng' ) ? ' selected="selected"' : '';
+					echo '<option value="' . $code . '"' . $sel . '>' . $meta['name'] . '</option>';
+				}
+				?>
+			</select>
+			<input tabindex="2" type="submit" value="&gt;&gt;" />
+		</form>
+		<?php
+		break;
+	case 'license':
+		$ui->show_header();
+		require( ENANO_ROOT . '/install/includes/stages/license.php' );
+		break;
+	case 'sysreqs':
+		$ui->show_header();
+		require( ENANO_ROOT . '/install/includes/stages/sysreqs.php' );
+		break;
+	case 'database':
+		if ( isset($_POST['driver']) && in_array($_POST['driver'], $supported_drivers) )
+		{
+			// This is SAFE! It's validated against the array in in_array() above.
+			$driver = $_POST['driver'];
+			require( ENANO_ROOT . "/install/includes/stages/database_{$driver}.php" );
+		}
+		else
+		{
+			$ui->show_header();
+			// No driver selected - give the DB drive selection page
+			require( ENANO_ROOT . '/install/includes/stages/database.php' );
+		}
+		break;
+	case 'website':
+		require( ENANO_ROOT . '/install/includes/stages/website.php' );
+		break;
+	case 'login':
+		require( ENANO_ROOT . '/install/includes/stages/login.php' );
+		break;
+	case 'confirm':
+		require( ENANO_ROOT . '/install/includes/stages/confirm.php' );
+		break;
+	case 'install':
+		require( ENANO_ROOT . '/install/includes/stages/install.php' );
+		break;
+	case 'finish':
+		require( ENANO_ROOT . '/install/includes/stages/finish.php' );
+		break;
 }
 
 $ui->show_footer();
--- a/install/readme.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/readme.php	Sun Mar 28 23:10:46 2010 -0400
@@ -26,7 +26,7 @@
 ?>
 <h2>Readme</h2>
 <p>This document contains important information you'll want to know before you install Enano. For installation instructions, please
-   see the <a href="http://docs.enanocms.org/Help:2.1">Enano installation guide</a>. <a href="index.php">Return to welcome menu &raquo;</a></p>
+ 	see the <a href="http://docs.enanocms.org/Help:2.1">Enano installation guide</a>. <a href="index.php">Return to welcome menu &raquo;</a></p>
 <pre class="scroller"><?php
 
 $readme = @file_get_contents('./README');
--- a/install/schemas/upgrade/1.1.5-1.1.6.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/schemas/upgrade/1.1.5-1.1.6.php	Sun Mar 28 23:10:46 2010 -0400
@@ -9,27 +9,27 @@
 
 $q = $db->sql_query('UPDATE ' . table_prefix . "users SET old_encryption = 2 WHERE user_id > 1 AND old_encryption = 0;");
 if ( !$q )
-  $db->_die();
+	$db->_die();
 
 $q = $db->sql_query('SELECT user_id, password FROM ' . table_prefix . "users WHERE user_id > 1 AND old_encryption = 2;");
 if ( !$q )
-  $db->_die();
+	$db->_die();
 
 while ( $row = $db->fetchrow($q) )
 {
-  $password = $session->pk_decrypt($row['password']);
-  if ( empty($password) )
-  {
-    global $ui;
-    echo '<p>1.1.5-1.1.6 migration script: ERROR: bad password returned from $session->pk_decrypt()</p>';
-    $ui->show_footer();
-    exit;
-  }
-  $hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
-  $password = hmac_sha1($password, $hmac_secret);
-  $e = $db->sql_query('UPDATE ' . table_prefix . "users SET password = '{$password}', password_salt = '{$hmac_secret}', old_encryption = 0 WHERE user_id = {$row['user_id']};");
-  if ( !$e )
-    $db->_die();
+	$password = $session->pk_decrypt($row['password']);
+	if ( empty($password) )
+	{
+		global $ui;
+		echo '<p>1.1.5-1.1.6 migration script: ERROR: bad password returned from $session->pk_decrypt()</p>';
+		$ui->show_footer();
+		exit;
+	}
+	$hmac_secret = hexencode(AESCrypt::randkey(20), '', '');
+	$password = hmac_sha1($password, $hmac_secret);
+	$e = $db->sql_query('UPDATE ' . table_prefix . "users SET password = '{$password}', password_salt = '{$hmac_secret}', old_encryption = 0 WHERE user_id = {$row['user_id']};");
+	if ( !$e )
+		$db->_die();
 }
 
 
--- a/install/schemas/upgrade/1.1.6-1.1.7.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/schemas/upgrade/1.1.6-1.1.7.php	Sun Mar 28 23:10:46 2010 -0400
@@ -6,30 +6,30 @@
 
 $q = $db->sql_query('SELECT user_id, username FROM ' . table_prefix . 'users;');
 if ( !$q )
-  $db->_die();
+	$db->_die();
 
 $map = array();
 while($row = $db->fetchrow())
 {
-  $map[ $row['username'] ] = $row['user_id'];
+	$map[ $row['username'] ] = $row['user_id'];
 }
 $db->free_result();
 
 $q = $db->sql_query('SELECT author FROM ' . table_prefix . 'logs WHERE author_uid = 1;');
 if ( !$q )
-  $db->_die();
+	$db->_die();
 
 $updated = array();
 
 while ( $row = $db->fetchrow($q) )
 {
-  if ( isset($map[ $row['author'] ]) && !is_valid_ip($row['author']) && !in_array($row['author'], $updated) )
-  {
-    $author = $db->escape($row['author']);
-    $sql = "UPDATE " . table_prefix . "logs SET author_uid = {$map[ $row['author'] ]} WHERE author = '$author';";
-    if ( !$db->sql_query($sql) )
-      $db->_die();
-    $updated[] = $row['author'];
-  }
+	if ( isset($map[ $row['author'] ]) && !is_valid_ip($row['author']) && !in_array($row['author'], $updated) )
+	{
+		$author = $db->escape($row['author']);
+		$sql = "UPDATE " . table_prefix . "logs SET author_uid = {$map[ $row['author'] ]} WHERE author = '$author';";
+		if ( !$db->sql_query($sql) )
+			$db->_die();
+		$updated[] = $row['author'];
+	}
 }
 
--- a/install/schemas/upgrade/migration/1.0-1.1.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/schemas/upgrade/migration/1.0-1.1.php	Sun Mar 28 23:10:46 2010 -0400
@@ -2,56 +2,56 @@
 
 function MIGRATE()
 {
-  global $languages;
-  global $db, $dbdriver;
-  
-  // Database upgrade
-  try
-  {
-    $sql_parser = new SQL_Parser('install/schemas/upgrade/migration/1.0-1.1-' . $dbdriver . '.sql');
-  }
-  catch ( Exception $e )
-  {
-    die("<pre>$e</pre>");
-  }
-  
-  $sql_parser->assign_vars(array(
-      'TABLE_PREFIX' => table_prefix
-    ));
-  
-  $sql_list = $sql_parser->parse();
-  foreach ( $sql_list as $sql )
-  {
-    if ( !$db->sql_query($sql) )
-      $db->_die();
-  }
-  
-  // Install default language
-  $lang_id = 'eng';
-  $lang_data =& $languages[$lang_id];
-  $lang_dir = ENANO_ROOT . "/language/{$lang_data['dir']}/";
-  // function install_language($lang_code, $lang_name_neutral, $lang_name_local, $lang_file = false)
-  install_language($lang_id, $lang_data['name_eng'], false);
-  
-  // Only import strings if the script isn't planning to do it again later
-  global $do_langimport;
-  if ( !$do_langimport )
-  {
-    $lang_local = new Language($lang_id);
-    $lang_local->import($lang_dir . "core.json");
-    $lang_local->import($lang_dir . "tools.json");
-    $lang_local->import($lang_dir . "user.json");
-    $lang_local->import($lang_dir . "admin.json");
-  }
-  
-  // This doesn't set to installer_enano_version() because it only
-  // migrates the database from 1.0.x to 1.1.x status and runs the
-  // core logic required to transform a 1.0.x installation into
-  // a 1.1.x installation. Thus, when upgrading, the upgrade script
-  // still needs to run all later upgrade schema files in addition
-  // to this migration code.
-  setConfig('enano_version', '1.1.1');
-  
-  return true;
+	global $languages;
+	global $db, $dbdriver;
+	
+	// Database upgrade
+	try
+	{
+		$sql_parser = new SQL_Parser('install/schemas/upgrade/migration/1.0-1.1-' . $dbdriver . '.sql');
+	}
+	catch ( Exception $e )
+	{
+		die("<pre>$e</pre>");
+	}
+	
+	$sql_parser->assign_vars(array(
+			'TABLE_PREFIX' => table_prefix
+		));
+	
+	$sql_list = $sql_parser->parse();
+	foreach ( $sql_list as $sql )
+	{
+		if ( !$db->sql_query($sql) )
+			$db->_die();
+	}
+	
+	// Install default language
+	$lang_id = 'eng';
+	$lang_data =& $languages[$lang_id];
+	$lang_dir = ENANO_ROOT . "/language/{$lang_data['dir']}/";
+	// function install_language($lang_code, $lang_name_neutral, $lang_name_local, $lang_file = false)
+	install_language($lang_id, $lang_data['name_eng'], false);
+	
+	// Only import strings if the script isn't planning to do it again later
+	global $do_langimport;
+	if ( !$do_langimport )
+	{
+		$lang_local = new Language($lang_id);
+		$lang_local->import($lang_dir . "core.json");
+		$lang_local->import($lang_dir . "tools.json");
+		$lang_local->import($lang_dir . "user.json");
+		$lang_local->import($lang_dir . "admin.json");
+	}
+	
+	// This doesn't set to installer_enano_version() because it only
+	// migrates the database from 1.0.x to 1.1.x status and runs the
+	// core logic required to transform a 1.0.x installation into
+	// a 1.1.x installation. Thus, when upgrading, the upgrade script
+	// still needs to run all later upgrade schema files in addition
+	// to this migration code.
+	setConfig('enano_version', '1.1.1');
+	
+	return true;
 }
 
--- a/install/upgrade.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/install/upgrade.php	Sun Mar 28 23:10:46 2010 -0400
@@ -46,11 +46,11 @@
 $versions_flipped = array_flip($enano_versions['1.1']);
 if ( !isset($versions_flipped[ enano_version() ]) || $versions_flipped[ enano_version() ] < $versions_flipped['1.1.7'] )
 {
-  $do_langimport = true;
+	$do_langimport = true;
 }
 
 if ( in_array(enano_version(), array('1.1.1', '1.1.2', '1.1.3', '1.1.4', '1.1.5')) || substr(enano_version(), 0, 3) == '1.0' )
-  define('ENANO_UPGRADE_USE_AES_PASSWORDS', 1);
+	define('ENANO_UPGRADE_USE_AES_PASSWORDS', 1);
 
 // init languages
 $lang_id_list = array_keys($languages);
@@ -73,13 +73,13 @@
 // Version check
 if ( enano_version() == installer_enano_version() )
 {
-  $ui->show_header();
-  $link_home = makeUrl(get_main_page(), false, true);
-  echo '<h3>' . $lang->get('upgrade_err_current_title') . '</h3>' .
-       '<p>' . $lang->get('upgrade_err_current_body', array('mainpage_link' => $link_home)) . '</p>' .
-       '<p>' . $lang->get('upgrade_err_current_body_para2', array('mainpage_link' => $link_home)) . '</p>';
-  $ui->show_footer();
-  exit();
+	$ui->show_header();
+	$link_home = makeUrl(get_main_page(), false, true);
+	echo '<h3>' . $lang->get('upgrade_err_current_title') . '</h3>' .
+ 			'<p>' . $lang->get('upgrade_err_current_body', array('mainpage_link' => $link_home)) . '</p>' .
+ 			'<p>' . $lang->get('upgrade_err_current_body_para2', array('mainpage_link' => $link_home)) . '</p>';
+	$ui->show_footer();
+	exit();
 }
 
 // Start session manager
@@ -88,115 +88,115 @@
 // Welcome page
 if ( !isset($_GET['stage']) )
 {
-  $ui->show_header();
-  
-  if ( preg_match('/1\.0/', enano_version()) )
-  {
-    // Migrating from 1.0.x
-    echo '<h3>' . $lang->get('upgrade_welcome_banshee_heading', array('enano_version' => installer_enano_version())) . '</h3>';
-    echo '<p>' . $lang->get('upgrade_welcome_banshee_para1') . '</p>';
-    echo '<p>' . $lang->get('upgrade_welcome_banshee_para2') . '</p>';
-  }
-  else
-  {
-    // Upgrading from 1.1.x/1.2.x
-    echo '<h3>' . $lang->get('upgrade_welcome_caoineag_heading', array('enano_version' => installer_enano_version())) . '</h3>';
-    echo '<p>' . $lang->get('upgrade_welcome_caoineag_para1') . '</p>';
-  }
-  
-  echo '<div style="font-size: x-large; text-align: center; margin: 20px 0;">';
-  echo '<a class="abutton" href="' . $session->append_sid('upgrade.php?stage=confirm') . '" style="text-decoration: none;">' . $lang->get('upgrade_welcome_btn_continue') . ' &raquo;</a>';
-  echo '</div>';
-  
-  $ui->show_footer();
-  exit;
+	$ui->show_header();
+	
+	if ( preg_match('/1\.0/', enano_version()) )
+	{
+		// Migrating from 1.0.x
+		echo '<h3>' . $lang->get('upgrade_welcome_banshee_heading', array('enano_version' => installer_enano_version())) . '</h3>';
+		echo '<p>' . $lang->get('upgrade_welcome_banshee_para1') . '</p>';
+		echo '<p>' . $lang->get('upgrade_welcome_banshee_para2') . '</p>';
+	}
+	else
+	{
+		// Upgrading from 1.1.x/1.2.x
+		echo '<h3>' . $lang->get('upgrade_welcome_caoineag_heading', array('enano_version' => installer_enano_version())) . '</h3>';
+		echo '<p>' . $lang->get('upgrade_welcome_caoineag_para1') . '</p>';
+	}
+	
+	echo '<div style="font-size: x-large; text-align: center; margin: 20px 0;">';
+	echo '<a class="abutton" href="' . $session->append_sid('upgrade.php?stage=confirm') . '" style="text-decoration: none;">' . $lang->get('upgrade_welcome_btn_continue') . ' &raquo;</a>';
+	echo '</div>';
+	
+	$ui->show_footer();
+	exit;
 }
 
 if ( !$session->user_logged_in || ( $session->user_logged_in && $session->auth_level < USER_LEVEL_ADMIN ) )
 {
-  // if we're not logged in, destroy any existing session keys in the browser
-  @setcookie('sid', '', time() - 86400);
-  
-  $ui->set_visible_stage($stg_login);
-  if ( isset($_POST['do_login']) )
-  {
-    if ( !$session->user_logged_in )
-    {
-      $result = $session->login_without_crypto($_POST['username'], $_POST['password'], false, USER_LEVEL_MEMBER);
-    }
-    if ( !isset($result) || ( isset($result) && $result['success']) )
-    {
-      $result = $session->login_without_crypto($_POST['username'], $_POST['password'], false, USER_LEVEL_ADMIN);
-      if ( $result['success'] )
-      {
-        header('HTTP/1.1 302 Some kind of redirect with implied no content');
-        header('Location: ' . scriptPath . '/install/' . $session->append_sid('upgrade.php?stage=confirm'));
-        exit();
-      }
-    }
-  }
-  
-  $ui->show_header();
-  
-  ?>
-  <h3><?php echo $lang->get('upgrade_login_msg_auth_needed_title'); ?></h3>
-  <?php
-  
-  echo '<form action="upgrade.php?stage=login" method="post">';
-  
-  if ( isset($result) )
-  {
-    echo '<b>' . $lang->get('upgrade_login_err_failed', array('error_code' => $result['error'])) . '</b>';
-  }
-  
-  ?>
-  <p><?php
-  if ( $session->user_logged_in )
-  {
-    echo $lang->get('upgrade_login_msg_auth_needed_body_level2');
-  }
-  else
-  {
-    echo $lang->get('upgrade_login_msg_auth_needed_body_level1');
-  }
-  ?></p>
-  <p>
-  <?php echo $lang->get('upgrade_login_msg_local_auth'); ?>
-  </p>
-  <table border="0" cellspacing="0" cellpadding="5" style="margin: 0 auto;">
-  <tr>
-    <td><?php echo $lang->get('user_login_field_username'); ?>:</td>
-    <td><input type="text" name="username" tabindex="1" /></td>
-  </tr>
-  <tr>
-    <td><?php echo $lang->get('user_login_field_password'); ?>:</td>
-    <td><input type="password" name="password" tabindex="2" /></td>
-  </tr>
-  <tr>
-    <td colspan="2" style="text-align: center;">
-      <input type="submit" name="do_login" value="<?php echo $lang->get('upgrade_login_btn_login'); ?>" tabindex="3" />
-    </td>
-  </tr>
-  </table>
-  <?php
-  
-  echo '</form>';
-  
-  $ui->show_footer();
-  exit();
+	// if we're not logged in, destroy any existing session keys in the browser
+	@setcookie('sid', '', time() - 86400);
+	
+	$ui->set_visible_stage($stg_login);
+	if ( isset($_POST['do_login']) )
+	{
+		if ( !$session->user_logged_in )
+		{
+			$result = $session->login_without_crypto($_POST['username'], $_POST['password'], false, USER_LEVEL_MEMBER);
+		}
+		if ( !isset($result) || ( isset($result) && $result['success']) )
+		{
+			$result = $session->login_without_crypto($_POST['username'], $_POST['password'], false, USER_LEVEL_ADMIN);
+			if ( $result['success'] )
+			{
+				header('HTTP/1.1 302 Some kind of redirect with implied no content');
+				header('Location: ' . scriptPath . '/install/' . $session->append_sid('upgrade.php?stage=confirm'));
+				exit();
+			}
+		}
+	}
+	
+	$ui->show_header();
+	
+	?>
+	<h3><?php echo $lang->get('upgrade_login_msg_auth_needed_title'); ?></h3>
+	<?php
+	
+	echo '<form action="upgrade.php?stage=login" method="post">';
+	
+	if ( isset($result) )
+	{
+		echo '<b>' . $lang->get('upgrade_login_err_failed', array('error_code' => $result['error'])) . '</b>';
+	}
+	
+	?>
+	<p><?php
+	if ( $session->user_logged_in )
+	{
+		echo $lang->get('upgrade_login_msg_auth_needed_body_level2');
+	}
+	else
+	{
+		echo $lang->get('upgrade_login_msg_auth_needed_body_level1');
+	}
+	?></p>
+	<p>
+	<?php echo $lang->get('upgrade_login_msg_local_auth'); ?>
+	</p>
+	<table border="0" cellspacing="0" cellpadding="5" style="margin: 0 auto;">
+	<tr>
+		<td><?php echo $lang->get('user_login_field_username'); ?>:</td>
+		<td><input type="text" name="username" tabindex="1" /></td>
+	</tr>
+	<tr>
+		<td><?php echo $lang->get('user_login_field_password'); ?>:</td>
+		<td><input type="password" name="password" tabindex="2" /></td>
+	</tr>
+	<tr>
+		<td colspan="2" style="text-align: center;">
+			<input type="submit" name="do_login" value="<?php echo $lang->get('upgrade_login_btn_login'); ?>" tabindex="3" />
+		</td>
+	</tr>
+	</table>
+	<?php
+	
+	echo '</form>';
+	
+	$ui->show_footer();
+	exit();
 }
 
 if ( isset($_GET['stage']) && @$_GET['stage'] == 'pimpmyenano' )
 {
-  $ui->set_visible_stage($stg_upgrade);
+	$ui->set_visible_stage($stg_upgrade);
 }
 else if ( isset($_GET['stage']) && @$_GET['stage'] == 'postpimp' )
 {
-  $ui->set_visible_stage($stg_finish);
+	$ui->set_visible_stage($stg_finish);
 }
 else
 {
-  $ui->set_visible_stage($stg_confirm);
+	$ui->set_visible_stage($stg_confirm);
 }
 
 // The real migration code
@@ -204,137 +204,137 @@
 
 if ( isset($_GET['stage']) && @$_GET['stage'] == 'pimpmyenano' )
 {
-  /*
-   HOW DOES ENANO'S UPGRADER WORK?
-   
-   Versions of Enano are organized into branches and then specific versions by
-   version number. The upgrader works by using a list of known version numbers
-   and then systematically executing upgrade schemas for each version.
-   
-   When the user requests an upgrade, the first thing performed is a migration
-   check, which verifies that they are within the right branch. If they are not
-   within the right branch the upgrade framework will load a migration script
-   which will define a function named MIGRATE(). Performing more than one
-   migration in one pass will probably never be supported. How that works for
-   UX in 1.3.x/1.4.x I know not yet.
-   
-   After performing any necessary branch migrations, the framework will perform
-   any upgrades within the target branch, which is the first two parts
-   (delimited by periods) of the installer's version number defined in the
-   installer's common.php.
-   
-   enano_perform_upgrade() will only do upgrades. Not migrations. The two as
-   illustrated within this installer are very different.
-   */
-  
-  // Do we need to run the migration first?
-  list($major_version, $minor_version) = explode('.', preg_replace('/^upg-/', '', enano_version()));
-  $current_branch = "$major_version.$minor_version";
-  
-  list($major_version, $minor_version) = explode('.', installer_enano_version());
-  $target_branch = "$major_version.$minor_version";
-  
-  if ( $target_branch != $current_branch )
-  {
-    // First upgrade to the latest revision of the current branch
-    enano_perform_upgrade($current_branch);
-    // Branch migration could be tricky and is often highly specific between
-    // major branches, so just include a custom migration script.
-    require(ENANO_ROOT . "/install/schemas/upgrade/migration/{$current_branch}-{$target_branch}.php");
-    $result = MIGRATE();
-    if ( !$result )
-    {
-      echo 'Migration failed, there should be an error message above.';
-      $ui->show_footer();
-      exit;
-    }
-  }
-  
-  // Do the actual upgrade
-  enano_perform_upgrade($target_branch);
-  
-  // Mark as upgrade-in-progress
-  setConfig('enano_version', 'upg-' . installer_enano_version());
-  
-  ?>
-  <h3>
-    <?php echo $lang->get('upgrade_msg_schema_complete_title'); ?>
-  </h3>
-  <p>
-    <?php echo $lang->get('upgrade_msg_schema_complete_body'); ?>
-  </p>
-  <form action="upgrade.php" method="get" style="text-align: center;">
-    <input type="hidden" name="auth" value="<?php echo $session->sid_super; ?>" />
-    <p style="text-align: center;">
-      <button name="stage" value="postpimp" class="submit">
-        <?php echo $lang->get('upgrade_btn_continue'); ?>
-      </button>
-    </p>
-  </form>
-  <?php
+	/*
+ 	HOW DOES ENANO'S UPGRADER WORK?
+ 	
+ 	Versions of Enano are organized into branches and then specific versions by
+ 	version number. The upgrader works by using a list of known version numbers
+ 	and then systematically executing upgrade schemas for each version.
+ 	
+ 	When the user requests an upgrade, the first thing performed is a migration
+ 	check, which verifies that they are within the right branch. If they are not
+ 	within the right branch the upgrade framework will load a migration script
+ 	which will define a function named MIGRATE(). Performing more than one
+ 	migration in one pass will probably never be supported. How that works for
+ 	UX in 1.3.x/1.4.x I know not yet.
+ 	
+ 	After performing any necessary branch migrations, the framework will perform
+ 	any upgrades within the target branch, which is the first two parts
+ 	(delimited by periods) of the installer's version number defined in the
+ 	installer's common.php.
+ 	
+ 	enano_perform_upgrade() will only do upgrades. Not migrations. The two as
+ 	illustrated within this installer are very different.
+ 	*/
+	
+	// Do we need to run the migration first?
+	list($major_version, $minor_version) = explode('.', preg_replace('/^upg-/', '', enano_version()));
+	$current_branch = "$major_version.$minor_version";
+	
+	list($major_version, $minor_version) = explode('.', installer_enano_version());
+	$target_branch = "$major_version.$minor_version";
+	
+	if ( $target_branch != $current_branch )
+	{
+		// First upgrade to the latest revision of the current branch
+		enano_perform_upgrade($current_branch);
+		// Branch migration could be tricky and is often highly specific between
+		// major branches, so just include a custom migration script.
+		require(ENANO_ROOT . "/install/schemas/upgrade/migration/{$current_branch}-{$target_branch}.php");
+		$result = MIGRATE();
+		if ( !$result )
+		{
+			echo 'Migration failed, there should be an error message above.';
+			$ui->show_footer();
+			exit;
+		}
+	}
+	
+	// Do the actual upgrade
+	enano_perform_upgrade($target_branch);
+	
+	// Mark as upgrade-in-progress
+	setConfig('enano_version', 'upg-' . installer_enano_version());
+	
+	?>
+	<h3>
+		<?php echo $lang->get('upgrade_msg_schema_complete_title'); ?>
+	</h3>
+	<p>
+		<?php echo $lang->get('upgrade_msg_schema_complete_body'); ?>
+	</p>
+	<form action="upgrade.php" method="get" style="text-align: center;">
+		<input type="hidden" name="auth" value="<?php echo $session->sid_super; ?>" />
+		<p style="text-align: center;">
+			<button name="stage" value="postpimp" class="submit">
+				<?php echo $lang->get('upgrade_btn_continue'); ?>
+			</button>
+		</p>
+	</form>
+	<?php
 }
 else if ( isset($_GET['stage']) && @$_GET['stage'] == 'postpimp' )
 {
-  // verify version
-  if ( enano_version() != 'upg-' . installer_enano_version() )
-  {
-    echo '<p>' . $lang->get('upgrade_err_post_not_available') . '</p>';
-    $ui->show_footer();
-    $db->close();
-    exit();
-  }
-  
-  function stg_load_files()
-  {
-    if ( !@include( ENANO_ROOT . "/install/includes/payload.php" ) )
-      return false;
-    
-    return true;
-  }
-  
-  echo '<h3>' . $lang->get('upgrade_post_status_title') . '</h3>';
-  echo '<p>' . $lang->get('upgrade_post_status_body') . '</p>';
-  
-  start_install_table();
-  run_installer_stage('load', $lang->get('install_stg_load_title'), 'stg_load_files', $lang->get('install_stg_load_body'), false);
-  run_installer_stage('importlang', $lang->get('install_stg_importlang_title'), 'stg_lang_import', $lang->get('install_stg_importlang_body'));
-  run_installer_stage('flushcache', $lang->get('upgrade_stg_flushcache_title'), 'stg_flush_cache', $lang->get('upgrade_stg_flushcache_body'));
-  run_installer_stage('setversion', $lang->get('upgrade_stg_setversion_title'), 'stg_set_version', $lang->get('upgrade_stg_setversion_body'));
-  close_install_table();
-  
-  // demote privileges
-  $session->logout(USER_LEVEL_ADMIN);
-  
-  $link_home = makeUrl(get_main_page(), false, true);
-  echo '<h3>' . $lang->get('upgrade_post_status_finish_title') . '</h3>';
-  echo '<p>' . $lang->get('upgrade_post_status_finish_body', array('mainpage_link' => $link_home)) . '</p>';
+	// verify version
+	if ( enano_version() != 'upg-' . installer_enano_version() )
+	{
+		echo '<p>' . $lang->get('upgrade_err_post_not_available') . '</p>';
+		$ui->show_footer();
+		$db->close();
+		exit();
+	}
+	
+	function stg_load_files()
+	{
+		if ( !@include( ENANO_ROOT . "/install/includes/payload.php" ) )
+			return false;
+		
+		return true;
+	}
+	
+	echo '<h3>' . $lang->get('upgrade_post_status_title') . '</h3>';
+	echo '<p>' . $lang->get('upgrade_post_status_body') . '</p>';
+	
+	start_install_table();
+	run_installer_stage('load', $lang->get('install_stg_load_title'), 'stg_load_files', $lang->get('install_stg_load_body'), false);
+	run_installer_stage('importlang', $lang->get('install_stg_importlang_title'), 'stg_lang_import', $lang->get('install_stg_importlang_body'));
+	run_installer_stage('flushcache', $lang->get('upgrade_stg_flushcache_title'), 'stg_flush_cache', $lang->get('upgrade_stg_flushcache_body'));
+	run_installer_stage('setversion', $lang->get('upgrade_stg_setversion_title'), 'stg_set_version', $lang->get('upgrade_stg_setversion_body'));
+	close_install_table();
+	
+	// demote privileges
+	$session->logout(USER_LEVEL_ADMIN);
+	
+	$link_home = makeUrl(get_main_page(), false, true);
+	echo '<h3>' . $lang->get('upgrade_post_status_finish_title') . '</h3>';
+	echo '<p>' . $lang->get('upgrade_post_status_finish_body', array('mainpage_link' => $link_home)) . '</p>';
 }
 else
 {
-  ?>
-  <h3><?php echo $lang->get('upgrade_confirm_title'); ?></h3>
-  <p><?php echo $lang->get('upgrade_confirm_body', array('enano_version' => installer_enano_version())); ?></p>
-  <ul>
-    <li><?php echo $lang->get('upgrade_confirm_objective_backup_fs', array('dir' => ENANO_ROOT)); ?></li>
-    <li><?php echo $lang->get('upgrade_confirm_objective_backup_db', array('dbname' => $dbname)); ?></li>
-  </ul>
-  <?php
-  if ( $do_langimport && !preg_match('/1\.0/', enano_version()) ):
-  ?>
-  <div class="warning-box" style="margin: 10px 0;">
-    <?php echo $lang->get('upgrade_confirm_warning_langimport'); ?>
-  </div>
-  <?php
-  endif;
-  ?>
-  <form method="get" action="upgrade.php" style="text-align: center;">
-    <input type="hidden" name="auth" value="<?php echo $session->sid_super; ?>" />
-    <button name="stage" value="pimpmyenano" class="submit" style="padding-bottom: 8px;">
-    <img src="images/icons/pimp.png" style="position: relative; top: 6px;" />
-      <?php echo $lang->get('upgrade_confirm_btn_upgrade'); ?>
-    </button>
-  </form>
-  <?php
+	?>
+	<h3><?php echo $lang->get('upgrade_confirm_title'); ?></h3>
+	<p><?php echo $lang->get('upgrade_confirm_body', array('enano_version' => installer_enano_version())); ?></p>
+	<ul>
+		<li><?php echo $lang->get('upgrade_confirm_objective_backup_fs', array('dir' => ENANO_ROOT)); ?></li>
+		<li><?php echo $lang->get('upgrade_confirm_objective_backup_db', array('dbname' => $dbname)); ?></li>
+	</ul>
+	<?php
+	if ( $do_langimport && !preg_match('/1\.0/', enano_version()) ):
+	?>
+	<div class="warning-box" style="margin: 10px 0;">
+		<?php echo $lang->get('upgrade_confirm_warning_langimport'); ?>
+	</div>
+	<?php
+	endif;
+	?>
+	<form method="get" action="upgrade.php" style="text-align: center;">
+		<input type="hidden" name="auth" value="<?php echo $session->sid_super; ?>" />
+		<button name="stage" value="pimpmyenano" class="submit" style="padding-bottom: 8px;">
+		<img src="images/icons/pimp.png" style="position: relative; top: 6px;" />
+			<?php echo $lang->get('upgrade_confirm_btn_upgrade'); ?>
+		</button>
+	</form>
+	<?php
 }
 
 $ui->show_footer();
--- a/language/english/admin.json	Sun Mar 28 21:49:26 2010 -0400
+++ b/language/english/admin.json	Sun Mar 28 23:10:46 2010 -0400
@@ -11,1233 +11,1233 @@
  */
 
 var enano_lang = {
-  categories: [
-    'meta', 'adm', 'acl', 'adminusers',
-    'acphome', 'acpgc', 'acpup', 'acpft', 'acppl', 'acppm', 'acped', 'acpdb', 'acplm', 'acppg', 'acpum', 'acpug', 'acpcp', 'acpmm', 'acpsl',
-    'acpcm', 'acpbc', 'acplo', 'acptm', 'acpur', 'sbedit',
-  ],
-  strings: {
-    meta: {
-      adm: 'Administration panel nav menu',
-      acl: 'Access control list editor',
-      acphome: 'ACP: Home',
-      acpgc: 'ACP: General configuration',
-      acpup: 'ACP: File uploads',
-      acpft: 'ACP: Allowed file types',
-      acppm: 'ACP: Manage pages',
-      acped: 'ACP: Edit page content',
-      acptm: 'ACP: Theme manager',
-      acppl: 'ACP: Manage plugins',
-      acpdb: 'ACP: Database backup',
-      acplm: 'ACP: Language manager',
-      acpcm: 'ACP: Cache settings',
-      acppg: 'ACP: Page groups',
-      acpum: 'ACP: User management',
-      acpur: 'ACP: User rank management',
-      acpug: 'ACP: User group management',
-      acpcp: 'ACP: COPPA support',
-      acpmm: 'ACP: Mass e-mail',
-      acpsl: 'ACP: Security log',
-      acpbc: 'ACP: Ban control',
-      acplo: 'ACP: Logout page',
-      sbedit: 'Sidebar editor',
-    },
-    adm: {
-      page_tagline: 'Administer your Enano website.',
-      
-      cat_general: 'General',
-      cat_content: 'Content',
-      cat_appearance: 'System and maintenance',
-      cat_users: 'Users',
-      cat_security: 'Security',
-      cat_plugins: 'Plugin configuration',
-      
-      page_general_config: 'General configuration',
-      page_file_uploads: 'File uploads',
-      page_file_types: 'Allowed file types',
-      
-      page_manager: 'Manage pages',
-      page_editor: 'Edit page content',
-      page_pg_groups: 'Manage page groups',
-      
-      page_themes: 'Manage themes',
-      page_plugins: 'Manage plugins',
-      page_db_backup: 'Backup database',
-      page_lang_manager: 'Language manager',
-      page_cache_manager: 'Cache settings',
-      
-      page_users: 'Manage users',
-      page_user_groups: 'Edit user groups',
-      page_coppa: 'COPPA support',
-      page_mass_email: 'Mass e-mail',
-      page_user_ranks: 'User ranks and titles',
-      
-      page_security_log: 'Security log',
-      page_ban_control: 'Ban control',
-      
-      btn_home: 'Administration panel home',
-      btn_logout: 'Log out of admin panel',
-      btn_keepalive_off: 'Turn on keep-alive',
-      btn_keepalive_on: 'Turn off keep-alive',
-      btn_keepalive_about: 'About keep-alive',
-      btn_keepalive_loading: 'Loading keep-alive button...',
-      
-      err_not_auth_title: 'Error: Not authenticated',
-      err_not_auth_body: 'It looks like your administration session is invalid or you are not authorized to access this administration page. Please <a href="%login_link%">re-authenticate</a> to continue.',
-    },
-    acl: {
-      err_access_denied: 'You are not authorized to view or edit access control lists.',
-      err_missing_template: 'It seems that (a) the file acledit.tpl is missing from this theme, and (b) the JSON response is working.',
-      err_user_not_found: 'The username you entered was not found.',
-      err_bad_group_id: 'The group ID you submitted is not valid.',
-      err_demo: 'Editing access control lists is disabled in the administration demo.',
-      err_zero_list: 'Supplied rule list has a length of zero',
-      err_pleaseselect_targettype: 'Please select a target type.',
-      err_pleaseselect_username: 'Please enter a username.',
-      err_select_preset: 'Please select a preset to load.',
-      err_preset_name_empty: 'Please enter a name for this preset.',
-      err_preset_is_blank: 'The preset you entered seems completely empty (i.e. all permissions set to "inherit")',
-      
-      radio_usergroup: 'A usergroup',
-      radio_user: 'A specific user',
-      radio_scope_thispage: 'Only this page',
-      radio_scope_wholesite: 'The entire website',
-      radio_scope_pagegroup: 'A group of pages',
-      
-      lbl_scope: 'What should this access rule control?',
-      lbl_welcome_title: 'Manage page access',
-      lbl_welcome_body: 'Please select who should be affected by this access rule.',
-      lbl_trace_title: 'View effective permissions',
-      lbl_trace_body: 'See what permissions are effective and where. <a href="http://docs.enanocms.org/Help:4.2" onclick="window.open(this.href); return false;">Learn about precedence and scope</a>',
-      lbl_editwin_title_create: 'Create access rule',
-      lbl_editwin_title_edit: 'Editing permissions',
-      lbl_editwin_body: 'This panel allows you to edit what the %target_type% "<b>%target%</b>" can do on <b>%scope_type%</b>. Unless you set a permission to "Deny", these permissions may be overridden by other rules.',
-      lbl_deleterule: 'Delete this rule',
-      lbl_save_success_title: 'Permissions updated',
-      lbl_save_success_body: 'The permissions for %target_name% on this page have been updated successfully. If you changed permissions that affect your user account, you may not see changes until you reload the page.',
-      lbl_delete_success: 'Rule deleted. ',
-      lbl_preset_load_title: 'Load a preset',
-      lbl_preset_load: 'Select a preset...',
-      lbl_preset_save_title: 'Enter a name for this preset',
-      lbl_field_inherit: 'Inherit',
-      lbl_field_deny: 'Deny',
-      lbl_field_disallow: 'Disallow',
-      lbl_field_wikimode: 'Wiki mode',
-      lbl_field_allow: 'Allow',
-      lbl_help: '<p>
-                   <b>Permission types:</b>
-                 </p>
-                 <ul>
-                   <li><b>Allow</b> means that the user is allowed to access the item</li>
-                   <li><b>Wiki mode</b> means the user can access the item if wiki mode is active (per-page wiki mode is taken into account)</li>
-                   <li><b>Disallow</b> means the user is denied access unless something allows it.</li>
-                   <li><b>Deny</b> means that the user is denied access to the item. This setting overrides all other permissions.</li>
-                   <li><b>Inherit</b> forces the permission to be unset and thus inherited from the defaults. Setting every permission to Inherit is the same as deleting the rule.</li>
-                 </ul>',
-      lbl_trace_user: 'See permissions for:',
-      lbl_trace_page: 'On page:',
-      
-      scope_type_wholesite: 'this entire site',
-      scope_type_thispage: 'this page',
-      scope_type_pagegroup: 'this group of pages',
-      
-      target_type_user: 'user',
-      target_type_group: 'group',
-      
-      msg_guest_howto: 'To edit permissions for guests, select "a specific user", and enter Anonymous as the username.',
-      msg_deleterule_confirm: 'Do you really want to delete this rule?',
-      msg_closeacl_confirm_title: 'Close the ACL manager?',
-      msg_closeacl_confirm_body: 'This will cancel any changes that you haven\'t saved.',
-      msg_deny_everyone_confirm: 'CAUTION: You are setting a Deny ruling for everyone on this site. This will block the selected actions from being performed at all. Do you really want to do this?\n\nPlease also note that the following core pages cannot be blocked: Special:Login, Special:Logout, and Special:LangExportJSON.',
-      
-      msg_scale_intro_title: 'Existing ACL rules',
-      msg_scale_intro_body: 'This is a list of all the existing ACL rules on your site. You can click an entry to edit it. Selecting the same criteria in the user and group selection page will also allow editing a rule.',
-      
-      msg_scale_allow: 'Allow',
-      msg_scale_mostly_allow: 'Mostly allow',
-      msg_scale_some_allow: 'Some allow',
-      msg_scale_mixed: 'Mixed',
-      msg_scale_some_deny: 'Some deny',
-      msg_scale_mostly_deny: 'Mostly deny',
-      msg_scale_deny: 'Deny',
-      
-      msg_list_user: '(User: %username%)',
-      msg_list_group: '(Group: %group%)',
-      msg_list_on_page: ' on page: %page_name%',
-      msg_list_on_page_group: ' for page group: %page_group%',
-      msg_list_entire_site: ' for the entire site',
-      msg_list_score: 'Score: %score% (%desc%) %info%',
-      msg_no_presets: 'No presets are defined. Define a preset by setting all the ACL settings to what you want, and then hitting Save. <a %close_flags%>Close</a>',
-      msg_debug_main_title: 'View effective permissions',
-      msg_debug_main_body: 'This tool allows you to see what actual permissions are in use. It can be helpful if you are struggling to determine why a certain action is being allowed or denied. There are two views available for this window: you can either view the information sorted by individual actions, or group actions by which rule sets them.',
-      msg_trace_key: 'Color guide',
-      msg_failed_deps: 'Failed dependencies: ',
-      
-      btn_success_dismiss: 'dismiss',
-      btn_success_close: 'close manager',
-      btn_deleterule: 'Delete rule',
-      btn_createrule: 'Create rule',
-      btn_returnto_editor: 'Return to ACL editor',
-      btn_returnto_userscope: 'Return to user/scope selection',
-      btn_show_existing: '&raquo; View existing rules',
-      btn_view_effective: '&raquo; Show diagnostic tools',
-      btn_close: 'Close ACL wizard',
-      btn_edit_presets: 'Presets: <a %load_flags%>Load</a> | <a %save_flags%>Save</a>',
-      btn_load_preset: 'Load preset',
-      btn_save_preset: 'Save preset',
-      btn_edit_rule: 'edit',
-      btn_view_key: 'View color key',
-      btn_sort_perm: 'Sort individually',
-      btn_sort_rule: 'Sort by rule',
-      
-      inherit_enano_default: 'Enano defaults',
-      inherit_global_everyone: 'Rule for everyone on the entire site',
-      inherit_global_group: 'Rule for the group "%group_name%" on the entire site',
-      inherit_global_user: 'Rule for this user on the entire site',
-      inherit_pg_everyone: 'Rule for everyone in the page group "%pg_name"',
-      inherit_pg_group: 'Rule for the group "%group_name%" in the page group "%pg_name%"',
-      inherit_pg_user: 'Rule for this user in the page group "%pg_name%"',
-      inherit_local_everyone: 'Rule for everyone on this page',
-      inherit_local_group: 'Rule for the group "%group_name%" on this page',
-      inherit_local_user: 'Rule for this user on this page',
-      
-      inherit_key_enano_default: '%this.acl_inherit_enano_default% (most broad)',
-      inherit_key_global_everyone: '%this.acl_inherit_global_everyone%',
-      inherit_key_global_group: 'Rule for a user group on the entire site',
-      inherit_key_global_user: '%this.acl_inherit_global_user%',
-      inherit_key_pg_everyone: 'Rule for everyone in a specific page group',
-      inherit_key_pg_group: 'Rule for a usergroup on a specific page group',
-      inherit_key_pg_user: 'Rule for this user in a specific page group',
-      inherit_key_local_everyone: '%this.acl_inherit_local_everyone%',
-      inherit_key_local_group: 'Rule for a specific usergroup on this page',
-      inherit_key_local_user: '%this.acl_inherit_local_user% (most specific)',
-    },
-    acphome: {
-      heading_main: 'Welcome to the Administration Dashboard.',
-      welcome_line1: 'Thanks for choosing Enano as your site\'s CMS! The administration panel gives you control over nearly every aspect of your site. The links to the left allow you to navigate around the panel. Below you will find some statistics on your site and any alerts you might need to attend to.',
-      
-      // Stats!
-      stat_header: 'Site statistics',
-      stat_enano_version: 'Enano CMS %version% (%releasename%)<br /><a href="%aboutlink%">View server information and license</a>',
-      stat_numpages: 'Number of pages:',
-      stat_edits: 'Number of edits made:',
-      stat_edits_data: '%edit_count% (average of %per_day% per day)',
-      stat_comments: 'Number of comments:',
-      stat_comments_data: '%comment_count% (average of %per_day% per day)',
-      stat_users: 'Number of users:',
-      stat_filesize: 'Total size of uploaded files:',
-      stat_cachesize: 'Total size of cache:',
-      stat_avatarsize: 'Space occupied by avatars:',
-      stat_dbsize: 'Database size:',
-      stat_dbsize_unsupported: 'Unsupported',
-      stat_installdate: 'Site started:',
-      stat_installdate_ago: '(%days% %this.etc_unit_days% ago)',
-      stat_lastupdate: 'Last Enano upgrade:',
-      stat_lastupdate_never: 'Never',
-      
-      heading_alerts: 'Active alerts',
-      
-      msg_demo_title: 'Enano is running in demo mode.',
-      msg_demo_body: 'If you borked something up, or if you\'re done testing, you can <a href="%reset_url%">reset this site</a>. The site is reset automatically once every two hours. When a reset is performed, all custom modifications to the site are lost and replaced with default values.',
-      
-      msg_install_files_title: 'Installer files found',
-      msg_install_files_body: 'Please delete the install/ directory from your Enano installation folder &ndash; it contains sensitive tools that might allow your site to be compromised.',
-      
-      heading_updates: 'Check for updates',
-      msg_updates_info: 'The Enano team will on occasion release new versions of Enano. We always recommend that you run the latest available version because many releases contain security patches. Enano checks for updates by looking at an <a href="%updates_url%">XML file</a> and doesn\'t share any information about your site.',
-      btn_check_updates: 'Check for updates',
-      
-      heading_inactive_users: 'Users are awaiting activation',
-      msg_inactive_users_one: '1 user has requested manual account activation. You can activate the account by going to the <a %um_flags%>User Manager</a>.',
-      msg_inactive_users_plural: '%num_users% users have requested manual account activation. You can activate those accounts by going to the <a %um_flags%>User Manager</a>.',
-      
-      msg_users_locked_out: 'Active IP address lockouts',
-      msg_users_locked_out_hint: 'The following IP addresses have been automatically locked out from login attempts. You can delete these active lockouts, if you choose.',
-      th_locked_out_ip: 'IP address',
-      th_locked_out_username: 'Username (most recent attempt)',
-      th_locked_out_status: 'Status',
-      th_locked_out_time: 'Time remaining',
-      lbl_locked_out_warned: 'Warned (failures: %fail_count%)',
-      lbl_locked_out_banned: 'Locked out',
-      btn_lockout_unblock: 'Unblock',
-      btn_lockout_clear: 'Clear',
-      msg_lockout_clear_success: 'The IP address %ip% has been cleared from the active lockout list.',
-      
-      heading_docs: 'Enano documentation',
-      msg_docs_info: 'The <a href="http://docs.enanocms.org/" onclick="window.open(this.href); return false;">Enano administrator\'s handbook</a> is maintained as a wiki. It will help you get started with Enano and learn about how we do things differently.',
-      heading_support: 'Get support',
-      msg_support_info: 'Stuck? Think you found a bug? Tell us about it! Post a message in the <a href="http://forum.enanocms.org/" onclick="window.open(this.href); return false;">Enano support forums</a> to obtain community support for Enano. You may also be interested in our <a href="http://enanocms.org/Support" onclick="window.open(this.href); return false;">other support channels</a>.',
-      
-      heading_top_pages: 'Most requested pages',
-      th_toppages_page: 'Page',
-      th_toppages_hits: 'Hits',
-      heading_seclog: 'Security log',
-      msg_seclog_info: 'This list shows the 5 most recent actions/attempted actions performed by administrators on this site. This also includes attempts to view blocked pages and use the administration panel without appropriate privileges. You can view a complete list using the link below.',
-      btn_seclog_full: 'Full security log',
-    },
-    acpgc: {
-      err_avatar_dir_invalid: 'You have entered an invalid avatar directory.',
-      err_avatar_dir_not_exist: 'The avatar directory you entered does not exist in the filesystem.',
-      msg_save_success: 'Your changes to the site configuration have been saved.',
-      
-      // Section: global site options
-      heading_main: 'Global site options',
-      heading_submain: 'These options control the entire site.',
-      field_site_name: 'Site name:',
-      field_site_desc: 'Site description:',
-      field_main_page: 'Main page:',
-      field_main_page_option_same: 'Use the same main page for everyone',
-      field_main_page_option_members: 'Use a different main page for members:',
-      field_main_page_members: 'Main page for members:',
-      field_copyright: 'Copyright notice shown on pages:',
-      field_copyright_hint: 'Hint: To make a copyright symbol (&copy;), type <tt>&amp;copy;</tt>.',
-      field_contactemail: 'Contact e-mail',
-      field_contactemail_hint: 'All e-mail sent from this site will appear to have come from the address shown here.',
-      
-      // Section: wiki mode
-      heading_wikimode: 'Wiki mode',
-      field_wikimode_intro: 'Enano can also act as a wiki, meaning anyone can edit and create pages. To enable Wiki Mode, check the box to the right.',
-      field_wikimode_info_sanitize: 'In Wiki Mode, certain HTML tags such as &lt;script&gt; and &lt;object&gt; are disabled, and all PHP code is disabled, except if the person editing the page is an administrator.',
-      field_wikimode_info_history: 'Also, Enano keeps complete page history, which makes restoring vandalized pages easy. You can also protect pages so that they cannot be edited.',
-      field_wikimode: 'Enable Wiki Mode',
-      field_editnotice_title: 'Edit page notice',
-      field_editnotice_info: 'When Wiki Mode is enabled, anyone can edit pages. Check the box below and enter a message to display it whenever the page editor is opened. Administrators often use this field to display a legal disclaimer or a notice of what license the user agrees to submit their content under.',
-      field_editnotice: 'Show a message whenever pages are edited',
-      field_edit_require_captcha_title: 'Require visual confirmation for guests editing pages',
-      field_edit_require_captcha_hint: 'If this is enabled, guests will be asked to enter a visual confirmation code before saving changes to a page.',
-      field_edit_require_captcha: 'Require guests to complete a CAPTCHA when editing pages',
-      
-      // Section: statistics and hit counting
-      heading_stats: 'Statistics and hit counting',
-      stats_intro: 'Enano has the ability to show statistics for every page on the site. This allows you to keep very close track of who is visiting your site, and from where.',
-      stats_hint_privacy: 'Unfortunately, some users don\'t like being logged. For this reason, you should state clearly what is logged (usually the username or IP address, current time, page name, and referer URL) in your privacy policy. If your site is primarily geared towards children, and you are a United States citizen, you are required to have a privacy policy stating exactly what is being logged under the terms of the Childrens\' Online Privacy Protection Act.',
-      field_stats_enable: 'Log all page hits',
-      field_stats_hint: 'This excludes special and administration pages.',
-      
-      // Section: comment system
-      heading_comments: 'Comment system',
-      field_enable_comments: 'Enable the comment system',
-      field_approve_comments: 'Require approval before article comments can be shown',
-      field_comment_allow_guests: 'Allow guests to post comments',
-      field_comment_allow_guests_yes: 'Yes',
-      field_comment_allow_guests_captcha: 'Require visual confirmation',
-      field_comment_allow_guests_no: 'No (require login)',
-      field_comment_spam_policy: 'Spam policy:',
-      field_comment_spam_policy_hint: 'This requres a <a href="http://enanocms.org/Category:Spam_filtering">spam filtering plugin</a> to be installed.',
-      field_comment_spam_policy_moderate: 'Moderate comments (default)',
-      field_comment_spam_policy_reject: 'Reject post',
-      field_comment_spam_policy_accept: 'Ignore and accept posts',
-      
-      // Section: disable site
-      heading_disablesite: 'Disable all site access',
-      field_disablesite_hint: 'Disabling the site allows you to work on the site without letting non-administrators see or use it.',
-      field_disablesite: 'Disable this site',
-      field_disablesite_message: 'Message to show to users:',
-      
-      // Section: default theme
-      heading_default_theme: 'Visual defaults',
-      field_default_theme: 'Default theme for guests and new users:',
-      
-      // Section: breadcrumbs
-      field_breadcrumb_mode: 'Enable breadcrumbs:',
-      field_breadcrumb_mode_subpages: 'Only with a slash in the URL (default)',
-      field_breadcrumb_mode_always: 'Always',
-      field_breadcrumb_mode_never: 'Never',
-      
-      // Section: CDN
-      field_cdn_path: 'URL to <acronym title="Content Delivery Network">CDN</acronym> server:',
-      field_cdn_path_hint: 'A CDN, or Content Delivery Network, allows downloading of shared Enano components from a server designed to serve out only images, CSS, and script files. Since a browser can open separate connections for the page and for images and scripts, the page loads faster. Leave this blank to just use Enano\'s local files (default).',
-      field_cdn_path_example: 'Example: <tt>http://cdn.mycompany.com/enano/',
-      
-      // Section: gzip
-      field_gzip: 'Enable gzip compression',
-      field_gzip_hint: 'Compress pages using before sending them to the user. At the cost of a small amount of CPU, this can save a lot of bandwidth. This only enables compression for HTML and some Javascript; to gzip compress CSS and images, please consult your webserver\'s documentation.',
-      field_gzip_lbl: 'Use gzip compression',
-      field_gzip_btn_check: 'Check server for support',
-      field_gzip_check_msg_server_does_it: 'Your server performs gzip compression on its own. There is no need to enable it in Enano, and if you do, you may cause Enano to stop working.',
-      field_gzip_check_msg_server_good: 'Your server is not automatically compressing PHP-generated pages, so Enano\'s compression facilities are needed.',
-      field_gzip_check_msg_php_bad: 'PHP does not have gzip support enabled.',
-      field_gzip_check_msg_php_good: 'Found gzip compression support in PHP.',
-      field_gzip_check_msg_success: 'Success - all server checks passed. You can enable gzip support.',
-      field_gzip_check_msg_failure: 'Server check failed. Unless you think Enano is wrong, don\'t enable gzip below.',
-      
-      // Main section: users and communication
-      heading_users: 'Users and communication',
-      
-      // Section: account activation
-      heading_activate: 'User account activation',
-      activate_intro_line1: 'If you would like to require users to confirm their e-mail addresses by way of account activation, you can enable this behavior here. If this option is set to "None", users will be able to register and use this site without confirming their e-mail addresses. If this option is set to "User", users will automatically be sent e-mails upon registration with a link to activate their accounts. And lastly, if this option is set to "Admin", users\' accounts will not be active until an administrator activates the account.',
-      activate_intro_line2: 'You may also disable registration completely if needed.',
-      activate_intro_sfnet_warning: 'Note: if you are using a SourceForge.net server, do not select User. SourceForge.net does not allow e-mails to be sent from project web sites.',
-      field_activate: 'Account activation:',
-      field_activate_disable: 'Disable registration',
-      field_activate_none: 'None',
-      field_activate_user: 'User',
-      field_activate_admin: 'Admin',
-      
-      // Section: terms of use (TOU)
-      heading_tou: 'Registration agreement/Terms of Use',
-      field_tou: 'Registration agreement',
-      field_tou_hint: 'The text you enter here will be displayed to users upon any attempt to create an account on this site.',
-      
-      // Section: account lockouts
-      heading_lockout: 'Account lockouts',
-      lockout_intro: 'Configure Enano to prevent or restrict logins for a specified period of time if a user enters an incorrect password a specific number of times.',
-      field_lockout_threshold: 'Lockout threshold:',
-      field_lockout_threshold_hint: 'How many times can a user enter wrong credentials before a lockout goes into effect?',
-      field_lockout_duration: 'Lockout duration:',
-      field_lockout_duration_hint: 'This is how long an account lockout should last, in minutes.',
-      field_lockout_policy: 'Lockout policy:',
-      field_lockout_policy_hint: 'What should be done when a lockout goes into effect?',
-      field_lockout_policy_nothing: 'Don\'t do anything',
-      field_lockout_policy_captcha: 'Require visual confirmation',
-      field_lockout_policy_lockout: 'Prevent all login attempts',
-      
-      // Section: password strength
-      heading_passstrength: 'Password strength',
-      field_passstrength_title: 'Enable password strength analysis',
-      field_passstrength_hint: 'This should be enabled in most cases. When this is enabled, a strength meter and a numerical score will be displayed wherever a password can be changed.',
-      field_passstrength: 'Enabled',
-      field_passminimum_title: 'Minimum strength score',
-      field_passminimum_hint: 'This is the lowest score a password will be allowed to have. -10 will allow any password. A score of under -3 is considered weak, under 1 is fair, under 4 is good, under 10 is strong, and 10 and above are very strong. The scale is open-ended. This only has an effect if the meter is enabled above.',
-      
-      // Section: e-mail
-      heading_email: 'E-mail sent from the site',
-      field_email_method: 'E-mail sending method:',
-      field_email_method_hint: 'Try using the built-in e-mail method first. If that doesn\'t work, you will need to enter valid SMTP information here.',
-      field_email_method_builtin: 'PHP\'s built-in mail() function',
-      field_email_method_smtp: 'Use an external SMTP server',
-      field_email_smtp_hostname: 'SMTP hostname:',
-      field_email_smtp_hostname_hint: 'This option only applies to the external SMTP mode.',
-      field_email_smtp_auth: 'SMTP credentials:',
-      field_email_smtp_username: 'Username:',
-      field_email_smtp_password: 'Password:',
-      
-      // Section: Sessions
-      heading_sessions: 'User sessions',
-      hint_sessions_noelev: '<b>Remember:</b> Settings here only affect normal logins - you can\'t change the length of sessions that give you elevated privileges, such as the re-authentication that occurs when you access the administration panel. <a href="http://docs.enanocms.org/Help:Appendix_B" onclick="window.open(this.href); return false;">Read about Enano\'s security model</a>.',
-      field_short_time: 'Length of short sessions in minutes:',
-      field_short_time_hint: 'This is how long a user\'s session will last when they don\'t check the "remember me" checkbox. Short sessions are automatically renewed every time a page is loaded.',
-      field_long_time: 'Length of extended sessions in days:',
-      field_long_time_hint: 'This is how long a user\'s session will last when the "remember me" checkbox is selected during their login. Long sessions can\'t be renewed - they always last a fixed amount of time. Set this to 0 to make extended sessions infinite, e.g. they are only terminated when the user logs out manually (this may present a security risk).',
-      
-      // Section: avatars
-      heading_avatars: 'Avatars',
-      avatars_intro: 'Avatars are small images that users can display on their profiles and in comments.',
-      field_avatar_enable: 'Enable avatar support:',
-      field_avatar_enable_hint: 'Supported formats are JPEG, PNG, and GIF&trade;.',
-      field_avatar_enable_label: 'Enabled',
-      field_avatar_max_filesize: 'Maximum avatar file size:',
-      field_avatar_max_filesize_hint: 'For smaller sites, the highest value for this should be about 50KB, 51200. Larger sites with more visitors will likely want to use something much smaller, such as 10KB.',
-      field_avatar_max_dimensions: 'Maximum avatar dimensions:',
-      field_avatar_max_dimensions_hint: 'The format is width &#215; height. Typically you want to have this square (the same width and height). These are only maximum dimensions; users are not prevented from having smaller images.',
-      field_avatar_allow_anim_title: 'Allow animated avatars:',
-      field_avatar_allow_anim_hint: 'If this is checked, users can upload APNG and Animated GIF&trade; avatars. Sometimes such images can be specifically made to be distracting, like rapidly flashing images. If this is unchecked, these formats will be blocked, and only still PNGs and GIFs will be allowed.',
-      field_avatar_allow_anim: 'Don\'t block animated images',
-      field_avatar_upload_methods: 'Allowed upload methods:',
-      field_avatar_upload_file: 'Allow users to upload image files from their computers',
-      field_avatar_upload_http: 'Allow users to enter a URL to their desired avatar',
-      field_avatar_upload_gravatar: 'Allow users to use their <a href="http://www.gravatar.com/" onclick="window.open(this.href); return false;">Gravatar</a>',
-      field_avatar_gravatar_rating: 'Highest allowed rating for Gravatar images:',
-      field_avatar_gravatar_rating_g: 'G (no objectionable content)',
-      field_avatar_gravatar_rating_pg: 'PG (rude gestures, immodest dress, mild swearing/violence)',
-      field_avatar_gravatar_rating_r: 'R (profanity, violence, nudity, hard drug use)',
-      field_avatar_gravatar_rating_x: 'X (hardcore sexual images, extremely disturbing violence)',
-      field_avatar_directory: 'Avatar storage directory:',
-      field_avatar_directory_hint: 'This should be relative to your Enano root and should contain only alphanumeric characters and forward slashes, even if your server runs Windows.',
-      
-      // Section: misc options
-      heading_usermisc: 'Other user options',
-      field_userpage_acl_title: 'New users can edit their user pages:',
-      field_userpage_acl_hint: 'This setting will cause Enano to grant certain rights to newly registered users, effective only on their user page. These rights include creating their page, editing their page, and posting comments. Since it generates a new ACL rule, you are able to override new permissions. This setting will only take effect on new users - it does not affect the permissions of those already registered.',
-      field_userpage_acl: 'Grant editing rights to new users on their user pages',
-      
-      // Main section: sidebar links
-      heading_sidebar: 'Sidebar links',
-      
-      // Section: promote Enano
-      heading_promoteenano: 'Promote Enano',
-      field_enano_link_title: 'Show link to enanocms.org on the sidebar',
-      field_enano_link_hint: 'You may help show your support for Enano with this link. This is entirely optional and the Enano project does not discriminate against sites that disable this button. (This differs from our policy on the "Powered by Enano" link; see <a href="http://enanocms.org/powered-link" onclick="window.open(this.href); return false;">this page</a> for more information.)',
-      field_enano_link: 'Place a link to enanocms.org on the sidebar',
-      
-      // Section: SF.net logo
-      heading_sfnet_logo: 'SourceForge.net logo',
-      sfnet_intro: 'All projects hosted by SourceForge.net are required to display an official SourceForge.net logo on their pages.  If you want to display a SourceForge.net logo on the sidebar, check the box below, enter your group ID, and select an image type.',
-      field_sfnet_display: 'Display the SourceForge.net logo on the right sidebar',
-      field_sfnet_group_id: 'Group ID:',
-      field_sfnet_logo_style: 'Logo style:',
-      field_sfnet_logo_style_1: '88x31%this.etc_unit_pixels_short%, white',
-      field_sfnet_logo_style_2: '125x37%this.etc_unit_pixels_short%, white',
-      field_sfnet_logo_style_3: '125x37%this.etc_unit_pixels_short%, black',
-      field_sfnet_logo_style_4: '125x37%this.etc_unit_pixels_short%, blue',
-      field_sfnet_logo_style_5: '210x62%this.etc_unit_pixels_short%, white',
-      field_sfnet_logo_style_6: '210x62%this.etc_unit_pixels_short%, black',
-      field_sfnet_logo_style_7: '210x62%this.etc_unit_pixels_short%, blue',
-      
-      // Section: W3C validation buttons
-      heading_w3clogos: 'W3C compliance logos',
-      w3clogos_intro: 'Enano generates (by default) Valid XHTML 1.1 code, plus valid CSS.  If you want to show this off, check the appropriate boxes below.',
-      w3clogos_btn_html32: 'HTML 3.2',
-      w3clogos_btn_html40: 'HTML 4.0',
-      w3clogos_btn_html401: 'HTML 4.01',
-      w3clogos_btn_xhtml10: 'XHTML 1.0',
-      w3clogos_btn_xhtml11: 'XHTML 1.1',
-      w3clogos_btn_css: 'CSS',
-      
-      // Section Defective By Design link
-      heading_dbd: 'Defective By Design Anti-DRM button',
-      dbd_intro: 'The Enano project is strongly against Digital Restrictions Management.',
-      dbd_explain: 'DRM infringes on consumer rights by using technical locks to prevent you from using your Fair Use rights granted by copyright law. This means that consumers are harmed in many ways, for example when they can\'t copy purchased digital media to their own devices. Furthermore, since most DRM schemes are proprietary and designed to prevent interoperability, you can be locked you into a specific brand or product. You can help spread consumer awareness and show your opposition to DRM through this button. It\'s as easy as checking the box below to place a link to <a href="http://www.defectivebydesign.org">DefectiveByDesign.org</a> on your sidebar.',
-      field_stopdrm: 'Help stop DRM by placing a link to DBD on the sidebar!',
-      
-      // Save button
-      btn_save_changes: 'Save changes'
-    },
-    acpup: {
-      heading_main: 'File upload configuration',
-      intro: 'Enano supports the ability to upload files to your website and store the files in the database. This enables you to embed images and such into pages without manually writing the HTML. However, the upload feature can sometimes pose a risk to your site, as viruses and executable files can sometimes be uploaded.',
-      field_enable: 'Enable file uploads',
-      field_max_size: 'Maximum file size:',
-      info_magick: 'You can allow Enano to generate thumbnails of images automatically. This feature requires ImageMagick to work properly. If your server does not have ImageMagick on it, Enano will try to use the GD library (if available) to scale images. This can be slower, but it works on a wider range of servers. If even that does not work, Enano will simply make your users\' browsers scale the images. In most cases this is fine, but if you are uploading large (>100KB) images and embedding them inside of pages, you should try to enable ImageMagick or configure GD because transferring these large images many times can cost you quite a lot of bandwidth.',
-      field_magick_enable: 'Use ImageMagick to scale images',
-      field_magick_path: 'Path to ImageMagick:',
-      err_magick_not_found: '<b>Warning:</b> the file "%magick_path%" was not found, and the ImageMagick file path was not updated.',
-      // Translators: for the path here, please be sure to use a double-backslash in the Windows path. Avoid translating the file paths
-      // anyway since they're generally the same even on non-English Windows systems.
-      field_magick_path_hint: 'On Linux and Unix servers, the most likely options here are /usr/bin/convert and /usr/local/bin/convert. If you server runs Windows, then ImageMagick is most likely to be C:\\Windows\\Convert.exe or C:\\Windows\\System32\\Convert.exe.',
-      info_cache: 'If you use ImageMagick to scale images, your server will be very busy constantly scaling images if your website is busy, and your site may experience slowdowns. You can dramatically speed up this scaling process if you use a directory to cache thumbnail images.',
-      info_cache_chmod: '<b>Please note:</b> the cache/ directory on your server <u>must</u> be writable by the server. While this is not usually a problem on Windows servers, most Linux/Unix servers will require you to CHMOD the cache/ directory to 777. See your FTP client\'s user guide for more information on how to do this.',
-      msg_cache_not_writable: ' <b>At present, it seems that the cache directory is not writable. The checkbox below has been disabled to maintain the stability of Enano.</b>',
-      field_cache: 'Cache thumbnailed images',
-      info_history: 'Lastly, you can choose whether file history will be saved. If this option is turned on, you will be able to roll back any malicious changes made to uploaded files, but this requires a significant amount of filesystem storage. You should probably leave this option enabled unless you have less than 250MB of disk space on your hosting account or server.',
-      field_history: 'Keep a history of uploaded files',
-      btn_save: 'Save changes',
-    },
-    acpft: {
-      // Nope. There isn't anything else. Sorry to disappoint.
-      heading_main: 'Allowed file types',
-      hint: 'Using the form below, you can decide which file types are allowed to be uploaded to this site.',
-      msg_saved: 'Your changes have been saved.',
-      msg_demo_mode: 'Hmm, enabling executables, are we? Tsk tsk. I\'d love to know what\'s in that EXE file you want to upload. OK, maybe you didn\'t enable EXEs. But nevertheless, changing allowed filetypes is disabled in the demo.',
-    },
-    acppl: {
-      heading_main: 'Plugin management',
-      intro: 'This interface allows you to control what plugins are in use on this site. Everything you do on this page will be shown in the security logs. Some plugins don\'t fully support the newer features like installation, upgrading and uninstalls. Installing these plugins will still work, but sometimes authors include setup instructions in the plugin file. You should check the file for further setup instructions once the plugin is installed.',
-      
-      lbl_plugin_name: '<b>%plugin%</b> by %author%',
-      lbl_status_installed: 'Installed',
-      lbl_status_uninstalled: 'Not installed',
-      lbl_status_system: 'System plugin',
-      lbl_status_need_upgrade: 'Disabled (needs upgrade)',
-      lbl_status_disabled: 'Disabled',
-      lbl_filename: 'Filename:',
-      lbl_plugin_site: 'Plugin homepage:',
-      lbl_author_site: 'Author homepage:',
-      lbl_version: 'Version:',
-      lbl_installed_version: 'Installed version:',
-      
-      btn_install: 'Install',
-      btn_disable: 'Disable',
-      btn_enable: 'Enable',
-      btn_upgrade: 'Upgrade',
-      btn_uninstall: 'Uninstall',
-      btn_reimport: 'Re-import strings',
-      
-      msg_confirm_uninstall: 'Uninstalling this plugin may cause the loss of data that was created with it. You should only uninstall a plugin if you are certain you\'ll have no further use for it; in fact, you don\'t even need to uninstall a plugin if you\'re deleting it from the filesystem.',
-      msg_confirm_install: 'Plugins are not supported by the Enano project and could harm your site if malicious. You should only install plugins from sources that you trust.',
-      msg_confirm_reimport: 'Re-importing strings will reload all language data from the plugin file. This should fix missing messages, but any customizations you have made to the plugin\'s language strings will be lost.',
-      
-      msg_confirm_authext_title: 'This plugin is an authentication extension.',
-      msg_confirm_authext_body: 'This plugin hooks into Enano\'s login system. It might be used to allow non-password-based authentication. If there is a security vulnerability in this plugin, it might open your site up to attack. Only continue if you trust the author of this plugin.\n\nBe sure to be vigilant in checking for updates to this plugin.',
-      
-      err_upgrade_not_supported: 'This plugin doesn\'t support automatic upgrades. The version number has been updated so the plugin will be re-enabled, but you should check the plugin file to see if the author provided instructions for finishing the upgrade.',
-      err_upgrade_bad_version: 'This plugin cannot be upgraded because you are running a version of the plugin that is not listed in the plugin\'s version list.',
-      err_upgrade_bad_target_version: 'This plugin cannot be upgraded because it does not support its own version. Please contact the author and ask them to fix this.',
-      err_upgrade_to_older: 'You are trying to upgrade to an older release of this plugin. This is unsupported and must be done manually.',
-      err_upgrade_bad_query: 'There is a problem with one of the SQL queries the plugin is trying to make.',
-      err_dmbs_not_supported: 'This plugin doesn\'t support %dbdriver% databases.',
-      err_import_no_strings: 'This plugin doesn\'t have language support.',
-      err_demo_mode: 'You can\'t manipulate plugins in the Enano demo for security reasons.',
-      
-      msg_old_entries_title: 'Import old plugin installation data',
-      msg_old_entries_body: 'There is still data from the old plugin structure in your database. You can import this to the new structure automatically using the button below.',
-      btn_import_old: 'Import old plugin settings',
-    },
-    acppm: {
-      heading_main: 'Edit page properties',
-      hint: 'This panel allows you to edit various properties of pages that aren\'t visible anywhere else. In addition to renaming pages, you can also change their <a href="http://docs.enanocms.org/Help:2.3" onclick="window.open(this.href); return false;">URL string</a> and options such as whether to index the page for searching or bypass Enano\'s template engine.',
-      err_page_not_found: 'No pages matching that search string could be found.',
-      msg_results_ambiguous_title: 'Ambiguous search results',
-      msg_results_ambiguous_body: 'Multiple pages that matched your search terms were found. Please select the page you wish to modify:',
-      ambig_btn_viewpage: 'View',
-      err_ambig_absolute: 'Your database is corrupt as it contains multiple pages with the same urlname and namespace.',
-      lbl_field_search: 'Search for a page title or URL string:',
-      heading_select_page_from_list: 'Select page from a list',
-      hint_select_page_from_list: 'You can also select the page you want to modify from the list below. The list is broken into sections of 100 pages, so if you have a lot of pages on your site, you can click the pagination control below to view more pages.',
-      
-      // Edit form
-      heading_editing: 'Editing page:',
-      lbl_page_name: 'Page\'s title:',
-      lbl_page_urlname: 'URL string:',
-      lbl_page_urlname_hint: 'No spaces, and don\'t enter the namespace prefix (e.g. User:). Changing this value is usually not a good idea, especially for templates and project pages, because it will invalidate the page\'s current URL.',
-      lbl_namespace: 'Namespace (URL prefix):',
-      ns_article: '[No prefix, default Article namespace]',
-      heading_advanced: 'Advanced options',
-      msg_file_ns_warning: '<b>WARNING:</b> Changing the namespace to something other than "File" will delete the uploaded file and all previous revisions of it.',
-      lbl_enable_comments_title: 'Allow comments to be posted on this page?',
-      lbl_enable_comments_hint: 'This option has no effect if comments are disabled globally in the administration panel. This option is enabled by default.',
-      lbl_enable_comments: 'Enable comments on this page',
-      lbl_special_title: 'Mark page as self-contained?',
-      lbl_special_hint: 'This option enables you to use your own HTML headers and other code. If you enable this, only the raw contents of the page will be displayed instead of Enano\'s full page formatting and styles. It is recommended that only advanced users enable this feature. As with other Enano pages, you may use PHP code in your pages (dependent on permissions), meaning you can use Enano\'s API on the page.',
-      lbl_special: 'Bypass the template engine for this page',
-      lbl_visible_title: 'Make page publicly listed?',
-      lbl_visible_hint: 'If you enable this option, this page will be indexed for searching and will appear in public page lists such as Special:AllPages. This option is enabled by default. Disabling this does not protect the page from unauthorized access. If you want to keep this page from being accessed without authorization, you should create a new ACL rule or password-protect the page.',
-      lbl_visible: 'Allow page to be indexed and listed',
-      lbl_protected_title: 'Protect page from edits?',
-      lbl_protected_off: 'Unprotected',
-      lbl_protected_on: 'Fully protected',
-      lbl_protected_semi: 'Semi-protected',
-      lbl_protected_hint: 'This option only has an effect if Wiki Mode is enabled. Selecting Unprotected means that any user (unless specifically blacklisted) can edit this page. Fully protected means that only administrators can edit the page. Semi-protected restricts editing to administrators and users that have been registered for at least four days.<br /><br /><b>Above all, no users except administrators can edit this page unless an ACL specifically allows it or Wiki Mode is enabled.</b>',
-      lbl_wikimode_title: 'Enable Wiki Mode for this page?',
-      lbl_wikimode_on: 'Enabled',
-      lbl_wikimode_off: 'Disabled',
-      lbl_wikimode_global: 'Inherit global setting',
-      lbl_wikimode_hint: 'By default, all pages use the Wiki Mode setting defined in General Configuration. You can override that using this field. Be aware that there are advantages and disadvantages to Wiki Mode. For example, Wiki Mode encourages collaboration, but also permits vandalism. See the <a href="http://docs.enanocms.org/Help:2.5" onclick="window.open(this.href); return false;">Enano Documentation article on Wiki Mode</a> for more information.',
-      lbl_delete_title: 'Delete this page?',
-      lbl_delete_hint: 'Remember that deleting pages is always reversible unless you clear the page\'s logs after deleting it.',
-      lbl_delete: 'Delete this page when I click Save',
-      
-      err_invalid_page_name: 'Please enter a name for the page.',
-      err_invalid_url_string: 'Please enter a URL string for the page.',
-      err_invalid_namespace: 'The namespace you selected is, for whatever reason, not valid.',
-      err_invalid_protection: 'The protection level selected is invalid.',
-      err_invalid_wiki_mode: 'The Wiki Mode level selected is invalid.',
-      err_header: 'There were one or more problems that prevented the page from being saved:',
-      delete_reason: 'Administrative deletion from admin CP; contact webmaster for details',
-      
-      msg_save_success: 'Your changes to the page have been saved. <a href="%viewpage_url%">View page &raquo;</a>',
-    },
-    acped: {
-      heading_main: 'Edit page content',
-      hint: 'This panel allows you to edit the contents of pages that are stored in the database.',
-      // The rest of this section is identical to the first parts of the acppm category by default (you can copy and paste).
-      err_page_not_found: 'No pages matching that search string could be found.',
-      msg_results_ambiguous_title: 'Ambiguous search results',
-      msg_results_ambiguous_body: 'Multiple pages that matched your search terms were found. Please select the page you wish to edit:',
-      ambig_btn_viewpage: 'View',
-      err_ambig_absolute: 'Your database is corrupt as it contains multiple pages with the same urlname and namespace.',
-      lbl_field_search: 'Search for a page title or URL string:',
-      heading_select_page_from_list: 'Select page from a list',
-      hint_select_page_from_list: 'You can also select the page you want to edit from the list below. The list is broken into sections of 100 pages, so if you have a lot of pages on your site, you can click the pagination control below to view more pages.',
-    },
-    acptm: {
-      heading_edit_themes: 'Installed themes',
-      btn_system_themes_show: '&raquo; Show system themes',
-      btn_system_themes_hide: '&raquo; Hide system themes',
-      btn_theme_edit: 'Edit',
-      btn_theme_system: 'System theme',
-      heading_install_themes: 'Themes available for installation',
-      btn_theme_install: 'Install',
-      
-      // Editor
-      heading_theme_edit: 'Editing theme: %theme_name%',
-      field_theme_name: 'Theme name:',
-      field_default_style: 'Select a default style:',
-      field_default_theme: 'Site-wide default theme:',
-      field_default_msg_current: 'This is the current default',
-      field_default_btn_make_default: 'Make this the default theme when I click Save',
-      field_disable_title: 'Disable theme: ',
-      field_disable: 'Prevent all users and guests from using this theme',
-      heading_theme_groups: 'User and group policy',
-      field_policy: 'Theme access policy:',
-      field_policy_allow_all: 'Allow everyone to use this theme',
-      field_policy_whitelist: 'Only allow the users and groups I select below',
-      field_policy_blacklist: 'Allow everyone except what I select below',
-      field_acl_heading_groups: 'Groups',
-      field_acl_heading_users: 'Users',
-      field_acl_add_user: 'Add a user: ',
-      btn_uninstall_theme: 'Uninstall theme',
-      msg_uninstall_confirm: 'Are you sure you want to uninstall this theme?',
-      
-      err_invalid_username: 'Please enter a valid username.',
-      err_username_not_found: 'The user you entered does not exist.',
-      err_save_validation_failed: 'One or more of the fields in the form is incorrect. Please ensure that you have entered a proper value for all of the fields in the theme editing form, and then click %this.etc_save_changes% again.',
-      err_uninstalling_default: 'You cannot uninstall the default theme.',
-      err_uninstalling_oxygen: 'You cannot uninstall the Oxygen theme because it\'s used for important functions such as database errors.',
-      warn_cant_disable_default: 'Please note that the theme was not disabled because it is currently the default.',
-      warn_access_with_default: 'If you selected to whitelist or blacklist certain users, that choice will not apply to guests because this is the default theme.',
-      msg_save_success: 'Your changes to this theme have been saved.',
-      msg_uninstall_success: 'The selected theme has been uninstalled.',
-    },
-    acpcm: {
-      heading_main: 'Performance and caching settings',
-      intro: 'From this page you can control what information on your site is stored in cache files. Caching speeds up Enano performance by allowing data to be retrieved from the disk instead of querying the database. Sometimes the cache isn\'t updated immediately when the database is. From this page you can control what is cached, and you can clear specific caches to force them to refresh.',
-      msg_refresh_warning: 'Some of the caches on this page will automatically refresh immediately even if you click Clear. This is because any use of the component will invoke the cache. To disable this behavior, you must disable caching site-wide.',
-      table_header: 'Cache settings',
-      lbl_enable_cache: 'Enable the cache (recommended)',
-      hint_enable_cache: 'To use the cache, the folder "cache" in the Enano root directory needs to be writable by the server. You can usually accomplish this by using your FTP client\'s CHMOD feature to set the permissions on this folder to 777. Learn more <a href="http://en.wikipedia.org/wiki/Chmod" onclick="window.open(this.href); return false;">about UNIX permissions</a>.',
-      btn_clear_all: 'Clear all caches',
-      hint_clear_all: 'To force all caches (except image thumbnails) on the site to empty, click this button. Certain caches, such as language data, must be regenerated in order to fully refresh the cache. It is recommended that you use "%this.acpcm_btn_refresh_all%" below to clear all caches and then regenerate them.',
-      btn_refresh_all: 'Refresh all caches',
-      hint_refresh_all: 'This will clear all caches (except image thumbnails) on the site to empty and then regenerate, except certain components that are cached on demand.',
-      
-      th_individual_caches: 'Individual caches',
-      btn_clear: 'Clear',
-      btn_refresh: 'Refresh',
-      
-      cache_page_desc_title: 'Page metadata',
-      cache_page_desc_body: 'Information about pages on the site is stored in this cache. This cache is updated when a page is created, renamed or deleted, and it expires every 20 minutes.',
-      cache_ranks_desc_title: 'User ranks',
-      cache_ranks_desc_body: 'Since user ranks take a long time to calculate, fully computed rank data is cached to speed loading of comments and user pages. This cache is updated when any user information is changed, and its expiry time is 15 minutes.',
-      cache_sidebar_desc_title: 'Sidebar for guest users',
-      cache_sidebar_desc_body: 'Rendering the sidebar is a CPU-intensive process because of the number of templates that must be parsed and the custom logic in sidebars. As a result, the fully rendered sidebar is cached for guests. This cache is updated when the sidebar editor is used and expires every 10 minutes.',
-      cache_plugins_desc_title: 'Plugin metadata',
-      cache_plugins_desc_body: 'Plugin files contain special information in the headers that allow Enano to read information about the plugin. Since it takes time to read and parse this information, metadata about plugins is cached. This cache is updated when any change is detected to the first 10 lines of a PHP file in the plugins/ directory.',
-      cache_template_desc_title: 'Template files',
-      cache_template_desc_body: 'Page templates have a lot of logic that takes time to process. They are compiled and then cached on disk to speed loading. This cache is cleared whenever a template file is changed. This cachs cannot be refreshed, as refreshes take place incrementally.',
-      cache_aes_desc_title: 'Encrypted session keys',
-      cache_aes_desc_body: 'Enano encrypts session keys using the AES encryption cipher. Decrypted session keys are cached on the server in a way that is inaccessible to users browsing the site. This cache is dynamically updated whenever a string is sent to the AES decryption module. Clearing it may cause a temporary increase in the load on your site and should only be done if disk space is a concern. This cache is limited to 5,000 keys; when this limit is reached, the oldest 2,500 keys are deleted.',
-      cache_lang_desc_title: 'Language strings',
-      cache_lang_desc_body: 'Language strings, or the text that makes up the Enano user interface, are cached on the disk because they take up a large amount of space in the database. Caching this information allows Enano to minimize bandwidth used when communicating with the database server.',
-      cache_js_desc_title: 'Compressed Javascript runtimes',
-      cache_js_desc_body: 'The on-page tools that Enano provides require a significant amount of Javascript code, which is compressed and cached for better load times. This cache is updated whenever any of the Javascript files are changed, but it is an incremental cache, meaning that it cannot be refreshed manually (only cleared).',
-      cache_thumbs_desc_title: 'Image thumbnails',
-      cache_thumbs_desc_body: 'Thumbnails (preview-size versions of uploaded images) can take up to 2 seconds to generate, usually increasing load times and potentially causing increased server load. This cache is refreshed on demand. Clearing this cache may cause increased loads on your server.',
-      cache_wikieditnotice_desc_title: 'Wiki mode legal notice',
-      cache_wikieditnotice_desc_body: 'This notice is rendered and cached to reduce database queries. If you use templates in your edit notice, you need to clear this cache to see the changes take effect. This cache expires every 60 minutes.',
-      
-      msg_action_success: 'The action you requested was successful.',
-      err_action_failed: 'There was an error during the requested action.'
-    },
-    acpdb: {
-      err_not_supported_title: 'Not supported',
-      err_not_supported_desc: 'This function is only supported under the MySQL database driver.',
-      err_demo_mode_title: 'Access denied',
-      err_demo_mode_desc: 'Since you\'re using the Enano demo, we can\'t allow database backups. Sorry.',
-      
-      intro: 'This page allows you to back up your Enano database should something go miserably wrong.',
-      lbl_system_tables: 'Export tables that are part of the Enano core',
-      lbl_additional_tables: 'Additional tables to export:',
-      lbl_include_structure: 'Include table structure',
-      lbl_include_data: 'Include table data',
-      btn_create_backup: 'Create backup',
-    },
-    acplm: {
-      // Language installation
-      heading_install: 'Languages available for installation',
-      col_lang_id: 'ID',
-      col_lang_code: 'Shorthand code',
-      col_lang_name: 'Language name (native)',
-      col_lang_name_eng: 'Language name (English)',
-      btn_install_language: 'Install',
-      err_lang_install_demo: 'Modifying, installing, and uninstalling languages is disabled in the demo for security reasons.',
-      msg_lang_install_success: 'The language pack %lang_name% has been installed.',
-      
-      // Editor portal
-      heading_editor_portal: 'Edit installed languages',
-      portal_btn_edit: 'Modify',
-      portal_btn_unin: 'Uninstall',
-      
-      // Properties table
-      heading_modify: 'Edit language info',
-      th_lang_basic: 'Basic language properties',
-      field_lang_name_native: 'Language name (native):',
-      field_lang_name_english: 'Language name (in English):',
-      field_lang_code: 'Shorthand code:',
-      field_lang_code_hint: 'You can\'t change this because it needs to be compliant with <a onclick="window.open(this.href); return false;" href="http://en.wikipedia.org/wiki/ISO_639-3">ISO 639-3</a>.',
-      msg_basic_save_success: 'Changes saved.',
-      
-      // String editor portal
-      heading_edit_strings_portal: 'Edit strings',
-      msg_edit_strings_portal_intro: 'You can edit the actual language strings used in this language. Be sure to preserve any variables (in the format of %variable_name%) even if you\'re translating a language. If you\'re translating all of Enano into a new language, you should edit the JSON files instead of using this console, so that comments in the language files can be preserved.',
-      btn_edit_strings_portal: 'Edit strings &raquo;',
-      
-      // Re-import button and explanation
-      heading_reimport_portal: 'Re-import this language',
-      msg_reimport_portal_intro: 'If you accidentally changed a lot of strings, you can re-import this language from the original language files. This will <b>destroy</b> any modifications you have made to the default set of strings, but any strings you\'ve added will be preserved. This is almost the same effect as re-installing the language. Don\'t stop this process while it\'s running, the re-import can take a long time.',
-      btn_reimport: 'Re-import language',
-      msg_reimport_success: 'The language was re-imported successfully. All Enano preset strings for this language have been restored.',
-      
-      // String editor
-      editor_heading: 'Editing category: %cat_name%',
-      editor_col_string_name: 'String name',
-      editor_col_string_content: 'String',
-      editor_btn_revert: 'Revert',
-      editor_btn_cancel: 'Cancel',
-      msg_string_save_success: 'Your changes have been saved.',
-      
-      // Backup interface
-      heading_backup: 'Backup language',
-      backup_intro: 'You can back up this language to make preserving custom strings easier if you ever migrate your Enano installation or re-install. Backed-up language files can be restored by using FTP or equivalent to copy the backup file to the language\'s folder, renaming it to "backup.json", and re-importing the language.',
-      btn_create_backup: 'Download backup',
-      
-      // Uninstaller
-      uninstall_confirm_title: 'Confirm uninstallation of language',
-      uninstall_confirm_body: 'Do you really want to uninstall this language? If you continue, all users that have selected this language will be reset to use the default. It is recommended that you create a backup of this language before you uninstall it if you have changed any strings.',
-      btn_uninstall_confirm: 'Confirm uninstallation',
-      btn_uninstall_cancel: 'Cancel',
-      err_cant_uninstall_default: 'You cannot uninstall the default language.',
-      msg_uninstall_success: 'The language has been uninstalled.',
-    },
-    acppg: {
-      // Main menu
-      heading_main: 'Manage page groups',
-      hint_intro: 'Enano\'s page grouping system allows you to build sets of pages that can be controlled by a single ACL rule. This makes managing features such as a members-only section of your site a lot easier. If you don\'t use the ACL system, you probably don\'t need to use page groups.',
-      col_group_name: 'Group name',
-      col_type: 'Type',
-      col_target: 'Target',
-      col_actions: 'Actions',
-      gtype_catlink: 'Link to category',
-      gtype_tagged: 'Group of pages with one tag',
-      gtype_static: 'Static group of pages',
-      gtype_regex: 'Regular expression match',
-      gtype_regex_long: 'Perl-compatible regular expression (advanced)',
-      lbl_tag: 'Tag:',
-      lbl_category: 'Category:',
-      lbl_regex: 'Expression:',
-      btn_edit: 'Edit',
-      btn_delete: 'Delete',
-      msg_no_groups: 'No page groups defined.',
-      btn_create_new: 'Create new group',
-      
-      // Creation form
-      err_no_cats: 'There aren\'t any categories on this site.',
-      th_create: 'Create page group',
-      field_group_name: 'Group name:',
-      field_group_name_hint: 'This should be short, descriptive, and human-readable.',
-      field_group_type: 'Group type:',
-      
-      field_member_pages: 'Member pages:',
-      field_member_pages_hint: 'Click the "plus" button to add more fields.',
-      field_target_category: 'Include pages in this category:',
-      field_target_category_hint: 'Pages in subcategories are <u>not</u> included, however subcategory pages themselves are.',
-      field_target_category_hint2: '<b>Reminder:</b> Enano does not automatically place any access controls on the category. If you don\'t want users to be able to freely add and remove pages from the category (assuming Wiki Mode is enabled for the category) then you need to enable protection on the category using the button on the more options menu.',
-      field_target_tag: 'Include pages with this tag:',
-      field_target_regex: 'Regular expression:',
-      field_target_regex_hint: 'Be sure to include the starting and ending delimiters and any flags you might need.<br />
-                                These pages might help: <a href="http://us.php.net/manual/en/reference.pcre.pattern.modifiers.php">Pattern modifiers</a> &bull; <a href="http://us.php.net/manual/en/reference.pcre.pattern.syntax.php">Pattern syntax</a><br />
-                                Examples: <tt>/^(Special|Admin):/i</tt> &bull; <tt>/^Image:([0-9]+)$/</tt><br />
-                                Developers, remember that this will be matched against the full page identifier string. This means that <tt>/^About_Enano$/</tt> will NOT match the page Special:About_Enano.',
-      btn_create_finish: 'Create page group',
-      
-      err_need_name: 'Please enter a name for the page group.',
-      err_need_tag: 'Please enter a page tag.',
-      err_need_cat: 'Please create a category page before linking a page group to a category.',
-      err_need_page: 'Please specify at least one page to place in this group.',
-      err_need_regex: 'Please specify a regular expression to match page IDs against.',
-      msg_create_success: 'The page group "%group_name%" has been created.',
-      
-      // Delete form
-      th_delete_confirm: 'Confirm deletion',
-      msg_delete_confirm: 'Are you sure you want to delete this page group?',
-      btn_delete_confirm: 'Yes, delete group',
-      msg_delete_success: 'The group "%pg_name%" has been deleted.',
-      
-      // Editor
-      th_editing_group: 'Editing page group:',
-      btn_save_name: 'Save group name',
-      th_remove_selected: 'Remove pages from this group',
-      field_remove: '<b>Remove</b> pages:',
-      btn_do_remove: 'Remove selected',
-      btn_save_update: 'Save and update',
-      btn_cancel_all: 'Cancel all changes',
-      th_onthefly: 'On-the-fly tools',
-      field_add_page: 'Add page:',
-      field_add_page_hint: 'You can add multiple pages by entering part of a page title, and it will be auto-completed. Press Enter to quickly add the page. This only works if you a really up-to-date browser.',
-      
-      // Validation messages and errors
-      err_ajaxadd_need_title: 'Please enter a page title.',
-      err_ajaxadd_already_in: 'The page you are trying to add is already in this group.',
-      ajaxadd_success: 'The page has been added to the specified group.',
-      err_save_need_name: 'Please enter a valid name for this group.',
-      msg_save_name_updated: 'The group name was updated successfully.',
-      err_save_need_tag: 'Please enter a valid tag.',
-      msg_save_tag_updated: 'The affecting tag was updated.',
-      err_save_need_regex: 'Please enter an expression to match against.',
-      msg_save_regex_updated: 'The expression to match against was updated.',
-      err_save_bad_category: 'No category ID specified on POST URI.',
-      msg_save_cat_updated: 'The affecting category was updated.',
-      err_save_no_pages: 'No pages were selected for deletion, and thus none were deleted.',
-      msg_save_pages_deleted: 'The requested page group members have been deleted.',
-    },
-    acpum: {
-      heading_main: 'User administration panel',
-      hint_intro: 'From this panel you can modify or delete user accounts.',
-      field_search_user: 'Search for user:',
-      field_search_user_hint: 'If your browser supports AJAX, this will provide suggestions for you.',
-      btn_search_user_go: 'Go',
-      heading_clear_sessions: 'Clear session key table',
-      hint_clear_sessions: 'It\'s a good idea to clean out your session keys table every once in a while, since this helps to reduce database size. During this process you will be logged off and (hopefully) logged back on automatically. If you do this, all users besides you will be logged off, so be sure to do this at a time when traffic is low.',
-      btn_clear_sessions: 'Clear session keys',
-      
-      heading_activation_one: '1 user is awaiting account activation',
-      heading_activation_plural: '%count% users are awaiting account activation',
-      col_activate_timestamp: 'Date of request',
-      col_activate_requestedby: 'Requested by',
-      col_activate_requestedfor: 'Requested for',
-      col_activate_coppauser: 'COPPA user',
-      col_activate_actions: 'Actions',
-      coppauser_yes: 'Yes',
-      coppauser_no: 'No',
-      btn_activate_now: 'Activate now',
-      btn_send_email: 'Send activation e-mail',
-      btn_activate_deny: 'Deny request',
-      msg_activate_success: 'The user account "%username%" has been activated.',
-      err_activate_fail: 'The user account %username% has NOT been activated, possibly because the account is already active.',
-      msg_activate_email_success: 'The user %username% has been sent an e-mail with an activation link.',
-      err_activate_email_fail: 'The user account %username% has not been activated, probably because of a bad SMTP configuration.',
-      msg_activate_deny_success: 'All activation requests for the user %username% have been deleted.',
-      
-      msg_sessionclear_success: 'The session key table has been cleared. Your database should be a little bit smaller now.',
-      err_sessionclear_demo: 'Sorry Charlie, no can do. You might mess up other people logged into the demo site.',
-      
-      // VALIDATION ERRORS
-      err_bad_username: 'The username you entered could not be found.',
-      err_validation_fail: 'Your request could not be processed due to the following validation errors:',
-      err_nosave_demo: 'Users cannot be modified or deleted in demo mode.',
-      msg_delete_success: 'The user account has been deleted.',
-      // Note the difference between this and err_bad_username. err_bad_username is shown when the username entered
-      // doesn't match any usernames in the database (e.g. no search results); err_illegal_username is shown when
-      // the admin tries to change the username to one that has illegal characters in it.
-      err_illegal_username: 'The username you entered contains invalid characters.',
-      err_no_aes_key: 'Session manager denied public encryption key lookup request',
-      err_illegal_email: 'You have entered an invalid e-mail address.',
-      msg_save_success: 'Your changes have been saved.',
-      
-      // EDITOR SMARTFORM
-      heading_editing_user: 'Editing user:',
-      heading_basic_options: 'Basic options',
-      
-      field_username: 'Username:',
-      field_username_hint: 'Must be at least 2 characters in length',
-      msg_same_user_username: 'You cannot change your own username. To change your username you must log into a different administrative account.',
-      
-      field_password: 'Password:',
-      field_password_hint: 'Password strength requirements are not enforced here.',
-      msg_password_unchanged: 'Password will be left unchanged.',
-      btn_reset_password: 'Reset password...',
-      msg_same_user_password: 'To change your password, please use the user preferences panel.',
-      field_password_title: 'Change password to:',
-      field_newpassword: 'New password:',
-      field_newpassword_confirm: 'Confirm:',
-      
-      field_email: 'E-mail address:',
-      msg_same_user_email: 'To change your e-mail address, please use the user preferences panel.',
-      
-      field_realname: 'Real name:',
-      msg_same_user_realname: 'To change your real name on file, please use the user preferences panel.',
-      
-      field_signature: 'Signature:',
-      
-      field_usertitle: 'User title:',
-      field_usertitle_hint: 'Displayed below the avatar or username, and above the rank.',
-      
-      heading_imcontact: 'Instant messenger contact information',
-      
-      field_aim: 'AIM handle:',
-      field_wlm: '<acronym title="Windows&trade; Live Messenger">WLM</acronym> handle:',
-      field_wlm_hint: 'If you don\'t specify the domain (@whatever.com), "@hotmail.com" will be assumed.',
-      field_yim: 'Yahoo! IM handle:',
-      field_xmpp: 'Jabber&trade;/XMPP handle:',
-      
-      heading_contact_extra: 'Extra contact information',
-      
-      field_homepage: 'Homepage:',
-      field_homepage_hint: 'Please remember the http:// prefix.',
-      field_location: 'Location:',
-      field_job: 'Job:',
-      field_hobbies: 'Hobbies:',
-      field_email_public: 'E-mail address is public',
-      field_email_public_hint: 'If this is checked, the user\'s e-mail address will be displayed on your the page. To protect the address from spambots, it will be encrypted.',
-      
-      avatar_heading: 'Avatar settings',
-      avatar_image_none: 'This user does not currently have an avatar.',
-      avatar_lbl_change: 'Change avatar:',
-      avatar_lbl_keep: 'Keep current setting',
-      avatar_lbl_remove: 'Delete this user\'s avatar',
-      avatar_lbl_set_http: 'Replace avatar using a new image from a URL',
-      avatar_lbl_set_file: 'Replace avatar using a new image from my computer',
-      avatar_lbl_set_gravatar: 'Replace avatar with Gravatar',
-      
-      heading_adminonly: 'Administrator-only options',
-      
-      field_active_title: 'User account is active',
-      field_active_hint: 'If this is unchecked, the existing activation key will be overwritten in the database, thus invalidating any activation e-mails sent to the user.',
-      field_active: 'Account is active and enabled',
-      field_userlevel: 'User\'s site access level',
-      field_userlevel_hint: 'If this is changed, the relevant group memberships will be updated accordingly.',
-      field_userrank: 'User\'s rank:',
-      field_userrank_hint: 'You can create more ranks using User Ranks and Titles. Ranks do not affect permissions (e.g. setting their rank to Administrator here will not give them administrator permissions).',
-      field_reg_ip: 'Registered from IP:',
-      
-      field_deleteaccount_title: 'Delete user account',
-      field_deleteaccount: 'Permanently delete this user account when I click Save',
-      msg_delete_own_account: '<blink style="color: red;">WARNING!</blink> This will delete your own user account!',
-      field_deleteaccount_hint: 'Even if you delete this user account, the username will be shown in page edit history, comments, and other areas of the site. Deleting a user account CANNOT BE UNDONE and should only be done in extreme circumstances. If the user has violated the site policy, deleting the account will not prevent him from using the site or creating a new account, for that you need to add a new ban rule.',
-      
-      btn_save: 'Save changes',
-    },
-    acpug: {
-      heading_main: 'Manage Usergroups',
-      heading_edit_existing: 'Edit an existing group',
-      btn_edit_stage1: 'Edit group',
-      heading_create_new: 'Create a new group',
-      field_group_name: 'Group name:',
-      field_group_rank: 'Assigned rank:',
-      btn_create_stage1: 'Continue',
-      
-      // Edit form
-      heading_edit_name: 'Edit group basics',
-      btn_cant_delete: 'Can\'t delete system group',
-      btn_delete_group: 'Delete this group',
-      btn_save_name: 'Save changes',
-      heading_edit_members: 'Edit group members',
-      msg_no_members: 'This group has no members.',
-      lbl_member_mod: 'Mod',
-      btn_remove_member: 'Remove member',
-      heading_add_member: 'Add a new member',
-      field_username: 'Username:',
-      field_make_mod: 'Is a group moderator',
-      field_make_mod_hint: '(can add and delete other members)',
-      btn_add_user: 'Add user to group',
-      
-      // Create form
-      err_group_name_invalid: 'The group name you chose is invalid.',
-      heading_creating_group: 'Creating group:',
-      field_group_mod: 'Group moderator',
-      field_group_type: 'Group status',
-      btn_create_stage2: 'Create group',
-      err_already_exist: 'The group name you entered already exists.',
-      err_bad_username: 'The username you entered could not be found.',
-      err_bad_insert_id: 'The group ID could not be looked up.',
-      heading_info: 'Information',
-      msg_create_success: 'The group %g_name% has been created successfully.',
-      
-      // More editor bits, validation messages
-      err_nodelete_system_group: 'The group "%g_name%" could not be deleted because it is a system group required for site functionality.',
-      msg_delete_success: 'The group "%g_name%" has been deleted. Return to the <a %a_flags%>group manager</a>.',
-      msg_name_update_success: 'The group name and rank have been updated.',
-      msg_user_added: 'The user "%username%" has been added to this usergroup.',
-      err_username_not_exist: '<b>The user "%username%" could not be added.</b><br />This username does not exist.',
-    },
-    acpcp: {
-      heading_main: 'Background information',
-      intro: 'The United States Childrens\' Online Privacy Protection Act (COPPA) was a law passed in 2001 that requires sites oriented towards children under 13 years old or with a significant amount of under-13 children clearly state what information is being collected in a privacy policy and obtain authorization from a parent or legal guardian before allowing children to use the site. Enano provides an easy way to allow you, as the website administrator, to obtain this authorization.',
-      msg_save_success: 'Your changes have been saved.',
-      th_form: 'COPPA support',
-      field_enable_title: 'Enable COPPA support:',
-      field_enable: 'COPPA enabled',
-      field_enable_hint: 'If this is checked, users will be asked if they are under 13 years of age before registering',
-      field_address: 'Your mailing address:',
-      field_address_hint: 'This is the address to which parents will send authorization forms.',
-    },
-    acpmm: {
-      heading_main: 'Send mass e-mail',
-      err_need_subject: 'Please enter a subject.',
-      err_need_message: 'Please enter a message.',
-      msg_send_success: 'Your message has been sent.',
-      err_send_fail: 'Could not send message for the following reason(s):',
-      err_demo: 'This function is disabled in the demo. You think demo@enanocms.org likes getting "test" mass e-mails?',
-      field_group_to: 'Send message to:',
-      field_group_to_hint: 'By default, this message will be sent to the group selected here. You may instead send the message to a specific list of users by entering them in the second row, with usernames separated by a single comma (no space).',
-      field_username: 'Usernames:',
-      field_subject: 'Subject:',
-      field_message: 'Message:',
-      btn_send: 'Send message',
-      msg_send_takeawhile: 'Please be warned: it may take a LONG time to send this message. <b>Please do not stop the script until the process is finished.</b>',
-    },
-    acpur: {
-      heading_main: 'User ranks and titles',
-      intro_para1: 'This screen lets you control users\' ranks and custom user titles. User ranks let you provide a graphic distinction between different types or roles of users using colors and other style rules. You can also associate a snippet of text with a rank; this will be displayed below the username of users that are in this rank.',
-      intro_para2: 'A user\'s rank is only a visual distinction; no permissions are associated with it. In other words, if someone is in the Administrators group but has the rank of %this.user_rank_member%, they will still have administrative abilities. On the flip side, giving someone the rank of %this.user_rank_admin% will not give them administrator privileges, but they will be labeled as an administrator.',
-      intro_para3: 'Ranks can be assigned based on a lot of different criteria. By default, Enano maps the Administrator and Moderator ranks based on user level. You can also assign a rank based on group memberships (e.g. being in a group automatically assigns a specific rank) and set individual ranks for users.',
-      intro_para4: 'This screen lets you create, edit, and delete ranks. To assign them, edit the respective user or usergroup.',
-      
-      msg_select_rank: 'To edit a rank, select it on the left. Or, click Create New Rank to start a new rank.',
-      msg_rank_delete_confirm_title: 'Delete this rank?',
-      msg_rank_delete_confirm_body: 'This will completely remove this rank and unassociate any users and groups with it.',
-      
-      th_edit_rank: 'Edit rank: %rank_title%',
-      th_create_rank: 'Create new rank',
-      
-      field_rank_title: 'Rank name:',
-      field_style_basic: 'Basic style options:',
-      field_style_basic_bold: 'Bold',
-      field_style_basic_italic: 'Italic',
-      field_style_basic_underline: 'Underline',
-      field_style_color: 'Username color:',
-      field_style_css: 'Additional CSS:',
-      field_preview: 'Preview:',
-      
-      btn_save: 'Save rank',
-      btn_create_submit: 'Create rank',
-      btn_create_init: 'Create new rank',
-      btn_delete: 'Delete rank',
-      
-      err_save_failed_title: 'Saving operation failed',
-      err_delete_failed_title: 'Deletion operation failed',
-      err_missing_rank_title: 'Please enter a title for this rank.',
-      err_cant_delete_system_rank: 'The rank you are trying to delete is an important system rank that cannot be deleted. However, you may customize the title and style of it to make it appear normal.',
-    },
-    acpsl: {
-      heading_main: 'System security log',
-      col_type: 'Type',
-      col_date: 'Date',
-      col_username: 'Username',
-      col_ip: 'IP Address',
-      entry_admin_auth_good: 'Successful elevated authentication<br /><small>Authentication level: %level%</small>',
-      entry_admin_auth_bad: 'Failed elevated authentication<br /><small>Attempted auth level: %level%</small>',
-      entry_activ_good: 'Successful account activation',
-      entry_auth_good: 'Successful regular user logon',
-      entry_activ_bad: 'Failed account activation',
-      entry_auth_bad: 'Failed regular user logon',
-      entry_sql_inject: 'SQL injection attempt<div style="max-width: 90%; clip: rect(0px,auto,auto,0px); overflow: auto; display: block; font-size: smaller;">Offending query: %query%</div>',
-      entry_db_backup: 'Database backup created<br /><small>Tables: %tables%</small>',
-      entry_install_enano: 'Installed Enano version %version%',
-      entry_upgrade_enano: 'Upgraded Enano to version %version%',
-      entry_illegal_page: 'Unauthorized viewing attempt<br /><small>Page: %illegal_link%</small>',
-      entry_upload_enable: 'Enabled file uploads',
-      entry_upload_disable: 'Disabled file uploads',
-      entry_magick_enable: 'Enabled ImageMagick for uploaded images',
-      entry_magick_disable: 'Disabled ImageMagick for uploaded images',
-      entry_filehist_enable: 'Enabled revision tracking for uploaded files',
-      entry_filehist_disable: 'Disabled revision tracking for uploaded files',
-      entry_magick_path: 'Changed path to ImageMagick executable',
-      entry_plugin_disable: 'Disabled plugin: %plugin%',
-      entry_plugin_enable: 'Enabled plugin:  %plugin%',
-      entry_plugin_install: 'Installed plugin:  %plugin%',
-      entry_plugin_uninstall: 'Uninstalled plugin:  %plugin%',
-      entry_plugin_upgrade: 'Upgraded plugin:  %plugin%',
-      entry_seclog_unauth: 'Unauthorized attempt to call security log fetcher',
-      entry_u_from_admin: 'User %username% demoted from Administrators group',
-      entry_u_from_mod: 'User %username% demoted from Moderators group',
-      entry_u_to_admin: 'User %username% added to Administrators group',
-      entry_u_to_mod: 'User %username% added to Moderators group',
-      entry_view_comment_ip: 'IP address viewed on comment by %username%',
-      tip_reverse_dns: 'Click for reverse DNS info',
-      
-      err_invalid_ip: 'No valid IPv4 or IPv6 address was provided. (In the demo, this is to be expected.)',
-      err_ptr_no_resolve: 'Reverse DNS was not available for this IP.'
-    },
-    acpbc: {
-      err_empty: 'Please enter something to ban.',
-      err_invalid_ip_range: 'The IP address range you entered is invalid.',
-      err_demo: 'This function is disabled in the demo. Just because <i>you</i> don\'t like %ban_target% doesn\'t mean <i>we</i> don\'t like %ban_target%.',
-      col_type: 'Type',
-      col_value: 'Value',
-      col_regex: 'Regular Expression',
-      msg_no_rules: 'No ban rules yet.',
-      ban_type_ip: 'IP address',
-      ban_type_username: 'Username',
-      ban_type_email: 'E-mail address',
-      btn_delete: 'Delete',
-      // Some languages like Chinese don't literally have the word "yes", so this would be something
-      // along the lines of "This is a regular expression" / "This is not..." in Chinese. Hence the
-      // separation of this from a generic "yes" string.
-      ban_regex_yes: 'Yes',
-      ban_regex_no: 'No',
-      heading_create_new: 'Create new ban rule',
-      field_type: 'Type:',
-      field_rule: 'Rule:',
-      field_rule_hint: 'You can ban multiple IP addresses, users, or e-mail addresses by separating entries with a single comma (User1,User2). Do not put a space after the comma. For IP addresses, you may specify ranges like 172|192.168.4-30|90-167.1-90, which will turn into 172 and 192 . 168 . 4-30 and 90-167 . 1 - 90, which matches 18,899 IP addresses.',
-      field_reason: 'Reason to show to the banned user:',
-      field_regex: 'This rule is a regular expression',
-      field_regex_hint: '(advanced users only)',
-      btn_create: 'Create new ban rule',
-    },
-    acplo: {
-      heading_main: 'You have now been logged out of the administration panel.',
-      msg_logout_complete: 'You will continue to be logged into the website, but you will need to re-authenticate before you can access the administration panel again.</p><p>Return to the <a href="%mainpage_link%">Main Page</a>.',
-    },
-    sbedit: {
-      header_msg: 'This control panel allows you to organize the sidebars, the collections of links and dynamic blocks that provide navigation for your site. Drag and drop blocks to move them between the left and right sidebars; changes will be saved automatically. Red blocks are disabled. This panel only works if you have Javascript enabled and working in your browser. <a href="%create_link%">Create a new block</a>.',
-      
-      msg_order_update_success: 'The sidebar order information was updated successfully.',
-      err_demo_php_disable: 'Adding PHP code blocks in the Enano administration demo has been disabled for security reasons.',
-      msg_item_added: 'The item was added.',
-      
-      create_intro: 'What type of block should this be?',
-      block_type_wiki: 'Textual content (wikitext)',
-      block_type_tpl: 'List of links',
-      block_type_html: 'Pure HTML',
-      block_type_php: 'PHP code',
-      block_type_plugin: 'Plugin block',
-      field_block_title: 'Block title:',
-      field_block_sidebar: 'Which sidebar:',
-      field_block_sidebar_left: 'Left',
-      field_block_sidebar_right: 'Right',
-      field_wikitext: '<p>
-                         <b>This block type is for textual content.</b> This could be information, a random fact, or some other blob of fully formatted text. Links show up inline,
-                         not as a list.
-                       </p>
-                       <p>
-                         Wikitext:
-                       </p>',
-      field_tplcode: '<p>
-                        <b>This block type is for links.</b> Use wikilinks (both internal and external are supported) to build a list of links. All links will be shown block-style,
-                        or one link per line. You can use logic in these blocks as well:
-                      </p>
-                      <pre>{if user_logged_in&#x7d;
-  [[Special:Preferences|User control panel]]
+	categories: [
+		'meta', 'adm', 'acl', 'adminusers',
+		'acphome', 'acpgc', 'acpup', 'acpft', 'acppl', 'acppm', 'acped', 'acpdb', 'acplm', 'acppg', 'acpum', 'acpug', 'acpcp', 'acpmm', 'acpsl',
+		'acpcm', 'acpbc', 'acplo', 'acptm', 'acpur', 'sbedit',
+	],
+	strings: {
+		meta: {
+			adm: 'Administration panel nav menu',
+			acl: 'Access control list editor',
+			acphome: 'ACP: Home',
+			acpgc: 'ACP: General configuration',
+			acpup: 'ACP: File uploads',
+			acpft: 'ACP: Allowed file types',
+			acppm: 'ACP: Manage pages',
+			acped: 'ACP: Edit page content',
+			acptm: 'ACP: Theme manager',
+			acppl: 'ACP: Manage plugins',
+			acpdb: 'ACP: Database backup',
+			acplm: 'ACP: Language manager',
+			acpcm: 'ACP: Cache settings',
+			acppg: 'ACP: Page groups',
+			acpum: 'ACP: User management',
+			acpur: 'ACP: User rank management',
+			acpug: 'ACP: User group management',
+			acpcp: 'ACP: COPPA support',
+			acpmm: 'ACP: Mass e-mail',
+			acpsl: 'ACP: Security log',
+			acpbc: 'ACP: Ban control',
+			acplo: 'ACP: Logout page',
+			sbedit: 'Sidebar editor',
+		},
+		adm: {
+			page_tagline: 'Administer your Enano website.',
+			
+			cat_general: 'General',
+			cat_content: 'Content',
+			cat_appearance: 'System and maintenance',
+			cat_users: 'Users',
+			cat_security: 'Security',
+			cat_plugins: 'Plugin configuration',
+			
+			page_general_config: 'General configuration',
+			page_file_uploads: 'File uploads',
+			page_file_types: 'Allowed file types',
+			
+			page_manager: 'Manage pages',
+			page_editor: 'Edit page content',
+			page_pg_groups: 'Manage page groups',
+			
+			page_themes: 'Manage themes',
+			page_plugins: 'Manage plugins',
+			page_db_backup: 'Backup database',
+			page_lang_manager: 'Language manager',
+			page_cache_manager: 'Cache settings',
+			
+			page_users: 'Manage users',
+			page_user_groups: 'Edit user groups',
+			page_coppa: 'COPPA support',
+			page_mass_email: 'Mass e-mail',
+			page_user_ranks: 'User ranks and titles',
+			
+			page_security_log: 'Security log',
+			page_ban_control: 'Ban control',
+			
+			btn_home: 'Administration panel home',
+			btn_logout: 'Log out of admin panel',
+			btn_keepalive_off: 'Turn on keep-alive',
+			btn_keepalive_on: 'Turn off keep-alive',
+			btn_keepalive_about: 'About keep-alive',
+			btn_keepalive_loading: 'Loading keep-alive button...',
+			
+			err_not_auth_title: 'Error: Not authenticated',
+			err_not_auth_body: 'It looks like your administration session is invalid or you are not authorized to access this administration page. Please <a href="%login_link%">re-authenticate</a> to continue.',
+		},
+		acl: {
+			err_access_denied: 'You are not authorized to view or edit access control lists.',
+			err_missing_template: 'It seems that (a) the file acledit.tpl is missing from this theme, and (b) the JSON response is working.',
+			err_user_not_found: 'The username you entered was not found.',
+			err_bad_group_id: 'The group ID you submitted is not valid.',
+			err_demo: 'Editing access control lists is disabled in the administration demo.',
+			err_zero_list: 'Supplied rule list has a length of zero',
+			err_pleaseselect_targettype: 'Please select a target type.',
+			err_pleaseselect_username: 'Please enter a username.',
+			err_select_preset: 'Please select a preset to load.',
+			err_preset_name_empty: 'Please enter a name for this preset.',
+			err_preset_is_blank: 'The preset you entered seems completely empty (i.e. all permissions set to "inherit")',
+			
+			radio_usergroup: 'A usergroup',
+			radio_user: 'A specific user',
+			radio_scope_thispage: 'Only this page',
+			radio_scope_wholesite: 'The entire website',
+			radio_scope_pagegroup: 'A group of pages',
+			
+			lbl_scope: 'What should this access rule control?',
+			lbl_welcome_title: 'Manage page access',
+			lbl_welcome_body: 'Please select who should be affected by this access rule.',
+			lbl_trace_title: 'View effective permissions',
+			lbl_trace_body: 'See what permissions are effective and where. <a href="http://docs.enanocms.org/Help:4.2" onclick="window.open(this.href); return false;">Learn about precedence and scope</a>',
+			lbl_editwin_title_create: 'Create access rule',
+			lbl_editwin_title_edit: 'Editing permissions',
+			lbl_editwin_body: 'This panel allows you to edit what the %target_type% "<b>%target%</b>" can do on <b>%scope_type%</b>. Unless you set a permission to "Deny", these permissions may be overridden by other rules.',
+			lbl_deleterule: 'Delete this rule',
+			lbl_save_success_title: 'Permissions updated',
+			lbl_save_success_body: 'The permissions for %target_name% on this page have been updated successfully. If you changed permissions that affect your user account, you may not see changes until you reload the page.',
+			lbl_delete_success: 'Rule deleted. ',
+			lbl_preset_load_title: 'Load a preset',
+			lbl_preset_load: 'Select a preset...',
+			lbl_preset_save_title: 'Enter a name for this preset',
+			lbl_field_inherit: 'Inherit',
+			lbl_field_deny: 'Deny',
+			lbl_field_disallow: 'Disallow',
+			lbl_field_wikimode: 'Wiki mode',
+			lbl_field_allow: 'Allow',
+			lbl_help: '<p>
+ 									<b>Permission types:</b>
+ 								</p>
+ 								<ul>
+ 									<li><b>Allow</b> means that the user is allowed to access the item</li>
+ 									<li><b>Wiki mode</b> means the user can access the item if wiki mode is active (per-page wiki mode is taken into account)</li>
+ 									<li><b>Disallow</b> means the user is denied access unless something allows it.</li>
+ 									<li><b>Deny</b> means that the user is denied access to the item. This setting overrides all other permissions.</li>
+ 									<li><b>Inherit</b> forces the permission to be unset and thus inherited from the defaults. Setting every permission to Inherit is the same as deleting the rule.</li>
+ 								</ul>',
+			lbl_trace_user: 'See permissions for:',
+			lbl_trace_page: 'On page:',
+			
+			scope_type_wholesite: 'this entire site',
+			scope_type_thispage: 'this page',
+			scope_type_pagegroup: 'this group of pages',
+			
+			target_type_user: 'user',
+			target_type_group: 'group',
+			
+			msg_guest_howto: 'To edit permissions for guests, select "a specific user", and enter Anonymous as the username.',
+			msg_deleterule_confirm: 'Do you really want to delete this rule?',
+			msg_closeacl_confirm_title: 'Close the ACL manager?',
+			msg_closeacl_confirm_body: 'This will cancel any changes that you haven\'t saved.',
+			msg_deny_everyone_confirm: 'CAUTION: You are setting a Deny ruling for everyone on this site. This will block the selected actions from being performed at all. Do you really want to do this?\n\nPlease also note that the following core pages cannot be blocked: Special:Login, Special:Logout, and Special:LangExportJSON.',
+			
+			msg_scale_intro_title: 'Existing ACL rules',
+			msg_scale_intro_body: 'This is a list of all the existing ACL rules on your site. You can click an entry to edit it. Selecting the same criteria in the user and group selection page will also allow editing a rule.',
+			
+			msg_scale_allow: 'Allow',
+			msg_scale_mostly_allow: 'Mostly allow',
+			msg_scale_some_allow: 'Some allow',
+			msg_scale_mixed: 'Mixed',
+			msg_scale_some_deny: 'Some deny',
+			msg_scale_mostly_deny: 'Mostly deny',
+			msg_scale_deny: 'Deny',
+			
+			msg_list_user: '(User: %username%)',
+			msg_list_group: '(Group: %group%)',
+			msg_list_on_page: ' on page: %page_name%',
+			msg_list_on_page_group: ' for page group: %page_group%',
+			msg_list_entire_site: ' for the entire site',
+			msg_list_score: 'Score: %score% (%desc%) %info%',
+			msg_no_presets: 'No presets are defined. Define a preset by setting all the ACL settings to what you want, and then hitting Save. <a %close_flags%>Close</a>',
+			msg_debug_main_title: 'View effective permissions',
+			msg_debug_main_body: 'This tool allows you to see what actual permissions are in use. It can be helpful if you are struggling to determine why a certain action is being allowed or denied. There are two views available for this window: you can either view the information sorted by individual actions, or group actions by which rule sets them.',
+			msg_trace_key: 'Color guide',
+			msg_failed_deps: 'Failed dependencies: ',
+			
+			btn_success_dismiss: 'dismiss',
+			btn_success_close: 'close manager',
+			btn_deleterule: 'Delete rule',
+			btn_createrule: 'Create rule',
+			btn_returnto_editor: 'Return to ACL editor',
+			btn_returnto_userscope: 'Return to user/scope selection',
+			btn_show_existing: '&raquo; View existing rules',
+			btn_view_effective: '&raquo; Show diagnostic tools',
+			btn_close: 'Close ACL wizard',
+			btn_edit_presets: 'Presets: <a %load_flags%>Load</a> | <a %save_flags%>Save</a>',
+			btn_load_preset: 'Load preset',
+			btn_save_preset: 'Save preset',
+			btn_edit_rule: 'edit',
+			btn_view_key: 'View color key',
+			btn_sort_perm: 'Sort individually',
+			btn_sort_rule: 'Sort by rule',
+			
+			inherit_enano_default: 'Enano defaults',
+			inherit_global_everyone: 'Rule for everyone on the entire site',
+			inherit_global_group: 'Rule for the group "%group_name%" on the entire site',
+			inherit_global_user: 'Rule for this user on the entire site',
+			inherit_pg_everyone: 'Rule for everyone in the page group "%pg_name"',
+			inherit_pg_group: 'Rule for the group "%group_name%" in the page group "%pg_name%"',
+			inherit_pg_user: 'Rule for this user in the page group "%pg_name%"',
+			inherit_local_everyone: 'Rule for everyone on this page',
+			inherit_local_group: 'Rule for the group "%group_name%" on this page',
+			inherit_local_user: 'Rule for this user on this page',
+			
+			inherit_key_enano_default: '%this.acl_inherit_enano_default% (most broad)',
+			inherit_key_global_everyone: '%this.acl_inherit_global_everyone%',
+			inherit_key_global_group: 'Rule for a user group on the entire site',
+			inherit_key_global_user: '%this.acl_inherit_global_user%',
+			inherit_key_pg_everyone: 'Rule for everyone in a specific page group',
+			inherit_key_pg_group: 'Rule for a usergroup on a specific page group',
+			inherit_key_pg_user: 'Rule for this user in a specific page group',
+			inherit_key_local_everyone: '%this.acl_inherit_local_everyone%',
+			inherit_key_local_group: 'Rule for a specific usergroup on this page',
+			inherit_key_local_user: '%this.acl_inherit_local_user% (most specific)',
+		},
+		acphome: {
+			heading_main: 'Welcome to the Administration Dashboard.',
+			welcome_line1: 'Thanks for choosing Enano as your site\'s CMS! The administration panel gives you control over nearly every aspect of your site. The links to the left allow you to navigate around the panel. Below you will find some statistics on your site and any alerts you might need to attend to.',
+			
+			// Stats!
+			stat_header: 'Site statistics',
+			stat_enano_version: 'Enano CMS %version% (%releasename%)<br /><a href="%aboutlink%">View server information and license</a>',
+			stat_numpages: 'Number of pages:',
+			stat_edits: 'Number of edits made:',
+			stat_edits_data: '%edit_count% (average of %per_day% per day)',
+			stat_comments: 'Number of comments:',
+			stat_comments_data: '%comment_count% (average of %per_day% per day)',
+			stat_users: 'Number of users:',
+			stat_filesize: 'Total size of uploaded files:',
+			stat_cachesize: 'Total size of cache:',
+			stat_avatarsize: 'Space occupied by avatars:',
+			stat_dbsize: 'Database size:',
+			stat_dbsize_unsupported: 'Unsupported',
+			stat_installdate: 'Site started:',
+			stat_installdate_ago: '(%days% %this.etc_unit_days% ago)',
+			stat_lastupdate: 'Last Enano upgrade:',
+			stat_lastupdate_never: 'Never',
+			
+			heading_alerts: 'Active alerts',
+			
+			msg_demo_title: 'Enano is running in demo mode.',
+			msg_demo_body: 'If you borked something up, or if you\'re done testing, you can <a href="%reset_url%">reset this site</a>. The site is reset automatically once every two hours. When a reset is performed, all custom modifications to the site are lost and replaced with default values.',
+			
+			msg_install_files_title: 'Installer files found',
+			msg_install_files_body: 'Please delete the install/ directory from your Enano installation folder &ndash; it contains sensitive tools that might allow your site to be compromised.',
+			
+			heading_updates: 'Check for updates',
+			msg_updates_info: 'The Enano team will on occasion release new versions of Enano. We always recommend that you run the latest available version because many releases contain security patches. Enano checks for updates by looking at an <a href="%updates_url%">XML file</a> and doesn\'t share any information about your site.',
+			btn_check_updates: 'Check for updates',
+			
+			heading_inactive_users: 'Users are awaiting activation',
+			msg_inactive_users_one: '1 user has requested manual account activation. You can activate the account by going to the <a %um_flags%>User Manager</a>.',
+			msg_inactive_users_plural: '%num_users% users have requested manual account activation. You can activate those accounts by going to the <a %um_flags%>User Manager</a>.',
+			
+			msg_users_locked_out: 'Active IP address lockouts',
+			msg_users_locked_out_hint: 'The following IP addresses have been automatically locked out from login attempts. You can delete these active lockouts, if you choose.',
+			th_locked_out_ip: 'IP address',
+			th_locked_out_username: 'Username (most recent attempt)',
+			th_locked_out_status: 'Status',
+			th_locked_out_time: 'Time remaining',
+			lbl_locked_out_warned: 'Warned (failures: %fail_count%)',
+			lbl_locked_out_banned: 'Locked out',
+			btn_lockout_unblock: 'Unblock',
+			btn_lockout_clear: 'Clear',
+			msg_lockout_clear_success: 'The IP address %ip% has been cleared from the active lockout list.',
+			
+			heading_docs: 'Enano documentation',
+			msg_docs_info: 'The <a href="http://docs.enanocms.org/" onclick="window.open(this.href); return false;">Enano administrator\'s handbook</a> is maintained as a wiki. It will help you get started with Enano and learn about how we do things differently.',
+			heading_support: 'Get support',
+			msg_support_info: 'Stuck? Think you found a bug? Tell us about it! Post a message in the <a href="http://forum.enanocms.org/" onclick="window.open(this.href); return false;">Enano support forums</a> to obtain community support for Enano. You may also be interested in our <a href="http://enanocms.org/Support" onclick="window.open(this.href); return false;">other support channels</a>.',
+			
+			heading_top_pages: 'Most requested pages',
+			th_toppages_page: 'Page',
+			th_toppages_hits: 'Hits',
+			heading_seclog: 'Security log',
+			msg_seclog_info: 'This list shows the 5 most recent actions/attempted actions performed by administrators on this site. This also includes attempts to view blocked pages and use the administration panel without appropriate privileges. You can view a complete list using the link below.',
+			btn_seclog_full: 'Full security log',
+		},
+		acpgc: {
+			err_avatar_dir_invalid: 'You have entered an invalid avatar directory.',
+			err_avatar_dir_not_exist: 'The avatar directory you entered does not exist in the filesystem.',
+			msg_save_success: 'Your changes to the site configuration have been saved.',
+			
+			// Section: global site options
+			heading_main: 'Global site options',
+			heading_submain: 'These options control the entire site.',
+			field_site_name: 'Site name:',
+			field_site_desc: 'Site description:',
+			field_main_page: 'Main page:',
+			field_main_page_option_same: 'Use the same main page for everyone',
+			field_main_page_option_members: 'Use a different main page for members:',
+			field_main_page_members: 'Main page for members:',
+			field_copyright: 'Copyright notice shown on pages:',
+			field_copyright_hint: 'Hint: To make a copyright symbol (&copy;), type <tt>&amp;copy;</tt>.',
+			field_contactemail: 'Contact e-mail',
+			field_contactemail_hint: 'All e-mail sent from this site will appear to have come from the address shown here.',
+			
+			// Section: wiki mode
+			heading_wikimode: 'Wiki mode',
+			field_wikimode_intro: 'Enano can also act as a wiki, meaning anyone can edit and create pages. To enable Wiki Mode, check the box to the right.',
+			field_wikimode_info_sanitize: 'In Wiki Mode, certain HTML tags such as &lt;script&gt; and &lt;object&gt; are disabled, and all PHP code is disabled, except if the person editing the page is an administrator.',
+			field_wikimode_info_history: 'Also, Enano keeps complete page history, which makes restoring vandalized pages easy. You can also protect pages so that they cannot be edited.',
+			field_wikimode: 'Enable Wiki Mode',
+			field_editnotice_title: 'Edit page notice',
+			field_editnotice_info: 'When Wiki Mode is enabled, anyone can edit pages. Check the box below and enter a message to display it whenever the page editor is opened. Administrators often use this field to display a legal disclaimer or a notice of what license the user agrees to submit their content under.',
+			field_editnotice: 'Show a message whenever pages are edited',
+			field_edit_require_captcha_title: 'Require visual confirmation for guests editing pages',
+			field_edit_require_captcha_hint: 'If this is enabled, guests will be asked to enter a visual confirmation code before saving changes to a page.',
+			field_edit_require_captcha: 'Require guests to complete a CAPTCHA when editing pages',
+			
+			// Section: statistics and hit counting
+			heading_stats: 'Statistics and hit counting',
+			stats_intro: 'Enano has the ability to show statistics for every page on the site. This allows you to keep very close track of who is visiting your site, and from where.',
+			stats_hint_privacy: 'Unfortunately, some users don\'t like being logged. For this reason, you should state clearly what is logged (usually the username or IP address, current time, page name, and referer URL) in your privacy policy. If your site is primarily geared towards children, and you are a United States citizen, you are required to have a privacy policy stating exactly what is being logged under the terms of the Childrens\' Online Privacy Protection Act.',
+			field_stats_enable: 'Log all page hits',
+			field_stats_hint: 'This excludes special and administration pages.',
+			
+			// Section: comment system
+			heading_comments: 'Comment system',
+			field_enable_comments: 'Enable the comment system',
+			field_approve_comments: 'Require approval before article comments can be shown',
+			field_comment_allow_guests: 'Allow guests to post comments',
+			field_comment_allow_guests_yes: 'Yes',
+			field_comment_allow_guests_captcha: 'Require visual confirmation',
+			field_comment_allow_guests_no: 'No (require login)',
+			field_comment_spam_policy: 'Spam policy:',
+			field_comment_spam_policy_hint: 'This requres a <a href="http://enanocms.org/Category:Spam_filtering">spam filtering plugin</a> to be installed.',
+			field_comment_spam_policy_moderate: 'Moderate comments (default)',
+			field_comment_spam_policy_reject: 'Reject post',
+			field_comment_spam_policy_accept: 'Ignore and accept posts',
+			
+			// Section: disable site
+			heading_disablesite: 'Disable all site access',
+			field_disablesite_hint: 'Disabling the site allows you to work on the site without letting non-administrators see or use it.',
+			field_disablesite: 'Disable this site',
+			field_disablesite_message: 'Message to show to users:',
+			
+			// Section: default theme
+			heading_default_theme: 'Visual defaults',
+			field_default_theme: 'Default theme for guests and new users:',
+			
+			// Section: breadcrumbs
+			field_breadcrumb_mode: 'Enable breadcrumbs:',
+			field_breadcrumb_mode_subpages: 'Only with a slash in the URL (default)',
+			field_breadcrumb_mode_always: 'Always',
+			field_breadcrumb_mode_never: 'Never',
+			
+			// Section: CDN
+			field_cdn_path: 'URL to <acronym title="Content Delivery Network">CDN</acronym> server:',
+			field_cdn_path_hint: 'A CDN, or Content Delivery Network, allows downloading of shared Enano components from a server designed to serve out only images, CSS, and script files. Since a browser can open separate connections for the page and for images and scripts, the page loads faster. Leave this blank to just use Enano\'s local files (default).',
+			field_cdn_path_example: 'Example: <tt>http://cdn.mycompany.com/enano/',
+			
+			// Section: gzip
+			field_gzip: 'Enable gzip compression',
+			field_gzip_hint: 'Compress pages using before sending them to the user. At the cost of a small amount of CPU, this can save a lot of bandwidth. This only enables compression for HTML and some Javascript; to gzip compress CSS and images, please consult your webserver\'s documentation.',
+			field_gzip_lbl: 'Use gzip compression',
+			field_gzip_btn_check: 'Check server for support',
+			field_gzip_check_msg_server_does_it: 'Your server performs gzip compression on its own. There is no need to enable it in Enano, and if you do, you may cause Enano to stop working.',
+			field_gzip_check_msg_server_good: 'Your server is not automatically compressing PHP-generated pages, so Enano\'s compression facilities are needed.',
+			field_gzip_check_msg_php_bad: 'PHP does not have gzip support enabled.',
+			field_gzip_check_msg_php_good: 'Found gzip compression support in PHP.',
+			field_gzip_check_msg_success: 'Success - all server checks passed. You can enable gzip support.',
+			field_gzip_check_msg_failure: 'Server check failed. Unless you think Enano is wrong, don\'t enable gzip below.',
+			
+			// Main section: users and communication
+			heading_users: 'Users and communication',
+			
+			// Section: account activation
+			heading_activate: 'User account activation',
+			activate_intro_line1: 'If you would like to require users to confirm their e-mail addresses by way of account activation, you can enable this behavior here. If this option is set to "None", users will be able to register and use this site without confirming their e-mail addresses. If this option is set to "User", users will automatically be sent e-mails upon registration with a link to activate their accounts. And lastly, if this option is set to "Admin", users\' accounts will not be active until an administrator activates the account.',
+			activate_intro_line2: 'You may also disable registration completely if needed.',
+			activate_intro_sfnet_warning: 'Note: if you are using a SourceForge.net server, do not select User. SourceForge.net does not allow e-mails to be sent from project web sites.',
+			field_activate: 'Account activation:',
+			field_activate_disable: 'Disable registration',
+			field_activate_none: 'None',
+			field_activate_user: 'User',
+			field_activate_admin: 'Admin',
+			
+			// Section: terms of use (TOU)
+			heading_tou: 'Registration agreement/Terms of Use',
+			field_tou: 'Registration agreement',
+			field_tou_hint: 'The text you enter here will be displayed to users upon any attempt to create an account on this site.',
+			
+			// Section: account lockouts
+			heading_lockout: 'Account lockouts',
+			lockout_intro: 'Configure Enano to prevent or restrict logins for a specified period of time if a user enters an incorrect password a specific number of times.',
+			field_lockout_threshold: 'Lockout threshold:',
+			field_lockout_threshold_hint: 'How many times can a user enter wrong credentials before a lockout goes into effect?',
+			field_lockout_duration: 'Lockout duration:',
+			field_lockout_duration_hint: 'This is how long an account lockout should last, in minutes.',
+			field_lockout_policy: 'Lockout policy:',
+			field_lockout_policy_hint: 'What should be done when a lockout goes into effect?',
+			field_lockout_policy_nothing: 'Don\'t do anything',
+			field_lockout_policy_captcha: 'Require visual confirmation',
+			field_lockout_policy_lockout: 'Prevent all login attempts',
+			
+			// Section: password strength
+			heading_passstrength: 'Password strength',
+			field_passstrength_title: 'Enable password strength analysis',
+			field_passstrength_hint: 'This should be enabled in most cases. When this is enabled, a strength meter and a numerical score will be displayed wherever a password can be changed.',
+			field_passstrength: 'Enabled',
+			field_passminimum_title: 'Minimum strength score',
+			field_passminimum_hint: 'This is the lowest score a password will be allowed to have. -10 will allow any password. A score of under -3 is considered weak, under 1 is fair, under 4 is good, under 10 is strong, and 10 and above are very strong. The scale is open-ended. This only has an effect if the meter is enabled above.',
+			
+			// Section: e-mail
+			heading_email: 'E-mail sent from the site',
+			field_email_method: 'E-mail sending method:',
+			field_email_method_hint: 'Try using the built-in e-mail method first. If that doesn\'t work, you will need to enter valid SMTP information here.',
+			field_email_method_builtin: 'PHP\'s built-in mail() function',
+			field_email_method_smtp: 'Use an external SMTP server',
+			field_email_smtp_hostname: 'SMTP hostname:',
+			field_email_smtp_hostname_hint: 'This option only applies to the external SMTP mode.',
+			field_email_smtp_auth: 'SMTP credentials:',
+			field_email_smtp_username: 'Username:',
+			field_email_smtp_password: 'Password:',
+			
+			// Section: Sessions
+			heading_sessions: 'User sessions',
+			hint_sessions_noelev: '<b>Remember:</b> Settings here only affect normal logins - you can\'t change the length of sessions that give you elevated privileges, such as the re-authentication that occurs when you access the administration panel. <a href="http://docs.enanocms.org/Help:Appendix_B" onclick="window.open(this.href); return false;">Read about Enano\'s security model</a>.',
+			field_short_time: 'Length of short sessions in minutes:',
+			field_short_time_hint: 'This is how long a user\'s session will last when they don\'t check the "remember me" checkbox. Short sessions are automatically renewed every time a page is loaded.',
+			field_long_time: 'Length of extended sessions in days:',
+			field_long_time_hint: 'This is how long a user\'s session will last when the "remember me" checkbox is selected during their login. Long sessions can\'t be renewed - they always last a fixed amount of time. Set this to 0 to make extended sessions infinite, e.g. they are only terminated when the user logs out manually (this may present a security risk).',
+			
+			// Section: avatars
+			heading_avatars: 'Avatars',
+			avatars_intro: 'Avatars are small images that users can display on their profiles and in comments.',
+			field_avatar_enable: 'Enable avatar support:',
+			field_avatar_enable_hint: 'Supported formats are JPEG, PNG, and GIF&trade;.',
+			field_avatar_enable_label: 'Enabled',
+			field_avatar_max_filesize: 'Maximum avatar file size:',
+			field_avatar_max_filesize_hint: 'For smaller sites, the highest value for this should be about 50KB, 51200. Larger sites with more visitors will likely want to use something much smaller, such as 10KB.',
+			field_avatar_max_dimensions: 'Maximum avatar dimensions:',
+			field_avatar_max_dimensions_hint: 'The format is width &#215; height. Typically you want to have this square (the same width and height). These are only maximum dimensions; users are not prevented from having smaller images.',
+			field_avatar_allow_anim_title: 'Allow animated avatars:',
+			field_avatar_allow_anim_hint: 'If this is checked, users can upload APNG and Animated GIF&trade; avatars. Sometimes such images can be specifically made to be distracting, like rapidly flashing images. If this is unchecked, these formats will be blocked, and only still PNGs and GIFs will be allowed.',
+			field_avatar_allow_anim: 'Don\'t block animated images',
+			field_avatar_upload_methods: 'Allowed upload methods:',
+			field_avatar_upload_file: 'Allow users to upload image files from their computers',
+			field_avatar_upload_http: 'Allow users to enter a URL to their desired avatar',
+			field_avatar_upload_gravatar: 'Allow users to use their <a href="http://www.gravatar.com/" onclick="window.open(this.href); return false;">Gravatar</a>',
+			field_avatar_gravatar_rating: 'Highest allowed rating for Gravatar images:',
+			field_avatar_gravatar_rating_g: 'G (no objectionable content)',
+			field_avatar_gravatar_rating_pg: 'PG (rude gestures, immodest dress, mild swearing/violence)',
+			field_avatar_gravatar_rating_r: 'R (profanity, violence, nudity, hard drug use)',
+			field_avatar_gravatar_rating_x: 'X (hardcore sexual images, extremely disturbing violence)',
+			field_avatar_directory: 'Avatar storage directory:',
+			field_avatar_directory_hint: 'This should be relative to your Enano root and should contain only alphanumeric characters and forward slashes, even if your server runs Windows.',
+			
+			// Section: misc options
+			heading_usermisc: 'Other user options',
+			field_userpage_acl_title: 'New users can edit their user pages:',
+			field_userpage_acl_hint: 'This setting will cause Enano to grant certain rights to newly registered users, effective only on their user page. These rights include creating their page, editing their page, and posting comments. Since it generates a new ACL rule, you are able to override new permissions. This setting will only take effect on new users - it does not affect the permissions of those already registered.',
+			field_userpage_acl: 'Grant editing rights to new users on their user pages',
+			
+			// Main section: sidebar links
+			heading_sidebar: 'Sidebar links',
+			
+			// Section: promote Enano
+			heading_promoteenano: 'Promote Enano',
+			field_enano_link_title: 'Show link to enanocms.org on the sidebar',
+			field_enano_link_hint: 'You may help show your support for Enano with this link. This is entirely optional and the Enano project does not discriminate against sites that disable this button. (This differs from our policy on the "Powered by Enano" link; see <a href="http://enanocms.org/powered-link" onclick="window.open(this.href); return false;">this page</a> for more information.)',
+			field_enano_link: 'Place a link to enanocms.org on the sidebar',
+			
+			// Section: SF.net logo
+			heading_sfnet_logo: 'SourceForge.net logo',
+			sfnet_intro: 'All projects hosted by SourceForge.net are required to display an official SourceForge.net logo on their pages.  If you want to display a SourceForge.net logo on the sidebar, check the box below, enter your group ID, and select an image type.',
+			field_sfnet_display: 'Display the SourceForge.net logo on the right sidebar',
+			field_sfnet_group_id: 'Group ID:',
+			field_sfnet_logo_style: 'Logo style:',
+			field_sfnet_logo_style_1: '88x31%this.etc_unit_pixels_short%, white',
+			field_sfnet_logo_style_2: '125x37%this.etc_unit_pixels_short%, white',
+			field_sfnet_logo_style_3: '125x37%this.etc_unit_pixels_short%, black',
+			field_sfnet_logo_style_4: '125x37%this.etc_unit_pixels_short%, blue',
+			field_sfnet_logo_style_5: '210x62%this.etc_unit_pixels_short%, white',
+			field_sfnet_logo_style_6: '210x62%this.etc_unit_pixels_short%, black',
+			field_sfnet_logo_style_7: '210x62%this.etc_unit_pixels_short%, blue',
+			
+			// Section: W3C validation buttons
+			heading_w3clogos: 'W3C compliance logos',
+			w3clogos_intro: 'Enano generates (by default) Valid XHTML 1.1 code, plus valid CSS.  If you want to show this off, check the appropriate boxes below.',
+			w3clogos_btn_html32: 'HTML 3.2',
+			w3clogos_btn_html40: 'HTML 4.0',
+			w3clogos_btn_html401: 'HTML 4.01',
+			w3clogos_btn_xhtml10: 'XHTML 1.0',
+			w3clogos_btn_xhtml11: 'XHTML 1.1',
+			w3clogos_btn_css: 'CSS',
+			
+			// Section Defective By Design link
+			heading_dbd: 'Defective By Design Anti-DRM button',
+			dbd_intro: 'The Enano project is strongly against Digital Restrictions Management.',
+			dbd_explain: 'DRM infringes on consumer rights by using technical locks to prevent you from using your Fair Use rights granted by copyright law. This means that consumers are harmed in many ways, for example when they can\'t copy purchased digital media to their own devices. Furthermore, since most DRM schemes are proprietary and designed to prevent interoperability, you can be locked you into a specific brand or product. You can help spread consumer awareness and show your opposition to DRM through this button. It\'s as easy as checking the box below to place a link to <a href="http://www.defectivebydesign.org">DefectiveByDesign.org</a> on your sidebar.',
+			field_stopdrm: 'Help stop DRM by placing a link to DBD on the sidebar!',
+			
+			// Save button
+			btn_save_changes: 'Save changes'
+		},
+		acpup: {
+			heading_main: 'File upload configuration',
+			intro: 'Enano supports the ability to upload files to your website and store the files in the database. This enables you to embed images and such into pages without manually writing the HTML. However, the upload feature can sometimes pose a risk to your site, as viruses and executable files can sometimes be uploaded.',
+			field_enable: 'Enable file uploads',
+			field_max_size: 'Maximum file size:',
+			info_magick: 'You can allow Enano to generate thumbnails of images automatically. This feature requires ImageMagick to work properly. If your server does not have ImageMagick on it, Enano will try to use the GD library (if available) to scale images. This can be slower, but it works on a wider range of servers. If even that does not work, Enano will simply make your users\' browsers scale the images. In most cases this is fine, but if you are uploading large (>100KB) images and embedding them inside of pages, you should try to enable ImageMagick or configure GD because transferring these large images many times can cost you quite a lot of bandwidth.',
+			field_magick_enable: 'Use ImageMagick to scale images',
+			field_magick_path: 'Path to ImageMagick:',
+			err_magick_not_found: '<b>Warning:</b> the file "%magick_path%" was not found, and the ImageMagick file path was not updated.',
+			// Translators: for the path here, please be sure to use a double-backslash in the Windows path. Avoid translating the file paths
+			// anyway since they're generally the same even on non-English Windows systems.
+			field_magick_path_hint: 'On Linux and Unix servers, the most likely options here are /usr/bin/convert and /usr/local/bin/convert. If you server runs Windows, then ImageMagick is most likely to be C:\\Windows\\Convert.exe or C:\\Windows\\System32\\Convert.exe.',
+			info_cache: 'If you use ImageMagick to scale images, your server will be very busy constantly scaling images if your website is busy, and your site may experience slowdowns. You can dramatically speed up this scaling process if you use a directory to cache thumbnail images.',
+			info_cache_chmod: '<b>Please note:</b> the cache/ directory on your server <u>must</u> be writable by the server. While this is not usually a problem on Windows servers, most Linux/Unix servers will require you to CHMOD the cache/ directory to 777. See your FTP client\'s user guide for more information on how to do this.',
+			msg_cache_not_writable: ' <b>At present, it seems that the cache directory is not writable. The checkbox below has been disabled to maintain the stability of Enano.</b>',
+			field_cache: 'Cache thumbnailed images',
+			info_history: 'Lastly, you can choose whether file history will be saved. If this option is turned on, you will be able to roll back any malicious changes made to uploaded files, but this requires a significant amount of filesystem storage. You should probably leave this option enabled unless you have less than 250MB of disk space on your hosting account or server.',
+			field_history: 'Keep a history of uploaded files',
+			btn_save: 'Save changes',
+		},
+		acpft: {
+			// Nope. There isn't anything else. Sorry to disappoint.
+			heading_main: 'Allowed file types',
+			hint: 'Using the form below, you can decide which file types are allowed to be uploaded to this site.',
+			msg_saved: 'Your changes have been saved.',
+			msg_demo_mode: 'Hmm, enabling executables, are we? Tsk tsk. I\'d love to know what\'s in that EXE file you want to upload. OK, maybe you didn\'t enable EXEs. But nevertheless, changing allowed filetypes is disabled in the demo.',
+		},
+		acppl: {
+			heading_main: 'Plugin management',
+			intro: 'This interface allows you to control what plugins are in use on this site. Everything you do on this page will be shown in the security logs. Some plugins don\'t fully support the newer features like installation, upgrading and uninstalls. Installing these plugins will still work, but sometimes authors include setup instructions in the plugin file. You should check the file for further setup instructions once the plugin is installed.',
+			
+			lbl_plugin_name: '<b>%plugin%</b> by %author%',
+			lbl_status_installed: 'Installed',
+			lbl_status_uninstalled: 'Not installed',
+			lbl_status_system: 'System plugin',
+			lbl_status_need_upgrade: 'Disabled (needs upgrade)',
+			lbl_status_disabled: 'Disabled',
+			lbl_filename: 'Filename:',
+			lbl_plugin_site: 'Plugin homepage:',
+			lbl_author_site: 'Author homepage:',
+			lbl_version: 'Version:',
+			lbl_installed_version: 'Installed version:',
+			
+			btn_install: 'Install',
+			btn_disable: 'Disable',
+			btn_enable: 'Enable',
+			btn_upgrade: 'Upgrade',
+			btn_uninstall: 'Uninstall',
+			btn_reimport: 'Re-import strings',
+			
+			msg_confirm_uninstall: 'Uninstalling this plugin may cause the loss of data that was created with it. You should only uninstall a plugin if you are certain you\'ll have no further use for it; in fact, you don\'t even need to uninstall a plugin if you\'re deleting it from the filesystem.',
+			msg_confirm_install: 'Plugins are not supported by the Enano project and could harm your site if malicious. You should only install plugins from sources that you trust.',
+			msg_confirm_reimport: 'Re-importing strings will reload all language data from the plugin file. This should fix missing messages, but any customizations you have made to the plugin\'s language strings will be lost.',
+			
+			msg_confirm_authext_title: 'This plugin is an authentication extension.',
+			msg_confirm_authext_body: 'This plugin hooks into Enano\'s login system. It might be used to allow non-password-based authentication. If there is a security vulnerability in this plugin, it might open your site up to attack. Only continue if you trust the author of this plugin.\n\nBe sure to be vigilant in checking for updates to this plugin.',
+			
+			err_upgrade_not_supported: 'This plugin doesn\'t support automatic upgrades. The version number has been updated so the plugin will be re-enabled, but you should check the plugin file to see if the author provided instructions for finishing the upgrade.',
+			err_upgrade_bad_version: 'This plugin cannot be upgraded because you are running a version of the plugin that is not listed in the plugin\'s version list.',
+			err_upgrade_bad_target_version: 'This plugin cannot be upgraded because it does not support its own version. Please contact the author and ask them to fix this.',
+			err_upgrade_to_older: 'You are trying to upgrade to an older release of this plugin. This is unsupported and must be done manually.',
+			err_upgrade_bad_query: 'There is a problem with one of the SQL queries the plugin is trying to make.',
+			err_dmbs_not_supported: 'This plugin doesn\'t support %dbdriver% databases.',
+			err_import_no_strings: 'This plugin doesn\'t have language support.',
+			err_demo_mode: 'You can\'t manipulate plugins in the Enano demo for security reasons.',
+			
+			msg_old_entries_title: 'Import old plugin installation data',
+			msg_old_entries_body: 'There is still data from the old plugin structure in your database. You can import this to the new structure automatically using the button below.',
+			btn_import_old: 'Import old plugin settings',
+		},
+		acppm: {
+			heading_main: 'Edit page properties',
+			hint: 'This panel allows you to edit various properties of pages that aren\'t visible anywhere else. In addition to renaming pages, you can also change their <a href="http://docs.enanocms.org/Help:2.3" onclick="window.open(this.href); return false;">URL string</a> and options such as whether to index the page for searching or bypass Enano\'s template engine.',
+			err_page_not_found: 'No pages matching that search string could be found.',
+			msg_results_ambiguous_title: 'Ambiguous search results',
+			msg_results_ambiguous_body: 'Multiple pages that matched your search terms were found. Please select the page you wish to modify:',
+			ambig_btn_viewpage: 'View',
+			err_ambig_absolute: 'Your database is corrupt as it contains multiple pages with the same urlname and namespace.',
+			lbl_field_search: 'Search for a page title or URL string:',
+			heading_select_page_from_list: 'Select page from a list',
+			hint_select_page_from_list: 'You can also select the page you want to modify from the list below. The list is broken into sections of 100 pages, so if you have a lot of pages on your site, you can click the pagination control below to view more pages.',
+			
+			// Edit form
+			heading_editing: 'Editing page:',
+			lbl_page_name: 'Page\'s title:',
+			lbl_page_urlname: 'URL string:',
+			lbl_page_urlname_hint: 'No spaces, and don\'t enter the namespace prefix (e.g. User:). Changing this value is usually not a good idea, especially for templates and project pages, because it will invalidate the page\'s current URL.',
+			lbl_namespace: 'Namespace (URL prefix):',
+			ns_article: '[No prefix, default Article namespace]',
+			heading_advanced: 'Advanced options',
+			msg_file_ns_warning: '<b>WARNING:</b> Changing the namespace to something other than "File" will delete the uploaded file and all previous revisions of it.',
+			lbl_enable_comments_title: 'Allow comments to be posted on this page?',
+			lbl_enable_comments_hint: 'This option has no effect if comments are disabled globally in the administration panel. This option is enabled by default.',
+			lbl_enable_comments: 'Enable comments on this page',
+			lbl_special_title: 'Mark page as self-contained?',
+			lbl_special_hint: 'This option enables you to use your own HTML headers and other code. If you enable this, only the raw contents of the page will be displayed instead of Enano\'s full page formatting and styles. It is recommended that only advanced users enable this feature. As with other Enano pages, you may use PHP code in your pages (dependent on permissions), meaning you can use Enano\'s API on the page.',
+			lbl_special: 'Bypass the template engine for this page',
+			lbl_visible_title: 'Make page publicly listed?',
+			lbl_visible_hint: 'If you enable this option, this page will be indexed for searching and will appear in public page lists such as Special:AllPages. This option is enabled by default. Disabling this does not protect the page from unauthorized access. If you want to keep this page from being accessed without authorization, you should create a new ACL rule or password-protect the page.',
+			lbl_visible: 'Allow page to be indexed and listed',
+			lbl_protected_title: 'Protect page from edits?',
+			lbl_protected_off: 'Unprotected',
+			lbl_protected_on: 'Fully protected',
+			lbl_protected_semi: 'Semi-protected',
+			lbl_protected_hint: 'This option only has an effect if Wiki Mode is enabled. Selecting Unprotected means that any user (unless specifically blacklisted) can edit this page. Fully protected means that only administrators can edit the page. Semi-protected restricts editing to administrators and users that have been registered for at least four days.<br /><br /><b>Above all, no users except administrators can edit this page unless an ACL specifically allows it or Wiki Mode is enabled.</b>',
+			lbl_wikimode_title: 'Enable Wiki Mode for this page?',
+			lbl_wikimode_on: 'Enabled',
+			lbl_wikimode_off: 'Disabled',
+			lbl_wikimode_global: 'Inherit global setting',
+			lbl_wikimode_hint: 'By default, all pages use the Wiki Mode setting defined in General Configuration. You can override that using this field. Be aware that there are advantages and disadvantages to Wiki Mode. For example, Wiki Mode encourages collaboration, but also permits vandalism. See the <a href="http://docs.enanocms.org/Help:2.5" onclick="window.open(this.href); return false;">Enano Documentation article on Wiki Mode</a> for more information.',
+			lbl_delete_title: 'Delete this page?',
+			lbl_delete_hint: 'Remember that deleting pages is always reversible unless you clear the page\'s logs after deleting it.',
+			lbl_delete: 'Delete this page when I click Save',
+			
+			err_invalid_page_name: 'Please enter a name for the page.',
+			err_invalid_url_string: 'Please enter a URL string for the page.',
+			err_invalid_namespace: 'The namespace you selected is, for whatever reason, not valid.',
+			err_invalid_protection: 'The protection level selected is invalid.',
+			err_invalid_wiki_mode: 'The Wiki Mode level selected is invalid.',
+			err_header: 'There were one or more problems that prevented the page from being saved:',
+			delete_reason: 'Administrative deletion from admin CP; contact webmaster for details',
+			
+			msg_save_success: 'Your changes to the page have been saved. <a href="%viewpage_url%">View page &raquo;</a>',
+		},
+		acped: {
+			heading_main: 'Edit page content',
+			hint: 'This panel allows you to edit the contents of pages that are stored in the database.',
+			// The rest of this section is identical to the first parts of the acppm category by default (you can copy and paste).
+			err_page_not_found: 'No pages matching that search string could be found.',
+			msg_results_ambiguous_title: 'Ambiguous search results',
+			msg_results_ambiguous_body: 'Multiple pages that matched your search terms were found. Please select the page you wish to edit:',
+			ambig_btn_viewpage: 'View',
+			err_ambig_absolute: 'Your database is corrupt as it contains multiple pages with the same urlname and namespace.',
+			lbl_field_search: 'Search for a page title or URL string:',
+			heading_select_page_from_list: 'Select page from a list',
+			hint_select_page_from_list: 'You can also select the page you want to edit from the list below. The list is broken into sections of 100 pages, so if you have a lot of pages on your site, you can click the pagination control below to view more pages.',
+		},
+		acptm: {
+			heading_edit_themes: 'Installed themes',
+			btn_system_themes_show: '&raquo; Show system themes',
+			btn_system_themes_hide: '&raquo; Hide system themes',
+			btn_theme_edit: 'Edit',
+			btn_theme_system: 'System theme',
+			heading_install_themes: 'Themes available for installation',
+			btn_theme_install: 'Install',
+			
+			// Editor
+			heading_theme_edit: 'Editing theme: %theme_name%',
+			field_theme_name: 'Theme name:',
+			field_default_style: 'Select a default style:',
+			field_default_theme: 'Site-wide default theme:',
+			field_default_msg_current: 'This is the current default',
+			field_default_btn_make_default: 'Make this the default theme when I click Save',
+			field_disable_title: 'Disable theme: ',
+			field_disable: 'Prevent all users and guests from using this theme',
+			heading_theme_groups: 'User and group policy',
+			field_policy: 'Theme access policy:',
+			field_policy_allow_all: 'Allow everyone to use this theme',
+			field_policy_whitelist: 'Only allow the users and groups I select below',
+			field_policy_blacklist: 'Allow everyone except what I select below',
+			field_acl_heading_groups: 'Groups',
+			field_acl_heading_users: 'Users',
+			field_acl_add_user: 'Add a user: ',
+			btn_uninstall_theme: 'Uninstall theme',
+			msg_uninstall_confirm: 'Are you sure you want to uninstall this theme?',
+			
+			err_invalid_username: 'Please enter a valid username.',
+			err_username_not_found: 'The user you entered does not exist.',
+			err_save_validation_failed: 'One or more of the fields in the form is incorrect. Please ensure that you have entered a proper value for all of the fields in the theme editing form, and then click %this.etc_save_changes% again.',
+			err_uninstalling_default: 'You cannot uninstall the default theme.',
+			err_uninstalling_oxygen: 'You cannot uninstall the Oxygen theme because it\'s used for important functions such as database errors.',
+			warn_cant_disable_default: 'Please note that the theme was not disabled because it is currently the default.',
+			warn_access_with_default: 'If you selected to whitelist or blacklist certain users, that choice will not apply to guests because this is the default theme.',
+			msg_save_success: 'Your changes to this theme have been saved.',
+			msg_uninstall_success: 'The selected theme has been uninstalled.',
+		},
+		acpcm: {
+			heading_main: 'Performance and caching settings',
+			intro: 'From this page you can control what information on your site is stored in cache files. Caching speeds up Enano performance by allowing data to be retrieved from the disk instead of querying the database. Sometimes the cache isn\'t updated immediately when the database is. From this page you can control what is cached, and you can clear specific caches to force them to refresh.',
+			msg_refresh_warning: 'Some of the caches on this page will automatically refresh immediately even if you click Clear. This is because any use of the component will invoke the cache. To disable this behavior, you must disable caching site-wide.',
+			table_header: 'Cache settings',
+			lbl_enable_cache: 'Enable the cache (recommended)',
+			hint_enable_cache: 'To use the cache, the folder "cache" in the Enano root directory needs to be writable by the server. You can usually accomplish this by using your FTP client\'s CHMOD feature to set the permissions on this folder to 777. Learn more <a href="http://en.wikipedia.org/wiki/Chmod" onclick="window.open(this.href); return false;">about UNIX permissions</a>.',
+			btn_clear_all: 'Clear all caches',
+			hint_clear_all: 'To force all caches (except image thumbnails) on the site to empty, click this button. Certain caches, such as language data, must be regenerated in order to fully refresh the cache. It is recommended that you use "%this.acpcm_btn_refresh_all%" below to clear all caches and then regenerate them.',
+			btn_refresh_all: 'Refresh all caches',
+			hint_refresh_all: 'This will clear all caches (except image thumbnails) on the site to empty and then regenerate, except certain components that are cached on demand.',
+			
+			th_individual_caches: 'Individual caches',
+			btn_clear: 'Clear',
+			btn_refresh: 'Refresh',
+			
+			cache_page_desc_title: 'Page metadata',
+			cache_page_desc_body: 'Information about pages on the site is stored in this cache. This cache is updated when a page is created, renamed or deleted, and it expires every 20 minutes.',
+			cache_ranks_desc_title: 'User ranks',
+			cache_ranks_desc_body: 'Since user ranks take a long time to calculate, fully computed rank data is cached to speed loading of comments and user pages. This cache is updated when any user information is changed, and its expiry time is 15 minutes.',
+			cache_sidebar_desc_title: 'Sidebar for guest users',
+			cache_sidebar_desc_body: 'Rendering the sidebar is a CPU-intensive process because of the number of templates that must be parsed and the custom logic in sidebars. As a result, the fully rendered sidebar is cached for guests. This cache is updated when the sidebar editor is used and expires every 10 minutes.',
+			cache_plugins_desc_title: 'Plugin metadata',
+			cache_plugins_desc_body: 'Plugin files contain special information in the headers that allow Enano to read information about the plugin. Since it takes time to read and parse this information, metadata about plugins is cached. This cache is updated when any change is detected to the first 10 lines of a PHP file in the plugins/ directory.',
+			cache_template_desc_title: 'Template files',
+			cache_template_desc_body: 'Page templates have a lot of logic that takes time to process. They are compiled and then cached on disk to speed loading. This cache is cleared whenever a template file is changed. This cachs cannot be refreshed, as refreshes take place incrementally.',
+			cache_aes_desc_title: 'Encrypted session keys',
+			cache_aes_desc_body: 'Enano encrypts session keys using the AES encryption cipher. Decrypted session keys are cached on the server in a way that is inaccessible to users browsing the site. This cache is dynamically updated whenever a string is sent to the AES decryption module. Clearing it may cause a temporary increase in the load on your site and should only be done if disk space is a concern. This cache is limited to 5,000 keys; when this limit is reached, the oldest 2,500 keys are deleted.',
+			cache_lang_desc_title: 'Language strings',
+			cache_lang_desc_body: 'Language strings, or the text that makes up the Enano user interface, are cached on the disk because they take up a large amount of space in the database. Caching this information allows Enano to minimize bandwidth used when communicating with the database server.',
+			cache_js_desc_title: 'Compressed Javascript runtimes',
+			cache_js_desc_body: 'The on-page tools that Enano provides require a significant amount of Javascript code, which is compressed and cached for better load times. This cache is updated whenever any of the Javascript files are changed, but it is an incremental cache, meaning that it cannot be refreshed manually (only cleared).',
+			cache_thumbs_desc_title: 'Image thumbnails',
+			cache_thumbs_desc_body: 'Thumbnails (preview-size versions of uploaded images) can take up to 2 seconds to generate, usually increasing load times and potentially causing increased server load. This cache is refreshed on demand. Clearing this cache may cause increased loads on your server.',
+			cache_wikieditnotice_desc_title: 'Wiki mode legal notice',
+			cache_wikieditnotice_desc_body: 'This notice is rendered and cached to reduce database queries. If you use templates in your edit notice, you need to clear this cache to see the changes take effect. This cache expires every 60 minutes.',
+			
+			msg_action_success: 'The action you requested was successful.',
+			err_action_failed: 'There was an error during the requested action.'
+		},
+		acpdb: {
+			err_not_supported_title: 'Not supported',
+			err_not_supported_desc: 'This function is only supported under the MySQL database driver.',
+			err_demo_mode_title: 'Access denied',
+			err_demo_mode_desc: 'Since you\'re using the Enano demo, we can\'t allow database backups. Sorry.',
+			
+			intro: 'This page allows you to back up your Enano database should something go miserably wrong.',
+			lbl_system_tables: 'Export tables that are part of the Enano core',
+			lbl_additional_tables: 'Additional tables to export:',
+			lbl_include_structure: 'Include table structure',
+			lbl_include_data: 'Include table data',
+			btn_create_backup: 'Create backup',
+		},
+		acplm: {
+			// Language installation
+			heading_install: 'Languages available for installation',
+			col_lang_id: 'ID',
+			col_lang_code: 'Shorthand code',
+			col_lang_name: 'Language name (native)',
+			col_lang_name_eng: 'Language name (English)',
+			btn_install_language: 'Install',
+			err_lang_install_demo: 'Modifying, installing, and uninstalling languages is disabled in the demo for security reasons.',
+			msg_lang_install_success: 'The language pack %lang_name% has been installed.',
+			
+			// Editor portal
+			heading_editor_portal: 'Edit installed languages',
+			portal_btn_edit: 'Modify',
+			portal_btn_unin: 'Uninstall',
+			
+			// Properties table
+			heading_modify: 'Edit language info',
+			th_lang_basic: 'Basic language properties',
+			field_lang_name_native: 'Language name (native):',
+			field_lang_name_english: 'Language name (in English):',
+			field_lang_code: 'Shorthand code:',
+			field_lang_code_hint: 'You can\'t change this because it needs to be compliant with <a onclick="window.open(this.href); return false;" href="http://en.wikipedia.org/wiki/ISO_639-3">ISO 639-3</a>.',
+			msg_basic_save_success: 'Changes saved.',
+			
+			// String editor portal
+			heading_edit_strings_portal: 'Edit strings',
+			msg_edit_strings_portal_intro: 'You can edit the actual language strings used in this language. Be sure to preserve any variables (in the format of %variable_name%) even if you\'re translating a language. If you\'re translating all of Enano into a new language, you should edit the JSON files instead of using this console, so that comments in the language files can be preserved.',
+			btn_edit_strings_portal: 'Edit strings &raquo;',
+			
+			// Re-import button and explanation
+			heading_reimport_portal: 'Re-import this language',
+			msg_reimport_portal_intro: 'If you accidentally changed a lot of strings, you can re-import this language from the original language files. This will <b>destroy</b> any modifications you have made to the default set of strings, but any strings you\'ve added will be preserved. This is almost the same effect as re-installing the language. Don\'t stop this process while it\'s running, the re-import can take a long time.',
+			btn_reimport: 'Re-import language',
+			msg_reimport_success: 'The language was re-imported successfully. All Enano preset strings for this language have been restored.',
+			
+			// String editor
+			editor_heading: 'Editing category: %cat_name%',
+			editor_col_string_name: 'String name',
+			editor_col_string_content: 'String',
+			editor_btn_revert: 'Revert',
+			editor_btn_cancel: 'Cancel',
+			msg_string_save_success: 'Your changes have been saved.',
+			
+			// Backup interface
+			heading_backup: 'Backup language',
+			backup_intro: 'You can back up this language to make preserving custom strings easier if you ever migrate your Enano installation or re-install. Backed-up language files can be restored by using FTP or equivalent to copy the backup file to the language\'s folder, renaming it to "backup.json", and re-importing the language.',
+			btn_create_backup: 'Download backup',
+			
+			// Uninstaller
+			uninstall_confirm_title: 'Confirm uninstallation of language',
+			uninstall_confirm_body: 'Do you really want to uninstall this language? If you continue, all users that have selected this language will be reset to use the default. It is recommended that you create a backup of this language before you uninstall it if you have changed any strings.',
+			btn_uninstall_confirm: 'Confirm uninstallation',
+			btn_uninstall_cancel: 'Cancel',
+			err_cant_uninstall_default: 'You cannot uninstall the default language.',
+			msg_uninstall_success: 'The language has been uninstalled.',
+		},
+		acppg: {
+			// Main menu
+			heading_main: 'Manage page groups',
+			hint_intro: 'Enano\'s page grouping system allows you to build sets of pages that can be controlled by a single ACL rule. This makes managing features such as a members-only section of your site a lot easier. If you don\'t use the ACL system, you probably don\'t need to use page groups.',
+			col_group_name: 'Group name',
+			col_type: 'Type',
+			col_target: 'Target',
+			col_actions: 'Actions',
+			gtype_catlink: 'Link to category',
+			gtype_tagged: 'Group of pages with one tag',
+			gtype_static: 'Static group of pages',
+			gtype_regex: 'Regular expression match',
+			gtype_regex_long: 'Perl-compatible regular expression (advanced)',
+			lbl_tag: 'Tag:',
+			lbl_category: 'Category:',
+			lbl_regex: 'Expression:',
+			btn_edit: 'Edit',
+			btn_delete: 'Delete',
+			msg_no_groups: 'No page groups defined.',
+			btn_create_new: 'Create new group',
+			
+			// Creation form
+			err_no_cats: 'There aren\'t any categories on this site.',
+			th_create: 'Create page group',
+			field_group_name: 'Group name:',
+			field_group_name_hint: 'This should be short, descriptive, and human-readable.',
+			field_group_type: 'Group type:',
+			
+			field_member_pages: 'Member pages:',
+			field_member_pages_hint: 'Click the "plus" button to add more fields.',
+			field_target_category: 'Include pages in this category:',
+			field_target_category_hint: 'Pages in subcategories are <u>not</u> included, however subcategory pages themselves are.',
+			field_target_category_hint2: '<b>Reminder:</b> Enano does not automatically place any access controls on the category. If you don\'t want users to be able to freely add and remove pages from the category (assuming Wiki Mode is enabled for the category) then you need to enable protection on the category using the button on the more options menu.',
+			field_target_tag: 'Include pages with this tag:',
+			field_target_regex: 'Regular expression:',
+			field_target_regex_hint: 'Be sure to include the starting and ending delimiters and any flags you might need.<br />
+																These pages might help: <a href="http://us.php.net/manual/en/reference.pcre.pattern.modifiers.php">Pattern modifiers</a> &bull; <a href="http://us.php.net/manual/en/reference.pcre.pattern.syntax.php">Pattern syntax</a><br />
+																Examples: <tt>/^(Special|Admin):/i</tt> &bull; <tt>/^Image:([0-9]+)$/</tt><br />
+																Developers, remember that this will be matched against the full page identifier string. This means that <tt>/^About_Enano$/</tt> will NOT match the page Special:About_Enano.',
+			btn_create_finish: 'Create page group',
+			
+			err_need_name: 'Please enter a name for the page group.',
+			err_need_tag: 'Please enter a page tag.',
+			err_need_cat: 'Please create a category page before linking a page group to a category.',
+			err_need_page: 'Please specify at least one page to place in this group.',
+			err_need_regex: 'Please specify a regular expression to match page IDs against.',
+			msg_create_success: 'The page group "%group_name%" has been created.',
+			
+			// Delete form
+			th_delete_confirm: 'Confirm deletion',
+			msg_delete_confirm: 'Are you sure you want to delete this page group?',
+			btn_delete_confirm: 'Yes, delete group',
+			msg_delete_success: 'The group "%pg_name%" has been deleted.',
+			
+			// Editor
+			th_editing_group: 'Editing page group:',
+			btn_save_name: 'Save group name',
+			th_remove_selected: 'Remove pages from this group',
+			field_remove: '<b>Remove</b> pages:',
+			btn_do_remove: 'Remove selected',
+			btn_save_update: 'Save and update',
+			btn_cancel_all: 'Cancel all changes',
+			th_onthefly: 'On-the-fly tools',
+			field_add_page: 'Add page:',
+			field_add_page_hint: 'You can add multiple pages by entering part of a page title, and it will be auto-completed. Press Enter to quickly add the page. This only works if you a really up-to-date browser.',
+			
+			// Validation messages and errors
+			err_ajaxadd_need_title: 'Please enter a page title.',
+			err_ajaxadd_already_in: 'The page you are trying to add is already in this group.',
+			ajaxadd_success: 'The page has been added to the specified group.',
+			err_save_need_name: 'Please enter a valid name for this group.',
+			msg_save_name_updated: 'The group name was updated successfully.',
+			err_save_need_tag: 'Please enter a valid tag.',
+			msg_save_tag_updated: 'The affecting tag was updated.',
+			err_save_need_regex: 'Please enter an expression to match against.',
+			msg_save_regex_updated: 'The expression to match against was updated.',
+			err_save_bad_category: 'No category ID specified on POST URI.',
+			msg_save_cat_updated: 'The affecting category was updated.',
+			err_save_no_pages: 'No pages were selected for deletion, and thus none were deleted.',
+			msg_save_pages_deleted: 'The requested page group members have been deleted.',
+		},
+		acpum: {
+			heading_main: 'User administration panel',
+			hint_intro: 'From this panel you can modify or delete user accounts.',
+			field_search_user: 'Search for user:',
+			field_search_user_hint: 'If your browser supports AJAX, this will provide suggestions for you.',
+			btn_search_user_go: 'Go',
+			heading_clear_sessions: 'Clear session key table',
+			hint_clear_sessions: 'It\'s a good idea to clean out your session keys table every once in a while, since this helps to reduce database size. During this process you will be logged off and (hopefully) logged back on automatically. If you do this, all users besides you will be logged off, so be sure to do this at a time when traffic is low.',
+			btn_clear_sessions: 'Clear session keys',
+			
+			heading_activation_one: '1 user is awaiting account activation',
+			heading_activation_plural: '%count% users are awaiting account activation',
+			col_activate_timestamp: 'Date of request',
+			col_activate_requestedby: 'Requested by',
+			col_activate_requestedfor: 'Requested for',
+			col_activate_coppauser: 'COPPA user',
+			col_activate_actions: 'Actions',
+			coppauser_yes: 'Yes',
+			coppauser_no: 'No',
+			btn_activate_now: 'Activate now',
+			btn_send_email: 'Send activation e-mail',
+			btn_activate_deny: 'Deny request',
+			msg_activate_success: 'The user account "%username%" has been activated.',
+			err_activate_fail: 'The user account %username% has NOT been activated, possibly because the account is already active.',
+			msg_activate_email_success: 'The user %username% has been sent an e-mail with an activation link.',
+			err_activate_email_fail: 'The user account %username% has not been activated, probably because of a bad SMTP configuration.',
+			msg_activate_deny_success: 'All activation requests for the user %username% have been deleted.',
+			
+			msg_sessionclear_success: 'The session key table has been cleared. Your database should be a little bit smaller now.',
+			err_sessionclear_demo: 'Sorry Charlie, no can do. You might mess up other people logged into the demo site.',
+			
+			// VALIDATION ERRORS
+			err_bad_username: 'The username you entered could not be found.',
+			err_validation_fail: 'Your request could not be processed due to the following validation errors:',
+			err_nosave_demo: 'Users cannot be modified or deleted in demo mode.',
+			msg_delete_success: 'The user account has been deleted.',
+			// Note the difference between this and err_bad_username. err_bad_username is shown when the username entered
+			// doesn't match any usernames in the database (e.g. no search results); err_illegal_username is shown when
+			// the admin tries to change the username to one that has illegal characters in it.
+			err_illegal_username: 'The username you entered contains invalid characters.',
+			err_no_aes_key: 'Session manager denied public encryption key lookup request',
+			err_illegal_email: 'You have entered an invalid e-mail address.',
+			msg_save_success: 'Your changes have been saved.',
+			
+			// EDITOR SMARTFORM
+			heading_editing_user: 'Editing user:',
+			heading_basic_options: 'Basic options',
+			
+			field_username: 'Username:',
+			field_username_hint: 'Must be at least 2 characters in length',
+			msg_same_user_username: 'You cannot change your own username. To change your username you must log into a different administrative account.',
+			
+			field_password: 'Password:',
+			field_password_hint: 'Password strength requirements are not enforced here.',
+			msg_password_unchanged: 'Password will be left unchanged.',
+			btn_reset_password: 'Reset password...',
+			msg_same_user_password: 'To change your password, please use the user preferences panel.',
+			field_password_title: 'Change password to:',
+			field_newpassword: 'New password:',
+			field_newpassword_confirm: 'Confirm:',
+			
+			field_email: 'E-mail address:',
+			msg_same_user_email: 'To change your e-mail address, please use the user preferences panel.',
+			
+			field_realname: 'Real name:',
+			msg_same_user_realname: 'To change your real name on file, please use the user preferences panel.',
+			
+			field_signature: 'Signature:',
+			
+			field_usertitle: 'User title:',
+			field_usertitle_hint: 'Displayed below the avatar or username, and above the rank.',
+			
+			heading_imcontact: 'Instant messenger contact information',
+			
+			field_aim: 'AIM handle:',
+			field_wlm: '<acronym title="Windows&trade; Live Messenger">WLM</acronym> handle:',
+			field_wlm_hint: 'If you don\'t specify the domain (@whatever.com), "@hotmail.com" will be assumed.',
+			field_yim: 'Yahoo! IM handle:',
+			field_xmpp: 'Jabber&trade;/XMPP handle:',
+			
+			heading_contact_extra: 'Extra contact information',
+			
+			field_homepage: 'Homepage:',
+			field_homepage_hint: 'Please remember the http:// prefix.',
+			field_location: 'Location:',
+			field_job: 'Job:',
+			field_hobbies: 'Hobbies:',
+			field_email_public: 'E-mail address is public',
+			field_email_public_hint: 'If this is checked, the user\'s e-mail address will be displayed on your the page. To protect the address from spambots, it will be encrypted.',
+			
+			avatar_heading: 'Avatar settings',
+			avatar_image_none: 'This user does not currently have an avatar.',
+			avatar_lbl_change: 'Change avatar:',
+			avatar_lbl_keep: 'Keep current setting',
+			avatar_lbl_remove: 'Delete this user\'s avatar',
+			avatar_lbl_set_http: 'Replace avatar using a new image from a URL',
+			avatar_lbl_set_file: 'Replace avatar using a new image from my computer',
+			avatar_lbl_set_gravatar: 'Replace avatar with Gravatar',
+			
+			heading_adminonly: 'Administrator-only options',
+			
+			field_active_title: 'User account is active',
+			field_active_hint: 'If this is unchecked, the existing activation key will be overwritten in the database, thus invalidating any activation e-mails sent to the user.',
+			field_active: 'Account is active and enabled',
+			field_userlevel: 'User\'s site access level',
+			field_userlevel_hint: 'If this is changed, the relevant group memberships will be updated accordingly.',
+			field_userrank: 'User\'s rank:',
+			field_userrank_hint: 'You can create more ranks using User Ranks and Titles. Ranks do not affect permissions (e.g. setting their rank to Administrator here will not give them administrator permissions).',
+			field_reg_ip: 'Registered from IP:',
+			
+			field_deleteaccount_title: 'Delete user account',
+			field_deleteaccount: 'Permanently delete this user account when I click Save',
+			msg_delete_own_account: '<blink style="color: red;">WARNING!</blink> This will delete your own user account!',
+			field_deleteaccount_hint: 'Even if you delete this user account, the username will be shown in page edit history, comments, and other areas of the site. Deleting a user account CANNOT BE UNDONE and should only be done in extreme circumstances. If the user has violated the site policy, deleting the account will not prevent him from using the site or creating a new account, for that you need to add a new ban rule.',
+			
+			btn_save: 'Save changes',
+		},
+		acpug: {
+			heading_main: 'Manage Usergroups',
+			heading_edit_existing: 'Edit an existing group',
+			btn_edit_stage1: 'Edit group',
+			heading_create_new: 'Create a new group',
+			field_group_name: 'Group name:',
+			field_group_rank: 'Assigned rank:',
+			btn_create_stage1: 'Continue',
+			
+			// Edit form
+			heading_edit_name: 'Edit group basics',
+			btn_cant_delete: 'Can\'t delete system group',
+			btn_delete_group: 'Delete this group',
+			btn_save_name: 'Save changes',
+			heading_edit_members: 'Edit group members',
+			msg_no_members: 'This group has no members.',
+			lbl_member_mod: 'Mod',
+			btn_remove_member: 'Remove member',
+			heading_add_member: 'Add a new member',
+			field_username: 'Username:',
+			field_make_mod: 'Is a group moderator',
+			field_make_mod_hint: '(can add and delete other members)',
+			btn_add_user: 'Add user to group',
+			
+			// Create form
+			err_group_name_invalid: 'The group name you chose is invalid.',
+			heading_creating_group: 'Creating group:',
+			field_group_mod: 'Group moderator',
+			field_group_type: 'Group status',
+			btn_create_stage2: 'Create group',
+			err_already_exist: 'The group name you entered already exists.',
+			err_bad_username: 'The username you entered could not be found.',
+			err_bad_insert_id: 'The group ID could not be looked up.',
+			heading_info: 'Information',
+			msg_create_success: 'The group %g_name% has been created successfully.',
+			
+			// More editor bits, validation messages
+			err_nodelete_system_group: 'The group "%g_name%" could not be deleted because it is a system group required for site functionality.',
+			msg_delete_success: 'The group "%g_name%" has been deleted. Return to the <a %a_flags%>group manager</a>.',
+			msg_name_update_success: 'The group name and rank have been updated.',
+			msg_user_added: 'The user "%username%" has been added to this usergroup.',
+			err_username_not_exist: '<b>The user "%username%" could not be added.</b><br />This username does not exist.',
+		},
+		acpcp: {
+			heading_main: 'Background information',
+			intro: 'The United States Childrens\' Online Privacy Protection Act (COPPA) was a law passed in 2001 that requires sites oriented towards children under 13 years old or with a significant amount of under-13 children clearly state what information is being collected in a privacy policy and obtain authorization from a parent or legal guardian before allowing children to use the site. Enano provides an easy way to allow you, as the website administrator, to obtain this authorization.',
+			msg_save_success: 'Your changes have been saved.',
+			th_form: 'COPPA support',
+			field_enable_title: 'Enable COPPA support:',
+			field_enable: 'COPPA enabled',
+			field_enable_hint: 'If this is checked, users will be asked if they are under 13 years of age before registering',
+			field_address: 'Your mailing address:',
+			field_address_hint: 'This is the address to which parents will send authorization forms.',
+		},
+		acpmm: {
+			heading_main: 'Send mass e-mail',
+			err_need_subject: 'Please enter a subject.',
+			err_need_message: 'Please enter a message.',
+			msg_send_success: 'Your message has been sent.',
+			err_send_fail: 'Could not send message for the following reason(s):',
+			err_demo: 'This function is disabled in the demo. You think demo@enanocms.org likes getting "test" mass e-mails?',
+			field_group_to: 'Send message to:',
+			field_group_to_hint: 'By default, this message will be sent to the group selected here. You may instead send the message to a specific list of users by entering them in the second row, with usernames separated by a single comma (no space).',
+			field_username: 'Usernames:',
+			field_subject: 'Subject:',
+			field_message: 'Message:',
+			btn_send: 'Send message',
+			msg_send_takeawhile: 'Please be warned: it may take a LONG time to send this message. <b>Please do not stop the script until the process is finished.</b>',
+		},
+		acpur: {
+			heading_main: 'User ranks and titles',
+			intro_para1: 'This screen lets you control users\' ranks and custom user titles. User ranks let you provide a graphic distinction between different types or roles of users using colors and other style rules. You can also associate a snippet of text with a rank; this will be displayed below the username of users that are in this rank.',
+			intro_para2: 'A user\'s rank is only a visual distinction; no permissions are associated with it. In other words, if someone is in the Administrators group but has the rank of %this.user_rank_member%, they will still have administrative abilities. On the flip side, giving someone the rank of %this.user_rank_admin% will not give them administrator privileges, but they will be labeled as an administrator.',
+			intro_para3: 'Ranks can be assigned based on a lot of different criteria. By default, Enano maps the Administrator and Moderator ranks based on user level. You can also assign a rank based on group memberships (e.g. being in a group automatically assigns a specific rank) and set individual ranks for users.',
+			intro_para4: 'This screen lets you create, edit, and delete ranks. To assign them, edit the respective user or usergroup.',
+			
+			msg_select_rank: 'To edit a rank, select it on the left. Or, click Create New Rank to start a new rank.',
+			msg_rank_delete_confirm_title: 'Delete this rank?',
+			msg_rank_delete_confirm_body: 'This will completely remove this rank and unassociate any users and groups with it.',
+			
+			th_edit_rank: 'Edit rank: %rank_title%',
+			th_create_rank: 'Create new rank',
+			
+			field_rank_title: 'Rank name:',
+			field_style_basic: 'Basic style options:',
+			field_style_basic_bold: 'Bold',
+			field_style_basic_italic: 'Italic',
+			field_style_basic_underline: 'Underline',
+			field_style_color: 'Username color:',
+			field_style_css: 'Additional CSS:',
+			field_preview: 'Preview:',
+			
+			btn_save: 'Save rank',
+			btn_create_submit: 'Create rank',
+			btn_create_init: 'Create new rank',
+			btn_delete: 'Delete rank',
+			
+			err_save_failed_title: 'Saving operation failed',
+			err_delete_failed_title: 'Deletion operation failed',
+			err_missing_rank_title: 'Please enter a title for this rank.',
+			err_cant_delete_system_rank: 'The rank you are trying to delete is an important system rank that cannot be deleted. However, you may customize the title and style of it to make it appear normal.',
+		},
+		acpsl: {
+			heading_main: 'System security log',
+			col_type: 'Type',
+			col_date: 'Date',
+			col_username: 'Username',
+			col_ip: 'IP Address',
+			entry_admin_auth_good: 'Successful elevated authentication<br /><small>Authentication level: %level%</small>',
+			entry_admin_auth_bad: 'Failed elevated authentication<br /><small>Attempted auth level: %level%</small>',
+			entry_activ_good: 'Successful account activation',
+			entry_auth_good: 'Successful regular user logon',
+			entry_activ_bad: 'Failed account activation',
+			entry_auth_bad: 'Failed regular user logon',
+			entry_sql_inject: 'SQL injection attempt<div style="max-width: 90%; clip: rect(0px,auto,auto,0px); overflow: auto; display: block; font-size: smaller;">Offending query: %query%</div>',
+			entry_db_backup: 'Database backup created<br /><small>Tables: %tables%</small>',
+			entry_install_enano: 'Installed Enano version %version%',
+			entry_upgrade_enano: 'Upgraded Enano to version %version%',
+			entry_illegal_page: 'Unauthorized viewing attempt<br /><small>Page: %illegal_link%</small>',
+			entry_upload_enable: 'Enabled file uploads',
+			entry_upload_disable: 'Disabled file uploads',
+			entry_magick_enable: 'Enabled ImageMagick for uploaded images',
+			entry_magick_disable: 'Disabled ImageMagick for uploaded images',
+			entry_filehist_enable: 'Enabled revision tracking for uploaded files',
+			entry_filehist_disable: 'Disabled revision tracking for uploaded files',
+			entry_magick_path: 'Changed path to ImageMagick executable',
+			entry_plugin_disable: 'Disabled plugin: %plugin%',
+			entry_plugin_enable: 'Enabled plugin:  %plugin%',
+			entry_plugin_install: 'Installed plugin:  %plugin%',
+			entry_plugin_uninstall: 'Uninstalled plugin:  %plugin%',
+			entry_plugin_upgrade: 'Upgraded plugin:  %plugin%',
+			entry_seclog_unauth: 'Unauthorized attempt to call security log fetcher',
+			entry_u_from_admin: 'User %username% demoted from Administrators group',
+			entry_u_from_mod: 'User %username% demoted from Moderators group',
+			entry_u_to_admin: 'User %username% added to Administrators group',
+			entry_u_to_mod: 'User %username% added to Moderators group',
+			entry_view_comment_ip: 'IP address viewed on comment by %username%',
+			tip_reverse_dns: 'Click for reverse DNS info',
+			
+			err_invalid_ip: 'No valid IPv4 or IPv6 address was provided. (In the demo, this is to be expected.)',
+			err_ptr_no_resolve: 'Reverse DNS was not available for this IP.'
+		},
+		acpbc: {
+			err_empty: 'Please enter something to ban.',
+			err_invalid_ip_range: 'The IP address range you entered is invalid.',
+			err_demo: 'This function is disabled in the demo. Just because <i>you</i> don\'t like %ban_target% doesn\'t mean <i>we</i> don\'t like %ban_target%.',
+			col_type: 'Type',
+			col_value: 'Value',
+			col_regex: 'Regular Expression',
+			msg_no_rules: 'No ban rules yet.',
+			ban_type_ip: 'IP address',
+			ban_type_username: 'Username',
+			ban_type_email: 'E-mail address',
+			btn_delete: 'Delete',
+			// Some languages like Chinese don't literally have the word "yes", so this would be something
+			// along the lines of "This is a regular expression" / "This is not..." in Chinese. Hence the
+			// separation of this from a generic "yes" string.
+			ban_regex_yes: 'Yes',
+			ban_regex_no: 'No',
+			heading_create_new: 'Create new ban rule',
+			field_type: 'Type:',
+			field_rule: 'Rule:',
+			field_rule_hint: 'You can ban multiple IP addresses, users, or e-mail addresses by separating entries with a single comma (User1,User2). Do not put a space after the comma. For IP addresses, you may specify ranges like 172|192.168.4-30|90-167.1-90, which will turn into 172 and 192 . 168 . 4-30 and 90-167 . 1 - 90, which matches 18,899 IP addresses.',
+			field_reason: 'Reason to show to the banned user:',
+			field_regex: 'This rule is a regular expression',
+			field_regex_hint: '(advanced users only)',
+			btn_create: 'Create new ban rule',
+		},
+		acplo: {
+			heading_main: 'You have now been logged out of the administration panel.',
+			msg_logout_complete: 'You will continue to be logged into the website, but you will need to re-authenticate before you can access the administration panel again.</p><p>Return to the <a href="%mainpage_link%">Main Page</a>.',
+		},
+		sbedit: {
+			header_msg: 'This control panel allows you to organize the sidebars, the collections of links and dynamic blocks that provide navigation for your site. Drag and drop blocks to move them between the left and right sidebars; changes will be saved automatically. Red blocks are disabled. This panel only works if you have Javascript enabled and working in your browser. <a href="%create_link%">Create a new block</a>.',
+			
+			msg_order_update_success: 'The sidebar order information was updated successfully.',
+			err_demo_php_disable: 'Adding PHP code blocks in the Enano administration demo has been disabled for security reasons.',
+			msg_item_added: 'The item was added.',
+			
+			create_intro: 'What type of block should this be?',
+			block_type_wiki: 'Textual content (wikitext)',
+			block_type_tpl: 'List of links',
+			block_type_html: 'Pure HTML',
+			block_type_php: 'PHP code',
+			block_type_plugin: 'Plugin block',
+			field_block_title: 'Block title:',
+			field_block_sidebar: 'Which sidebar:',
+			field_block_sidebar_left: 'Left',
+			field_block_sidebar_right: 'Right',
+			field_wikitext: '<p>
+ 												<b>This block type is for textual content.</b> This could be information, a random fact, or some other blob of fully formatted text. Links show up inline,
+ 												not as a list.
+ 											</p>
+ 											<p>
+ 												Wikitext:
+ 											</p>',
+			field_tplcode: '<p>
+												<b>This block type is for links.</b> Use wikilinks (both internal and external are supported) to build a list of links. All links will be shown block-style,
+												or one link per line. You can use logic in these blocks as well:
+											</p>
+											<pre>{if user_logged_in&#x7d;
+	[[Special:Preferences|User control panel]]
 {/if&#x7d;</pre>
-                      <p>
-                        Template code:
-                      </p>',
-      field_html: '<p>
-                     <b>This block type is for textual content.</b> HTML you use here will not be filtered or parsed at all - it will be displayed verbatim. This gives you
-                        slightly more control over your content but you can\'t use wikilinks or other wikitext in here.
-                   </p>
-                   <p>
-                     HTML to place inside the sidebar:
-                   </p>',
-      field_php_disabled: 'Creating PHP blocks in demo mode is disabled for security reasons.',
-      field_php: '<p>
-                    <b>WARNING:</b> If you don\'t know what you\'re doing, or if you are not fluent in PHP, stop now and choose a different block type. You will brick your Enano installation if you are not careful here.
-                    ALWAYS remember to write secure code! The Enano team is not responsible if someone drops all your tables because of an SQL injection vulnerability in your sidebar code. You are probably better off using the links and logic block type.
-                  </p>
-                  <p>
-                    <span style="color: red;">
-                      It is especially important to note that the syntax of your code is not validated here. If there is a syntax error in your code here, it might prevent Enano from working properly. It is recommended that you use an external PHP editor (like <a href="http://www.jedit.org">jEdit</a>) to check your syntax before you hit save.
-                    </span> You have been warned.
-                  </p>
-                  <p>
-                    Also, you should avoid using output buffering functions (ob_[start|end|get_contents|clean]) here, because Enano uses those to track output from this script.
-                  </p>
-                  <p>
-                    The standard &lt;?php and ?&gt; tags work here, but don\'t use an initial "&lt;?php" or it will cause a parse error.
-                  </p>
-                  <p>
-                    PHP code:
-                  </p>',
-      field_plugin: '<p>Plugin:</p>',
-      btn_create_block: 'Create new block',
-      
-      msg_block_moved: 'Item moved.',
-      msg_block_deleted: 'Item deleted.',
-      msg_plugin_not_loaded: 'Plugin isn\'t loaded',
-      msg_order_saved: 'Sidebar order saved.',
-      note_block_unnamed: 'Unnamed',
-      hint_rename: 'Double-click to rename this block',
-      note_block_disabled: '(disabled)',
-      tip_disenable: 'Toggle',
-      tip_edit: 'Edit block',
-      tip_delete: 'Delete',
-      tip_rename: 'Rename',
-      msg_delete_confirm_title: 'Delete block',
-      msg_delete_confirm_body: 'Are you sure you want to delete this sidebar block?',
-      btn_delete_confirm: 'Delete block',
-      btn_revert: 'Revert',
-      btn_create_new_stage1: 'Create new block',
-      btn_main_page: 'Main Page',
-      msg_cant_edit_plugin_title: 'This block cannot be edited.',
-      msg_cant_edit_plugin_body: 'This is a plugin block, and cannot be edited. <%close_link%>Close</a>',
-      btn_edit_save: 'save',
-      btn_edit_cancel: 'cancel',
-      msg_discard_confirm: 'Do you really want to discard your changes?',
-      msg_discard_order_confirm: 'Do you really want to revert your changes?\nNote: this does not revert edits or deletions, those are saved as soon as you confirm the action.',
-    }
-  }
+											<p>
+												Template code:
+											</p>',
+			field_html: '<p>
+ 										<b>This block type is for textual content.</b> HTML you use here will not be filtered or parsed at all - it will be displayed verbatim. This gives you
+												slightly more control over your content but you can\'t use wikilinks or other wikitext in here.
+ 									</p>
+ 									<p>
+ 										HTML to place inside the sidebar:
+ 									</p>',
+			field_php_disabled: 'Creating PHP blocks in demo mode is disabled for security reasons.',
+			field_php: '<p>
+										<b>WARNING:</b> If you don\'t know what you\'re doing, or if you are not fluent in PHP, stop now and choose a different block type. You will brick your Enano installation if you are not careful here.
+										ALWAYS remember to write secure code! The Enano team is not responsible if someone drops all your tables because of an SQL injection vulnerability in your sidebar code. You are probably better off using the links and logic block type.
+									</p>
+									<p>
+										<span style="color: red;">
+											It is especially important to note that the syntax of your code is not validated here. If there is a syntax error in your code here, it might prevent Enano from working properly. It is recommended that you use an external PHP editor (like <a href="http://www.jedit.org">jEdit</a>) to check your syntax before you hit save.
+										</span> You have been warned.
+									</p>
+									<p>
+										Also, you should avoid using output buffering functions (ob_[start|end|get_contents|clean]) here, because Enano uses those to track output from this script.
+									</p>
+									<p>
+										The standard &lt;?php and ?&gt; tags work here, but don\'t use an initial "&lt;?php" or it will cause a parse error.
+									</p>
+									<p>
+										PHP code:
+									</p>',
+			field_plugin: '<p>Plugin:</p>',
+			btn_create_block: 'Create new block',
+			
+			msg_block_moved: 'Item moved.',
+			msg_block_deleted: 'Item deleted.',
+			msg_plugin_not_loaded: 'Plugin isn\'t loaded',
+			msg_order_saved: 'Sidebar order saved.',
+			note_block_unnamed: 'Unnamed',
+			hint_rename: 'Double-click to rename this block',
+			note_block_disabled: '(disabled)',
+			tip_disenable: 'Toggle',
+			tip_edit: 'Edit block',
+			tip_delete: 'Delete',
+			tip_rename: 'Rename',
+			msg_delete_confirm_title: 'Delete block',
+			msg_delete_confirm_body: 'Are you sure you want to delete this sidebar block?',
+			btn_delete_confirm: 'Delete block',
+			btn_revert: 'Revert',
+			btn_create_new_stage1: 'Create new block',
+			btn_main_page: 'Main Page',
+			msg_cant_edit_plugin_title: 'This block cannot be edited.',
+			msg_cant_edit_plugin_body: 'This is a plugin block, and cannot be edited. <%close_link%>Close</a>',
+			btn_edit_save: 'save',
+			btn_edit_cancel: 'cancel',
+			msg_discard_confirm: 'Do you really want to discard your changes?',
+			msg_discard_order_confirm: 'Do you really want to revert your changes?\nNote: this does not revert edits or deletions, those are saved as soon as you confirm the action.',
+		}
+	}
 };
 
 // All done! :-)
--- a/language/english/core.json	Sun Mar 28 21:49:26 2010 -0400
+++ b/language/english/core.json	Sun Mar 28 23:10:46 2010 -0400
@@ -16,797 +16,797 @@
 // via Javascript as well.
 
 var enano_lang = {
-  categories: [
-    'meta', 'page', 'comment', 'onpage', 'etc', 'editor', 'history', 'catedit', 'tags', 'delvote', 'ajax', 'sidebar', 'perm', 'plugin', 'paginate', 'upload', 'tz'
-  ],
-  strings: {
-    meta: {
-      meta: 'Category names and basic metadata',
-      page: 'Page creation and control',
-      comment: 'Comment display',
-      onpage: 'On-page buttons and controls',
-      etc: 'Miscellaneous strings',
-      editor: 'Page editor interface',
-      history: 'Page history and log viewer',
-      catedit: 'Categorization box and editor',
-      tags: 'Page tagging interface',
-      delvote: 'Page deletion vote interface',
-      ajax: 'On-page AJAX applets',
-      sidebar: 'Default sidebar blocks and buttons',
-      perm: 'Page actions (for ACLs)',
-      plugin: 'Plugin names and descriptions',
-      paginate: 'Pagination widget',
-      upload: 'File upload interface',
-      tz: 'Time zones',
-      plural: 's',
-      enano_about_th: 'About the Enano Content Management System',
-      enano_about_poweredby: '<p>This website is powered by <a href="http://enanocms.org/">Enano</a>, the lightweight and open source CMS that everyone can use. Enano is copyright &copy; 2006-%year% Dan Fuhry. For legal information, along with a list of libraries that Enano uses, please see <a href="http://enanocms.org/Legal_information">Legal Information</a>.</p><p>The developers and maintainers of Enano strongly believe that software should not only be free to use, but free to be modified, distributed, and used to create derivative works. To help achieve this goal, we use licensing terms that require you to pass on the freedoms we give you when you share Enano. For more information about Free Software, check out the <a href="http://en.wikipedia.org/wiki/Free_Software" onclick="window.open(this.href); return false;">Wikipedia page</a> or the <a href="http://www.fsf.org/" onclick="window.open(this.href); return false;">Free Software Foundation\'s</a> homepage.</p>',
-      enano_about_gpl: '<p>This program is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</p><p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.</p><p>You should have received <a href="%gpl_link%">a copy of the GNU General Public License</a> along with this program; if not, write to:</p><p style="margin-left 2em;">Free Software Foundation, Inc.,<br />51 Franklin Street, Fifth Floor<br />Boston, MA 02110-1301, USA</p><p>Alternatively, you can <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">read it online</a>.</p>',
-      enano_about_lbl_enanoversion: '<a href="http://enanocms.org">Enano</a> version:',
-      enano_about_lbl_webserver: 'Web server:',
-      enano_about_lbl_serverplatform: 'Server platform:',
-      enano_about_lbl_phpversion: '<a href="http://www.php.net/">PHP</a> version:',
-      enano_about_lbl_mysqlversion: '<a href="http://www.mysql.com/">MySQL</a> version:',
-      enano_about_lbl_pgsqlversion: '<a href="http://www.postgresql.org/">PostgreSQL</a> version:'
-    },
-    page: {
-      sitedisabled_admin_msg_title: 'The site is currently disabled and thus is only accessible to administrators.',
-      sitedisabled_admin_msg_body: 'You can re-enable the site through the <a href="%admin_link%">administration panel</a>.',
-      
-      heading_sql_list: 'Query list as requested on URI',
-      
-      msg_stats_gentime_long: 'Generated in %time%sec',
-      msg_stats_gentime_short: 'Time: %time%s',
-      msg_stats_sql: '%nq% SQL',
-      
-      w3c_valid_html32: 'Valid HTML 3.2',
-      w3c_valid_html40: 'Valid HTML 4.0',
-      w3c_valid_html401: 'Valid HTML 4.01',
-      w3c_valid_html50: 'Valid HTML 5.0',
-      w3c_valid_xhtml10: 'Valid XHTML 1.0',
-      w3c_valid_xhtml11: 'Valid XHTML 1.1',
-      w3c_valid_css: 'Valid CSS',
-      enano_powered: 'Powered by <a href="%about_uri%">Enano</a>',
-      enano_powered_long: 'Website engine powered by <a href="%about_uri%">Enano</a>',
-      
-      protect_lbl_success_title: 'Page protected',
-      protect_lbl_success_body: 'The protection setting has been applied. <a href="%page_link%">Return to the page</a>.',
-      
-      rename_err_need_name: 'Error: you must enter a new name for this page.',
-      rename_lbl: 'Please enter a new name for this page:',
-      rename_btn_submit: 'Rename page',
-      rename_success_title: 'Page renamed',
-      
-      flushlogs_warning_stern: '<h3>You are about to <span style="color: red;">destroy</span> all logged edits and actions on this page.</h3><p>Unlike deleting or editing this page, this action is <u>not reversible</u>! You should only do this if you are desparate for database space.</p><p>Do you really want to continue?</p>',
-      flushlogs_btn_submit: 'Flush logs',
-      flushlogs_backup_summary: 'Automatic backup created when logs were purged',
-      
-      delvote_warning_stern: '<h3>Your vote counts.</h3><p>If you think that this page is not relavent to the content on this site, or if it looks like this page was only created in an attempt to spam the site, you can request that this page be deleted by an administrator.</p><p>After you vote, you should leave a comment explaining the reason for your vote, especially if you are the first person to vote against this page.</p>',
-      
-      delvote_count_zero: 'So far, no one has voted for the deletion of this page.',
-      delvote_count_one: 'So far, one person has voted to delete this page.',
-      delvote_count_plural: 'So far, %delvotes% people have voted to delete this page.',
-      delvote_btn_submit: 'Vote to delete this page',
-      delvote_reset_btn_submit: 'Reset votes',
-      
-      delete_warning_stern: '<h3>Confirm page deletion</h3><p>The contents of the page will not be deleted; you will still be able to recover the page using the History feature.</p><p>Please be aware, however, that the following information is not recoverable:</p><ul><li>The list of categories this page belongs to</li><li>Every file revision associated with this page (if this page is in the File namespace)</li><li>Comments on this page</li></ul><p>To delete this page, enter a reason below, and then click Delete this Page.</p>',
-      delete_err_need_reason: 'Please enter a reason for deleting this page.',
-      delete_btn_submit: 'Delete this page',
-      delete_lbl_reason: 'Reason for deleting:',
-      
-      wikimode_success_redirect: 'Wiki mode for this page has been set. Redirecting you to the page...',
-      wikimode_level_on: 'Wiki features will be enabled.',
-      wikimode_level_off: 'Wiki features will be disabled.',
-      wikimode_level_global: 'Wiki features will be synchronized to the global setting.',
-      wikimode_heading: 'You are changing wiki mode for this page.',
-      wikimode_warning: 'If you want to continue, please click the button below.',
-      wikimode_blurb_disable: 'Because this will disable the wiki behavior on this page, several features, most notably the ability for users to vote to have this page deleted, will be disabled as they are not relevant to non-wiki pages. In addition, users will not be able to edit this page unless an ACL rule specifically permits them.',
-      wikimode_blurb_enable: 'Because this will enable the wiki behavior on this page, users will gain the ability to freely edit this page unless an ACL rule specifically denies them. If your site is public and gets good traffic, you should be aware of the possiblity of vandalism, and you need to be ready to revert malicious edits to this page.',
-      wikimode_btn_submit: 'Set wiki mode',
-      
-      detag_err_page_exists: 'The detag action is only valid for pages that have been deleted in the past.',
-      detag_success_title: 'Page detagged',
-      detag_success_body: 'All stale tags have been removed from this page.',
-      
-      err_custompage_function_missing_title: 'Page backend not found',
-      err_custompage_function_missing_body: 'The administration page you are looking for was properly registered using the page API, but the backend function (<tt>%function_name%</tt>) was not found. If this is a plugin page, then this is almost certainly a bug with the plugin.',
-      err_redirects_exceeded: 'The maximum number of internal redirects has been exceeded.',
-      err_redirect_to_nonexistent: 'This page redirects to another page that doesn\'t exist.',
-      err_redirect_infinite_loop: 'This page infinitely redirects with another page (or another series of pages), and the infinite redirect was trapped.',
-      err_redirect_to_special: 'This page redirects to a Special or Administration page, which is not allowed.',
-      err_access_denied_title: 'You don\'t have permission to view this page.',
-      err_access_denied_body: '<p>Your user account doesn\'t have the necessary permission to view this page. There are a number of possible reasons for this:</p>
-                               <ul>
-                                 <li>You aren\'t logged in. Some pages are restricted to logged-in users.</li>
-                                 <li>The page you\'re trying to view is protected so that only members of a specific usergroup are allowed to read it.</li>
-                               </ul>
-                               <p>If you would like to inquire further about this message, you may contact the %site_administration%.</p>',
-      err_access_denied_siteadmin: 'site administrator',
-      err_seeking_living_among_dead: 'You are trying to un-delete a page that has since been restored.\n\n"But the men said to them, \'Why do you look for the living among the dead?\'" (Luke 24:5b/NIV)',
-      err_access_denied: 'Access to that action is denied.',
-      err_access_denied_need_reauth: '%this.etc_access_denied_need_reauth%',
-      err_invalid_parameter: 'An invalid value (parameter) was sent to this action.',
-      err_rb_action_not_supported: 'Rolling back actions of type "%action%" isn\'t supported.',
-      err_rb_file_rename_failed: 'Could not rename the file to its new name (1.1.x format)',
-      err_protection_already_there: 'The protection level you selected is already in effect for this page.',
-      err_page_not_exist: 'The page you tried to perform this action on does not exist.',
-      
-      msg_this_is_a_redirector: '<b>This page is a <i>redirector</i>.</b><br />
-                    This means that this page will not show its own content by default. Instead it will display the contents of the page it redirects to.<br /><br />
-                    To create a redirect page, make the <i>first characters</i> in the page content <tt>#redirect [[Page_ID]]</tt>. For more information, see the
-                    Enano <a href="http://enanocms.org/Help:Wiki_formatting" onclick="window.open(this.href); return false;">Wiki formatting guide</a>.<br /><br />
-                    This page redirects to %redirect_target%.',
-      msg_redirected_from: '(Redirected from %from%)',
-      msg_redirected_from_to: '(Redirected from %from% to %to%)',
-      msg_rb_success_rename: 'The page has been restored the name it had on %dateline%.',
-      // next 3 are mostly identical
-      msg_rb_success_prot: 'The page\'s protection has been undone and replaced with the previous level it had as of %dateline%.',
-      msg_rb_success_unprot: 'The page\'s unprotection has been undone and replaced with the previous level it had as of %dateline%.',
-      msg_rb_success_semiprot: 'The page\'s semi-protection has been undone and replaced with the previous level it had as of %dateline%.',
-      
-      msg_rb_success_delete: 'The deletion of this page, which occurred on %dateline%, has been undone. This page has been restored, but comments and categorization data may have been lost.',
-      msg_rb_success_reupload: 'The file has been restored to the version uploaded on %dateline%.',
-      
-      msg_rb_success_votereset: 'The votes for deletion of this page as of %dateline% have been restored and merged with any existing votes.',
-      
-      msg_passrequired_title: 'Password required',
-      msg_passrequired: 'Access to this page requires a password. Please enter the password for this page below:',
-      msg_pass_wrong: 'The password you entered for this page was incorrect. Please enter the password for this page below:',
-      lbl_password: 'Password:',
-      btn_password_submit: 'Submit',
-      
-      msg_404_title: 'There is no page with this title yet.',
-      msg_404_body: 'You have requested a page that doesn\'t exist yet.',
-      msg_404_title_userpage: 'No content',
-      msg_404_body_userpage: 'This user has not yet created any custom user page content.',
-      msg_404_create: 'You can <a %create_flags%>create this page</a>, or return to the <a href="%mainpage_link%">homepage</a>.',
-      msg_404_gohome: 'Return to the <a href="%mainpage_link%">homepage</a>.',
-      msg_404_was_deleted: '<b>This page was deleted on %delete_time%.</b> The stated reason was:</p><blockquote>%delete_reason%</blockquote><p>You can probably <a %rollback_flags%>roll back</a> the deletion.',
-      msg_404_admin_opts: 'Additional admin options: <a href="%detag_link%" title="Remove any tags on this page">detag page</a>',
-      msg_404_http_response: 'HTTP Error: 404 Not Found',
-      
-      msg_archived_title: 'Notice:',
-      msg_archived_body: 'The page you are viewing was archived on %archive_date% at %archive_time%.<br /><a href="%current_link%" onclick="ajaxReset(); return false;">View current version</a>  |  <a href="%restore_link%" onclick="%restore_onclick%">Restore this version</a>',
-      
-      msg_special_404_title: 'Can\'t load special page',
-      msg_special_404_body: 'The special page you requested could not be found. This may be due to a plugin failing to load. A list of all special pages on this website can be viewed <a href="%sp_link%">here</a>. You will be redirected to the main page in 15 seconds.',
-      msg_admin_404_title: 'Administration page not found',
-      msg_admin_404_body: '<p>You\'ve requested an administration page, but the function <tt>%func_name%</tt> doesn\'t exist, so the page can\'t be loaded.</p>
-                             <h3>Plugin developer?</h3>
-                              <p>Create a function called <tt>%func_name%</tt> - it should be loaded when you refresh this page.</p>
-                             <h3>Otherwise...</h3>
-                              <p>If you\'re trying to use a plugin\'s administration page, contact the developer of the plugin. If you\'re trying to use a function that is built into Enano (not added by a plugin), then please contact the Enano development team.</p>',
-      
-      msg_general_error: 'General error in page fetcher',
-                    
-      autosuggest_heading: 'Page name matches',
-      autosuggest_col_name: 'Page title',
-      autosuggest_col_page_id: 'Page ID'
-    },
-    comment: {
-      lbl_subject: 'Subject',
-      lbl_mod_options: 'Moderator options:',
-      heading: 'Article comments',
-      btn_send_privmsg: 'Send PM',
-      btn_add_buddy: 'Add buddy',
-      btn_edit: 'edit',
-      btn_delete: 'delete',
-      btn_mod_approve: 'Approve',
-      btn_mod_unapprove: 'Unapprove',
-      btn_mod_delete: 'Delete',
-      btn_mod_ip_logged: 'View IP',
-      btn_mod_ip_missing: 'IP not logged',
-      btn_mod_ip_notimplemented: 'Use AJAX interface to view IPs',
-      btn_save: 'save',
-      
-      msg_comment_posted: 'Your comment has been posted. If it does not appear right away, it is probably awaiting approval.',
-      
-      msg_count_zero: 'There are <span id="comment_count_inner">no</span> comments on this %page_type%.',
-      msg_count_one: 'There is <span id="comment_count_inner">1</span> comment on this %page_type%.',
-      msg_count_plural: 'There are <span id="comment_count_inner">%num_comments%</span> comments on this %page_type%.',
-      
-      msg_count_unapp_mod: '<span id="comment_count_unapp_inner">%num_unapp%</span> of those are unapproved.',
-      msg_count_unapp_one: 'However, there is <span id="comment_count_unapp_inner">1</span> additional comment awaiting approval.',
-      msg_count_unapp_plural: 'However, there are <span id="comment_count_unapp_inner">%num_unapp%</span> additional comments awaiting approval.',
-      
-      msg_foe_comment_hidden: 'Post from foe hidden.',
-      btn_display_foe_comment: 'Display post',
-      
-      msg_note_unapp: '(Unapproved)',
-      msg_note_spam: '(Spam)',
-      
-      msg_ip_address: 'IP address:',
-      
-      msg_delete_confirm: 'Do you really want to delete this comment?',
-      
-      err_captcha_wrong: 'The confirmation code you entered was incorrect.',
-      err_spamcheck_failed_rejected: 'Your comment was rejected because it appears to be spam.',
-      err_spamcheck_failed_flagged: 'Your comment was posted, but it appears to be spam and has been flagged as such for a moderator to review.',
-      ajax_err_generic_title: 'Error fetching comment data',
-      
-      postform_title: 'Got something to say?',
-      postform_blurb: 'If you have comments or suggestions on this article, you can shout it out here.',
-      postform_blurb_unapp: 'Before your post will be visible to the public, a moderator will have to approve it.',
-      postform_blurb_captcha: 'Because you are not logged in, you will need to enter a visual confirmation before your comment will be posted.',
-      postform_blurb_link: 'Leave a comment...',
-      postform_field_name: 'Your name/screen name:',
-      postform_field_subject: 'Comment subject:',
-      postform_field_comment: 'Comment:',
-      postform_field_captcha_title: 'Visual confirmation:',
-      postform_field_captcha_blurb: 'Please enter the confirmation code seen in the image on the right into the box. If you cannot read the code, please click on the image to generate a new one. This helps to prevent automated bot posting.',
-      postform_field_captcha_label: 'Confirmation code:',
-      postform_field_captcha_cantread_js: 'If you can\'t read the code, click on the image to generate a new one.',
-      postform_field_captcha_cantread_nojs: 'If you can\'t read the code, please refresh this page to generate a new one.',
-      postform_btn_submit: 'Submit comment',
-      
-      on_friend_list: 'On your friend list',
-      on_foe_list: 'On your foe list'
-    },
-    onpage: {
-      lbl_pagetools: 'Page tools',
-      lbl_page_article: 'article',
-      lbl_page_admin: 'administration page',
-      lbl_page_system: 'system message',
-      lbl_page_file: 'uploaded file',
-      lbl_page_help: 'documentation page',
-      lbl_page_user: 'user page',
-      lbl_page_special: 'special page',
-      lbl_page_template: 'template',
-      lbl_page_project: 'project page',
-      lbl_page_category: 'category',
-      lbl_page_external: 'external page',
-      
-      lbl_sitetools: 'site tools',
-      lbl_changes: 'changes',
-      
-      btn_changes_mine: 'mine',
-      btn_changes_recent: 'recent',
-      btn_changes_history: 'this page',
-      
-      btn_discussion: 'discussion (%num_comments%)',
-      btn_discussion_unapp: '<span title="Approved: %num_app% | Unapproved: %num_unapp% | Spam: %num_spam%">discussion (%num_comments%) [!]</span>',
-      btn_edit: 'edit this page',
-      btn_viewsource: 'view source',
-      btn_history: 'history',
-      btn_moreoptions: 'more options',
-      btn_breadcrumbs_home: 'Home',
-      btn_rename_inline: 'Double-click to rename this page',
-      
-      btn_rename: 'rename',
-      btn_printable: 'view printable version',
-      btn_votedelete: 'vote to delete this page',
-      btn_votedelete_reset: 'reset deletion votes',
-      lbl_wikimode: 'page wiki mode:',
-      btn_wikimode_on: 'on',
-      btn_wikimode_off: 'off',
-      btn_wikimode_global: 'global',
-      lbl_protect: 'protection:',
-      btn_protect_on: 'on',
-      btn_protect_off: 'off',
-      btn_protect_semi: 'semi',
-      btn_protect_change: 'change',
-      btn_clearlogs: 'clear page logs',
-      btn_deletepage: 'delete this page',
-      btn_deletepage_votes: ' (<b>%num_votes%</b> vote%plural%)',
-      lbl_password: 'page password:',
-      btn_password_set: 'set',
-      btn_acl: 'manage page access',
-      btn_admin: 'administrative options',
-      
-      tip_article: 'View the page contents, all of the page contents, and nothing but the page contents (alt-a)',
-      tip_comments: 'View the comments that other users have posted about this page (alt-c)',
-      tip_edit: 'Edit the contents of this page (alt-e)',
-      tip_viewsource: 'View the source code (wiki markup) that this page uses (alt-e)',
-      tip_history: 'View a log of actions taken on this page (alt-h)',
-      tip_rename: 'Change the display name of this page (alt-r)',
-      tip_delvote: 'Vote to have this page deleted (alt-d)',
-      tip_resetvotes: 'Clear the list of votes for deletion against this page (alt-y)',
-      tip_printable: 'View a version of this page that is suitable for printing',
-      tip_protect: 'Change the protection level of this page (alt-p)',
-      tip_flushlogs: 'Remove all edit and action logs for this page from the database. IRREVERSIBLE! (alt-l)',
-      tip_deletepage: 'Delete this page. This is always reversible unless the logs are cleared. (alt-k)',
-      tip_adminoptions: 'Administrative options for this page',
-      tip_moreoptions: 'Additional options for working with this page',
-      tip_password: 'Require a password in order for this page to be viewed',
-      tip_aclmanager: 'Manage who can do what with this page (alt-m)',
-      
-      cat_heading_subcategories: 'Subcategories',
-      cat_msg_no_subcategories: 'No subcategories.',
-      cat_heading_pages: 'Pages',
-      cat_msg_no_pages: 'No pages in this category.',
-      
-      filebox_heading: 'Uploaded file',
-      filebox_msg_not_found: 'There are no files uploaded with this name yet. <a href="%upload_link%">Upload a file...</a>',
-      filebox_lbl_type: 'Type:',
-      filebox_lbl_size: 'Size: %size%',
-      filebox_lbl_uploaded: 'Uploaded:',
-      filebox_msg_virus_warning: 'This file type may contain viruses or other code that could harm your computer. You should exercise caution if you download it.',
-      filebox_btn_download: 'Download this file',
-      filebox_btn_upload_new: 'Upload new version',
-      filebox_heading_history: 'File history',
-      filebox_btn_this_version: 'this ver',
-      filebox_btn_revert: 'restore',
-      filebox_btn_current: 'current',
-      
-      protect_heading: 'Protect page',
-      protect_msg_select_level: 'Select a protection level:',
-      protect_btn_full: 'Full protection',
-      protect_btn_full_hint: 'Prevents everyone except moderators and administrators (by default) from editing the page.',
-      protect_btn_semi: 'Semi-protection',
-      protect_btn_semi_hint: 'Only users who have been members of this site for more than 4 days will be able to edit this page.',
-      protect_btn_none: 'No protection',
-      protect_btn_none_hint: 'Allows everybody to edit this page unless ACLs deny them.',
-      protect_lbl_current: 'current setting',
-      protect_lbl_reason: 'Reason:',
-      protect_lbl_reason_hint: 'Enter a short reason for protecting the page. The message you enter here will be displayed in the page\'s logs.',
-      protect_btn_submit: 'Protect page',
-      protect_err_need_reason: 'Please enter a reason for protecting this page.',
-    },
-    editor: {
-      err_server: 'There was a problem starting the editor',
-      err_access_denied_title: 'Not authorized to view source',
-      err_access_denied_body: 'You are not authorized to edit or view the source of this page.',
-      err_save_title: 'There was a problem saving the page.',
-      err_save_body: 'A few problems were encountered while your page was being saved:',
-      err_obsolete_title: 'Someone else modified this page while you were editing it',
-      err_obsolete_body: 'While you were editing this page, %author% modified this page. The edit took place on %timestamp%. You can <a href="%page_url%" onclick="window.open(this.href); return false;">view the latest version of the page</a>, or click %this.editor_btn_save% again to replace the page with your revision.',
-      err_need_captcha_title: 'Missing confirmation code',
-      err_need_captcha_body: 'Please enter the confirmation code in the box labeled "%this.editor_lbl_field_captcha_code%". %this.editor_msg_captcha_blind%',
-      err_no_text_title: 'No text entered',
-      err_no_text_body: 'Please enter the text that will be in this page.',
-      // Server-side errors
-      err_no_rows: 'Page doesn\'t exist in the database',
-      err_no_permission: 'You do not have permission to edit this page.',
-      err_page_protected: 'This page is protected, and you do not have permission to edit protected pages.',
-      err_captcha_wrong: 'The confirmation code you entered is incorrect.',
-      err_spamcheck_failed: 'Your edit was rejected because it looks like spam.',
-      
-      msg_editor_heading: 'Editing page',
-      msg_saved: 'Your changes to this page have been saved.',
-      msg_revert_confirm: 'Do you really want to revert your changes?',
-      msg_discard_confirm: 'Do you really want to discard your changes?',
-      msg_confirm_ajax: 'Do you really want to do this?\nYour changes to this page have not been saved. If you continue, your changes will be lost.',
-      msg_unload: 'If you do, any changes that you have made to this page will be lost.',
-      msg_revert_confirm_title: 'Revert changes?',
-      msg_revert_confirm_body: 'All of the changes you have made to this page will be lost, and the text will be replaced with the latest published (not draft) version of the page.',
-      msg_cancel_confirm_title: 'Close without saving?',
-      msg_cancel_confirm_body: 'You haven\'t saved your changes to the page. Closing the editor will discard your changes.',
-      msg_diff: 'Below is a summarization of the differences between the current revision of the page (left), and your version (right).',
-      msg_diff_empty: 'There are no differences between the text in the editor and the current revision of the page.',
-      msg_editing_old_revision: 'You are editing an old revision of this page. If you click Save, newer revisions of this page will be undone.',
-      msg_have_draft_title: 'A draft copy of this page is available.',
-      msg_have_draft_body: '%author% saved a draft version of this page on %time%. You can <a href="#use_draft" onclick="ajaxEditorUseDraft(); return false;">use the draft copy</a>, or edit the current published version (below). If you edit the published version, the draft copy will remain available, but will not reflect your changes. It is recommended that you edit the draft version instead of editing the published version. You can also <a href="#delete_draft" onclick="ajaxEditorDeleteDraft(); return false;">discard the draft revision</a>.',
-      msg_convert_confirm_title: 'Convert this page?',
-      msg_convert_confirm_body: 'This will change the format of the page. This may cause some loss of formatting information.',
-      msg_convert_draft_load_title: 'Format changed',
-      msg_convert_draft_load_body: 'The revision that was just loaded is in a different format than the current setting. The editor has been switched to match the format.',
-      btn_graphical: 'Convert to HTML',
-      btn_wikitext: 'Convert to wikitext',
-      lbl_edit_summary: 'Brief summary of your changes:',
-      lbl_edit_summary_explain: 'Please summarize and briefly explain what you changed on the page.',
-      lbl_minor_edit: 'Mark revision as trivial:',
-      lbl_minor_edit_field: 'This is a minor edit',
-      lbl_minor_edit_explain: 'Select this if you\'re only making a minor change to the page',
-      btn_save: 'Save',
-      btn_savedraft: 'Save draft',
-      btn_preview: 'Preview',
-      btn_revert: 'Revert',
-      btn_cancel: 'Cancel',
-      btn_diff: 'Show changes',
-      btn_closeviewer: 'Close viewer',
-      msg_draft_saving: 'Saving...',
-      msg_draft_saved: 'Saved at %time%',
-      btn_revert_confirm: 'Revert changes',
-      btn_cancel_confirm: 'Close editor',
-      btn_cancel_cancel: 'Keep editing',
-      preview_blurb: '<b>Reminder:</b> This is only a preview - your changes to this page have not yet been saved.',
-      msg_save_success_title: 'Changes saved',
-      msg_save_success_body: 'Your changes to this page have been saved. Redirecting...',
-      reversion_edit_summary: 'Undid %undo_count% revision(s) by %current_author% to revision %last_rev_id% by %old_author%',
-      msg_confirm_delete_draft_title: 'Delete the draft revision?',
-      msg_confirm_delete_draft_body: 'This will discard the saved draft version of this page.',
-      btn_delete_draft: 'Delete draft',
-      
-      msg_captcha_pleaseenter: 'Please enter the code shown in the image to the right into the text box. This process helps to ensure that this page is not being edited by an automated bot. If the image to the right is illegible, you can regenerate it by clicking on the image (only works if your browser supports Javascript).',
-      msg_captcha_blind: 'If you are visually impaired or otherwise cannot read the text shown to the right, please contact the site management and they will be able to make your requested edits.',
-      lbl_field_captcha: 'Visual confirmation',
-      lbl_field_captcha_code: 'Code:'
-    },
-    history: {
-      summary_clearlogs: 'Automatic backup created when logs were purged',
-      page_subtitle: 'History of edits and actions',
-      heading_edits: 'Edits:',
-      heading_other: 'Other changes:',
-      no_entries: 'No history entries in this category.',
-      btn_compare: 'Compare selected revisions',
-      col_diff: 'Diff',
-      col_datetime: 'Date/time',
-      col_user: 'User',
-      col_page: 'Page',
-      col_summary: 'Edit summary',
-      col_minor: 'Minor',
-      col_actions: 'Actions',
-      col_action_taken: 'Action taken',
-      col_extra: 'Extra info',
-      extra_reason: 'Reason:',
-      extra_oldtitle: 'Old title:',
-      extra_protection_reversion: '(Reversion of previous protection)',
-      extra_upload_reversion: '(Restoration of previous upload)',
-      extra_numvotes: 'Number of votes:',
-      tip_rdns: 'Click cell background for reverse DNS info',
-      action_view: 'View',
-      action_contrib: 'User contribs',
-      action_restore: 'Restore',
-      action_revert: 'Revert action',
-      log_protect: 'Protected page',
-      log_unprotect: 'Unprotected page',
-      log_semiprotect: 'Semi-protected page',
-      log_rename: 'Renamed page',
-      log_create: 'Created page',
-      log_delete: 'Deleted page',
-      log_uploadnew: 'Uploaded new file version',
-      log_votereset: 'Reset deletion votes',
-      lbl_comparingrevisions: 'Comparing revisions:',
-      summary_none_given: 'No edit summary provided.',
-      err_wrong_password: 'Please enter the password for this page before viewing its history.'
-    },
-    catedit: {
-      title: 'Select which categories this page should be included in.',
-      no_categories: 'There are no categories on this site yet.',
-      catbox_lbl_categories: 'Categories:',
-      catbox_lbl_uncategorized: '(Uncategorized)',
-      catbox_link_edit: 'edit categorization',
-      catbox_link_showcategorization: 'show page categorization'
-    },
-    tags: {
-      catbox_link: 'show page tags',
-      lbl_page_tags: 'Page tags:',
-      lbl_no_tags: 'No tags on this page',
-      btn_add_tag: '(add a tag)',
-      lbl_add_tag: 'Add a tag:',
-      btn_add: '+ Add'
-    },
-    delvote: {
-      lbl_votes_one: 'There is one user that thinks this page should be deleted.',
-      lbl_votes_plural: 'There are %num_users% users that think this page should be deleted.',
-      lbl_users_that_voted: 'Users that voted:',
-      btn_deletepage: 'Delete page',
-      btn_resetvotes: 'Reset votes'
-    },
-    ajax: {
-      // Client-side messages
-      protect_prompt_reason: 'Reason for (un)protecting:',
-      rename_prompt: 'What title should this page be renamed to?\n%this.ajax_rename_notice%',
-      rename_prompt_short: 'Enter a new name for this page',
-      rename_notice: 'This won\'t change the URL to this page. To change the URL to this page, use Page Manager in the administration panel.',
-      delete_header: 'Delete this page?',
-      delete_lbl_reason: 'Reason:',
-      delete_prompt_reason: 'Please enter your reason for deleting this page. This will be visible in the page history as well as on the deleted page.',
-      delete_msg_confirm: 'Deleting pages is reversible. Users with administrative access can restore the page, but not any comments, categorization data, tags, or files (if the page contains an uploaded file). If Wiki Mode is in effect globally, everyone can create this page again and see past revisions.',
-      delete_lbl_confirm: 'Yes, I\'m sure',
-      delete_btn_delete: 'Delete page',
-      delvote_confirm_title: 'Vote to delete this page?',
-      delvote_confirm_body: 'Administrators and moderators will see a notice on this page showing who voted.',
-      delvote_btn_submit: 'Vote',
-      delvote_reset_confirm_title: 'Reset votes for deletion?',
-      delvote_reset_confirm_body: 'This will reset the number of votes against this page to zero.',
-      delvote_reset_btn_submit: 'Reset',
-      clearlogs_confirm_title: 'Clear this page\'s logs?',
-      clearlogs_confirm_body: 'This will erase this page\'s entire history and is an irreversible operation.',
-      clearlogs_btn_submit: 'Clear Logs',
-      killphp_confirm: 'Are you really sure you want to do this? Some pages might not function if this emergency-only feature is activated.',
-      killphp_success: 'Embedded PHP in pages has been disabled.',
-      lbl_moreoptions_nojs: 'More options for this page',
-      msg_loading_component: 'Loading %component%...',
-      
-      thmsel_lbl_choosetheme: 'Choose a theme',
-      thmsel_lbl_choosestyle: 'Choose a style variation',
-      thmsel_msg_success: 'Theme changed',
-      thmsel_btn_reload: 'Reload page',
-      thmsel_btn_close: 'Close selector and reload later',
-      thmsel_btn_close_hint: 'You\'ll see your new theme on the next page reload.',
-      
-      badjson_title: 'The site encountered an error while processing your request.',
-      badjson_body: 'We unexpectedly received the following response from the server. The response should have been in the JSON serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers for this problem:',
-      badjson_tip1: 'The server sent back a bad HTTP response code and thus sent an error page instead of running Enano. This indicates a possible problem with your server, and is not likely to be a bug with Enano.',
-      badjson_tip2: 'The server sent back the expected JSON response, but also injected some code into the response that should not be there. Typically this consists of advertisement code. In this case, the administrator of this site will have to contact their web host to have advertisements disabled.',
-      badjson_tip3: 'It\'s possible that Enano triggered a PHP error or warning. In this case, you may be looking at a bug in Enano.',
-      badjson_osc: 'This is KNOWN to be the case with the OpenSourceCMS.com demo version of Enano.',
-      badjson_msg_response: 'The response received from the server is as follows:',
-      badjson_msg_viewashtml: 'You may also choose to view the response as HTML.',
-      badjson_btn_viewashtml: 'View as HTML',
-      badjson_html_confirm_title: 'View the response as HTML?',
-      badjson_html_confirm_body: 'If the server\'s response was modified by an attacker to include malicious code, viewing the response as HTML might allow that malicious code to run. Only continue if you have inspected the response text and verified that it is safe.\n\nThe information in JSON responses is typically only meant to be useful to developers and the Enano applets.',
-      badjson_btn_close: 'Close',
-      
-      // Server-side responses
-      rename_too_short: 'The name you entered is too short. Please enter a longer name for this page.',
-      rename_success_title: 'Page renamed',
-      rename_success_body: 'This page has been renamed to "%page_name_new%". You are encouraged to leave a comment explaining your action.<br /><br />Please note that you might not see this change take effect until you reload the page.',
-      clearlogs_success: 'The logs for this page have been cleared. A backup of this page has been added to the logs table so that this page can be restored in case of vandalism or spam later.',
-      delete_need_reason: 'Invalid reason for deletion passed. Please enter a reason for deleting this page.',
-      delete_success: 'This page has been deleted. Note that there is still a log of edits and actions in the database, and anyone with admin rights can raise this page from the dead unless the log is cleared. If the deleted file is an image, there may still be cached thumbnails of it in the cache/ directory, which is inaccessible to users.',
-      delvote_success: 'Your vote to have this page deleted has been cast.\nYou are encouraged to leave a comment explaining the reason for your vote.',
-      delvote_already_voted: 'It appears that you have already voted to have this page deleted.',
-      delvote_reset_success: 'The number of votes for having this page deleted has been reset to zero.',
-      password_success: 'The password for this page has been set.',
-      password_disable_success: 'The password for this page has been disabled.'
-    },
-    sidebar: {
-      title_navigation: 'Navigation',
-      title_tools: 'Tools',
-      title_search: 'Search',
-      title_links: 'Links',
-      title_about: 'About',
-      
-      btn_home: 'Home',
-      btn_createpage: 'Create a page',
-      btn_uploadfile: 'Upload file',
-      btn_specialpages: 'Special pages',
-      btn_administration: 'Administration',
-      btn_editsidebar: 'Edit the sidebar',
-      btn_search_go: 'Go',
-      btn_enanopowered_admin_tip: 'You may disable this button in the admin panel under General Configuration.',
-      
-      btn_userpage: 'User page',
-      btn_mycontribs: 'My contributions',
-      btn_preferences: 'Preferences',
-      btn_preferences_short: 'User CP',
-      btn_privatemessages: 'Private messages',
-      btn_groupcp: 'Group control panel',
-      btn_register: 'Create an account',
-      btn_login: 'Log in',
-      btn_logout: 'Log out',
-      btn_changestyle: 'Change theme',
-      btn_recent_changes: 'Recent edits'
-    },
-    perm: {
-      read: 'Read page(s)',
-      post_comments: 'Post comments',
-      edit_comments: 'Edit own comments',
-      edit_page: 'Edit page',
-      view_source: 'View source',
-      edit_wysiwyg: 'Use graphical editor (<a href="http://docs.enanocms.org/Help:4.1#toc4">WEAK</a>)',
-      mod_comments: 'Moderate comments',
-      history_view: 'View history/diffs',
-      history_rollback: 'Rollback history',
-      history_rollback_extra: 'Undelete page(s)',
-      protect: 'Protect page(s)',
-      rename: 'Rename page(s)',
-      clear_logs: 'Clear page logs (dangerous)',
-      vote_delete: 'Vote to delete',
-      vote_reset: 'Reset delete votes',
-      delete_page: 'Delete page(s)',
-      tag_create: 'Tag page(s)',
-      tag_delete_own: 'Remove own page tags',
-      tag_delete_other: 'Remove others\' page tags',
-      set_wiki_mode: 'Set per-page wiki mode',
-      password_set: 'Set password',
-      password_reset: 'Disable/reset password',
-      mod_misc: 'Super moderator (generate SQL backtraces, view IP addresses, and send large numbers of private messages)',
-      edit_cat: 'Edit categorization',
-      even_when_protected: 'Allow editing, renaming, and categorization even when protected',
-      upload_files: 'Upload files',
-      upload_new_version: 'Upload new versions of files',
-      create_page: 'Create pages',
-      html_in_pages: 'Embed unrestricted HTML in pages',
-      php_in_pages: 'Embed PHP code in pages',
-      custom_user_title: 'Use a custom user title',
-      edit_acl: 'Edit access control lists'
-    },
-    plugin: {
-      author_enano: 'Enano CMS Project',
-      specialadmin_title: 'Administration panel',
-      specialadmin_desc: 'Provides the page Special:Administration, which is the AJAX frontend to the various Admin pagelets. This plugin cannot be disabled.',
-      privatemessages_title: 'Private Message frontend',
-      privatemessages_desc: 'Provides the page Special:PrivateMessages, which is used to manage private message functions. Also handles buddy lists.',
-      specialcss_title: 'CSS Backend',
-      specialcss_desc: 'Provides the page Special:CSS, which is used in template files to reference the style sheet. Disabling or deleting this plugin will result in site instability.',
-      specialgroups_title: 'Group control panel',
-      specialgroups_desc: 'Provides group moderators and site administrators with the ability to control who is part of their groups.',
-      specialpagefuncs_title: 'Special page-related pages',
-      specialpagefuncs_desc: 'Provides the page Special:CreatePage, which can be used to create new pages. Also adds the About Enano and GNU General Public License pages.',
-      specialsearch_title: 'Search UI/frontend',
-      specialsearch_desc: 'Provides the page Special:Search, which is a frontend to the Enano search engine.',
-      specialupdownload_title: 'Upload/download frontend',
-      specialupdownload_desc: 'Provides the pages Special:UploadFile and Special:DownloadFile. UploadFile is used to upload files to the site, and DownloadFile fetches the file from the database, creates thumbnails if necessary, and sends the file to the user.',
-      specialuserfuncs_title: 'Special user/login-related pages',
-      specialuserfuncs_desc: 'Provides the pages Special:Login, Special:Logout, Special:Register, and Special:Preferences.',
-      specialuserprefs_title: 'User control panel',
-      specialuserprefs_desc: 'Provides the page Special:Preferences.',
-      speciallog_title: 'Log displayer',
-      speciallog_desc: 'Provides the page Special:Log, which is used to view modifications to pages on the site.',
-      tagcloud_title: 'Tag cloud sidebar block',
-      tagcloud_desc: 'Brings back the "tag cloud" sidebar block that was removed in Enano 1.1.6.'
-    },
-    paginate: {
-      lbl_page: 'Page:',
-      btn_first: 'First',
-      btn_last: 'Last',
-      btn_prev: 'Prev',
-      btn_next: 'Next',
-      lbl_goto_page: 'Go to page:',
-      err_bad_page_title: 'Invalid entry',
-      err_bad_page_body: 'Please enter a page number between 1 and %max%.'
-    },
-    upload: {
-      err_disabled_site: 'File uploads are disabled this website.',
-      err_disabled_acl: 'File uploads are disabled for your user account or group.',
-      
-      err_title: 'Upload failed',
-      err_cant_get_file_meta: 'The server could not retrieve the array $_FILES[\'data\'].',
-      err_too_big_or_small: 'The file you uploaded is either too large or 0 bytes in length.',
-      err_banned_ext: 'The file type ".%ext%" is not allowed.',
-      err_banned_chars: 'The filename contains invalid characters.',
-      err_already_exists: 'The file already exists. You can <a href="%upload_link%">upload a new version of this file</a>.',
-      err_replace_protected: 'Either the file does not exist (and therefore cannot be updated) or the file is protected.',
-      err_move_failed: 'Could not move uploaded file to the new location.',
-      err_replace_denied: 'Uploading new versions of files has been disabled for your user account or group.',
-      
-      success_title: 'Upload complete',
-      success_body: 'Your file has been uploaded successfully. View the <a href="%file_link%">file\'s page</a>.',
-      
-      intro: 'Using this form you can upload a file to the %config.site_name% site.',
-      max_filesize: 'The maximum file size is %config.max_file_size% %this.etc_unit_bytes% (%size%).',
-      field_file: 'File:',
-      field_renameto: 'Rename to:',
-      field_comments: 'Comments:<br />(can be wiki-formatted)',
-      field_reason: 'Reason for uploading the new version:',
-      btn_upload: 'Upload file',
-      
-      err_not_found_title: 'File not found',
-      err_not_found_body: 'The file "%filename%" cannot be found.'
-    },
-    tz: {
-      // Thanks to phpBB for this timezone data.
-      // Do not add or remove from this list - contact the core team to have changes made to this list.
-      hrs_n12: 'UTC - 12 hours',
-      hrs_n11: 'UTC - 11 hours',
-      hrs_n10: 'UTC - 10 hours',
-      hrs_n9p5: 'UTC - 9:30 hours',
-      hrs_n9: 'UTC - 9 hours',
-      hrs_n8: 'UTC - 8 hours',
-      hrs_n7: 'UTC - 7 hours',
-      hrs_n6: 'UTC - 6 hours',
-      hrs_n5: 'UTC - 5 hours',
-      hrs_n4: 'UTC - 4 hours',
-      hrs_n3p5: 'UTC - 3:30 hours',
-      hrs_n3: 'UTC - 3 hours',
-      hrs_n2: 'UTC - 2 hours',
-      hrs_n1: 'UTC - 1 hour',
-      hrs_0: 'UTC',
-      hrs_1: 'UTC + 1 hour',
-      hrs_2: 'UTC + 2 hours',
-      hrs_3: 'UTC + 3 hours',
-      hrs_3p5: 'UTC + 3:30 hours',
-      hrs_4: 'UTC + 4 hours',
-      hrs_4p5: 'UTC + 4:30 hours',
-      hrs_5: 'UTC + 5 hours',
-      hrs_5p5: 'UTC + 5:30 hours',
-      hrs_5p75: 'UTC + 5:45 hours',
-      hrs_6: 'UTC + 6 hours',
-      hrs_6p5: 'UTC + 6:30 hours',
-      hrs_7: 'UTC + 7 hours',
-      hrs_8: 'UTC + 8 hours',
-      hrs_8p75: 'UTC + 8:45 hours',
-      hrs_9: 'UTC + 9 hours',
-      hrs_9p5: 'UTC + 9:30 hours',
-      hrs_10: 'UTC + 10 hours',
-      hrs_10p5: 'UTC + 10:30 hours',
-      hrs_11: 'UTC + 11 hours',
-      hrs_11p5: 'UTC + 11:30 hours',
-      hrs_12: 'UTC + 12 hours',
-      hrs_12p75: 'UTC + 12:45 hours',
-      hrs_13: 'UTC + 13 hours',
-      hrs_14: 'UTC + 14 hours',
-      title_n12: '[UTC - 12] Baker Island Time',
-      title_n11: '[UTC - 11] Niue Time, Samoa Standard Time',
-      title_n10: '[UTC - 10] Hawaii-Aleutian Standard Time, Cook Island Time',
-      title_n9p5: '[UTC - 9:30] Marquesas Islands Time',
-      title_n9: '[UTC - 9] Alaska Standard Time, Gambier Island Time',
-      title_n8: '[UTC - 8] Pacific Standard Time',
-      title_n7: '[UTC - 7] Mountain Standard Time',
-      title_n6: '[UTC - 6] Central Standard Time',
-      title_n5: '[UTC - 5] Eastern Standard Time',
-      title_n4: '[UTC - 4] Atlantic Standard Time',
-      title_n3p5: '[UTC - 3:30] Newfoundland Standard Time',
-      title_n3: '[UTC - 3] Amazon Standard Time, Central Greenland Time',
-      title_n2: '[UTC - 2] Fernando de Noronha Time, South Georgia &amp; the South Sandwich Islands Time',
-      title_n1: '[UTC - 1] Azores Standard Time, Cape Verde Time, Eastern Greenland Time',
-      title_0: '[UTC] Western European Time, Greenwich Mean Time',
-      title_1: '[UTC + 1] Central European Time, West African Time',
-      title_2: '[UTC + 2] Eastern European Time, Central African Time',
-      title_3: '[UTC + 3] Moscow Standard Time, Eastern African Time',
-      title_3p5: '[UTC + 3:30] Iran Standard Time',
-      title_4: '[UTC + 4] Gulf Standard Time, Samara Standard Time',
-      title_4p5: '[UTC + 4:30] Afghanistan Time',
-      title_5: '[UTC + 5] Pakistan Standard Time, Yekaterinburg Standard Time',
-      title_5p5: '[UTC + 5:30] Indian Standard Time, Sri Lanka Time',
-      title_5p75: '[UTC + 5:45] Nepal Time',
-      title_6: '[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirsk Standard Time',
-      title_6p5: '[UTC + 6:30] Cocos Islands Time, Myanmar Time',
-      title_7: '[UTC + 7] Indochina Time, Krasnoyarsk Standard Time',
-      title_8: '[UTC + 8] Chinese Standard Time, Australian Western Standard Time, Irkutsk Standard Time',
-      title_8p75: '[UTC + 8:45] Southeastern Western Australia Standard Time',
-      title_9: '[UTC + 9] Japan Standard Time, Korea Standard Time, Chita Standard Time',
-      title_9p5: '[UTC + 9:30] Australian Central Standard Time',
-      title_10: '[UTC + 10] Australian Eastern Standard Time, Vladivostok Standard Time',
-      title_10p5: '[UTC + 10:30] Lord Howe Standard Time',
-      title_11: '[UTC + 11] Solomon Island Time, Magadan Standard Time',
-      title_11p5: '[UTC + 11:30] Norfolk Island Time',
-      title_12: '[UTC + 12] New Zealand Time, Fiji Time, Kamchatka Standard Time',
-      title_12p75: '[UTC + 12:45] Chatham Islands Time',
-      title_13: '[UTC + 13] Tonga Time, Phoenix Islands Time',
-      title_14: '[UTC + 14] Line Island Time',
-      // This is a JSON string that lists all the timezones that are defined here.
-      list: '{"n12":-12,"n11":-11,"n10":-10,"n9p5":-9.5,"n9":-9,"n8":-8,"n7":-7,"n6":-6,"n5":-5,"n4":-4,"n3p5":-3.5,"n3":-3,"n2":-2,"n1":-1,"0":0,"1":1,"2":2,"3":3,"3p5":3.5,"4":4,"4p5":4.5,"5":5,"5p5":5.5,"5p75":5.75,"6":6,"6p5":6.5,"7":7,"8":8,"8p75":8.75,"9":9,"9p5":9.5,"10":10,"10p5":10.5,"11":11,"11p5":11.5,"12":12,"12p75":12.75,"13":13,"14":14}',
-      
-      // DST profiles
-      dst_off: 'My region doesn\'t observe DST',
-      dst_usa: 'United States: second Sunday of March to first Sunday of November',
-      dst_europe: 'Europe: last Sunday of March to last Sunday of October',
-      dst_australia: 'Australia (except Tasmania): last Sunday of October to last Sunday of March',
-      dst_tasmania: 'Tasmania: first Sunday of October to last Sunday of March'
-    },
-    etc: {
-      redirect_title: 'Redirecting...',
-      redirect_body: 'Please wait while you are redirected.',
-      redirect_timeout: 'If you are not redirected within %timeout% seconds, please <a href="%redirect_url%">click here</a>.',
-      // Generic "Save Changes" button
-      save_changes: 'Save changes',
-      // Generic "Cancel changes" button
-      cancel_changes: 'Cancel changes',
-      // Generic wizard buttons
-      wizard_next: 'Next >',
-      wizard_back: '< Back',
-      wizard_previous: '< Previous',
-      // Generic link to main page
-      btn_main_page: 'Main page',
-      // Generic switchable editor buttons
-      tinymce_btn_text: 'text editor',
-      tinymce_btn_graphical: 'graphical editor',
-      // Generic "Notice:" label
-      lbl_notice: 'Notice:',
-      // Generic "Access denied"
-      access_denied: 'Access to the specified file, resource, or action is denied.',
-      access_denied_short: 'Access denied',
-      access_denied_need_reauth: 'You need to re-authenticate before you can do that.',
-      return_to_page: 'Return to the page',
-      invalid_request_short: 'Invalid request',
-      // Message box buttons
-      ok: 'OK',
-      cancel: 'Cancel',
-      close: 'Close',
-      yes: 'Yes',
-      no: 'No',
-      go: 'Go',
-      unit_bytes: 'bytes',
-      unit_kilobytes: 'kilobytes',
-      unit_megabytes: 'megabytes',
-      unit_gigabytes: 'gigabytes',
-      unit_terabytes: 'terabytes',
-      unit_kilobytes_short: 'KB',
-      unit_megabytes_short: 'MB',
-      unit_gigabytes_short: 'GB',
-      unit_terabytes_short: 'TB',
-      unit_pixels: 'pixels',
-      unit_pixels_short: 'px',
-      unit_second: 'second',
-      unit_seconds: 'seconds',
-      unit_day: 'day',
-      unit_days: 'days',
-      unit_week: 'week',
-      unit_weeks: 'weeks',
-      unit_month: 'month',
-      unit_months: 'months',
-      unit_year: 'year',
-      unit_years: 'years',
-      unit_minute: 'minute',
-      unit_minutes: 'minutes',
-      unit_minute_short: 'min',
-      unit_minutes_short: 'mins'
-    }
-  }
+	categories: [
+		'meta', 'page', 'comment', 'onpage', 'etc', 'editor', 'history', 'catedit', 'tags', 'delvote', 'ajax', 'sidebar', 'perm', 'plugin', 'paginate', 'upload', 'tz'
+	],
+	strings: {
+		meta: {
+			meta: 'Category names and basic metadata',
+			page: 'Page creation and control',
+			comment: 'Comment display',
+			onpage: 'On-page buttons and controls',
+			etc: 'Miscellaneous strings',
+			editor: 'Page editor interface',
+			history: 'Page history and log viewer',
+			catedit: 'Categorization box and editor',
+			tags: 'Page tagging interface',
+			delvote: 'Page deletion vote interface',
+			ajax: 'On-page AJAX applets',
+			sidebar: 'Default sidebar blocks and buttons',
+			perm: 'Page actions (for ACLs)',
+			plugin: 'Plugin names and descriptions',
+			paginate: 'Pagination widget',
+			upload: 'File upload interface',
+			tz: 'Time zones',
+			plural: 's',
+			enano_about_th: 'About the Enano Content Management System',
+			enano_about_poweredby: '<p>This website is powered by <a href="http://enanocms.org/">Enano</a>, the lightweight and open source CMS that everyone can use. Enano is copyright &copy; 2006-%year% Dan Fuhry. For legal information, along with a list of libraries that Enano uses, please see <a href="http://enanocms.org/Legal_information">Legal Information</a>.</p><p>The developers and maintainers of Enano strongly believe that software should not only be free to use, but free to be modified, distributed, and used to create derivative works. To help achieve this goal, we use licensing terms that require you to pass on the freedoms we give you when you share Enano. For more information about Free Software, check out the <a href="http://en.wikipedia.org/wiki/Free_Software" onclick="window.open(this.href); return false;">Wikipedia page</a> or the <a href="http://www.fsf.org/" onclick="window.open(this.href); return false;">Free Software Foundation\'s</a> homepage.</p>',
+			enano_about_gpl: '<p>This program is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</p><p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.</p><p>You should have received <a href="%gpl_link%">a copy of the GNU General Public License</a> along with this program; if not, write to:</p><p style="margin-left 2em;">Free Software Foundation, Inc.,<br />51 Franklin Street, Fifth Floor<br />Boston, MA 02110-1301, USA</p><p>Alternatively, you can <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">read it online</a>.</p>',
+			enano_about_lbl_enanoversion: '<a href="http://enanocms.org">Enano</a> version:',
+			enano_about_lbl_webserver: 'Web server:',
+			enano_about_lbl_serverplatform: 'Server platform:',
+			enano_about_lbl_phpversion: '<a href="http://www.php.net/">PHP</a> version:',
+			enano_about_lbl_mysqlversion: '<a href="http://www.mysql.com/">MySQL</a> version:',
+			enano_about_lbl_pgsqlversion: '<a href="http://www.postgresql.org/">PostgreSQL</a> version:'
+		},
+		page: {
+			sitedisabled_admin_msg_title: 'The site is currently disabled and thus is only accessible to administrators.',
+			sitedisabled_admin_msg_body: 'You can re-enable the site through the <a href="%admin_link%">administration panel</a>.',
+			
+			heading_sql_list: 'Query list as requested on URI',
+			
+			msg_stats_gentime_long: 'Generated in %time%sec',
+			msg_stats_gentime_short: 'Time: %time%s',
+			msg_stats_sql: '%nq% SQL',
+			
+			w3c_valid_html32: 'Valid HTML 3.2',
+			w3c_valid_html40: 'Valid HTML 4.0',
+			w3c_valid_html401: 'Valid HTML 4.01',
+			w3c_valid_html50: 'Valid HTML 5.0',
+			w3c_valid_xhtml10: 'Valid XHTML 1.0',
+			w3c_valid_xhtml11: 'Valid XHTML 1.1',
+			w3c_valid_css: 'Valid CSS',
+			enano_powered: 'Powered by <a href="%about_uri%">Enano</a>',
+			enano_powered_long: 'Website engine powered by <a href="%about_uri%">Enano</a>',
+			
+			protect_lbl_success_title: 'Page protected',
+			protect_lbl_success_body: 'The protection setting has been applied. <a href="%page_link%">Return to the page</a>.',
+			
+			rename_err_need_name: 'Error: you must enter a new name for this page.',
+			rename_lbl: 'Please enter a new name for this page:',
+			rename_btn_submit: 'Rename page',
+			rename_success_title: 'Page renamed',
+			
+			flushlogs_warning_stern: '<h3>You are about to <span style="color: red;">destroy</span> all logged edits and actions on this page.</h3><p>Unlike deleting or editing this page, this action is <u>not reversible</u>! You should only do this if you are desparate for database space.</p><p>Do you really want to continue?</p>',
+			flushlogs_btn_submit: 'Flush logs',
+			flushlogs_backup_summary: 'Automatic backup created when logs were purged',
+			
+			delvote_warning_stern: '<h3>Your vote counts.</h3><p>If you think that this page is not relavent to the content on this site, or if it looks like this page was only created in an attempt to spam the site, you can request that this page be deleted by an administrator.</p><p>After you vote, you should leave a comment explaining the reason for your vote, especially if you are the first person to vote against this page.</p>',
+			
+			delvote_count_zero: 'So far, no one has voted for the deletion of this page.',
+			delvote_count_one: 'So far, one person has voted to delete this page.',
+			delvote_count_plural: 'So far, %delvotes% people have voted to delete this page.',
+			delvote_btn_submit: 'Vote to delete this page',
+			delvote_reset_btn_submit: 'Reset votes',
+			
+			delete_warning_stern: '<h3>Confirm page deletion</h3><p>The contents of the page will not be deleted; you will still be able to recover the page using the History feature.</p><p>Please be aware, however, that the following information is not recoverable:</p><ul><li>The list of categories this page belongs to</li><li>Every file revision associated with this page (if this page is in the File namespace)</li><li>Comments on this page</li></ul><p>To delete this page, enter a reason below, and then click Delete this Page.</p>',
+			delete_err_need_reason: 'Please enter a reason for deleting this page.',
+			delete_btn_submit: 'Delete this page',
+			delete_lbl_reason: 'Reason for deleting:',
+			
+			wikimode_success_redirect: 'Wiki mode for this page has been set. Redirecting you to the page...',
+			wikimode_level_on: 'Wiki features will be enabled.',
+			wikimode_level_off: 'Wiki features will be disabled.',
+			wikimode_level_global: 'Wiki features will be synchronized to the global setting.',
+			wikimode_heading: 'You are changing wiki mode for this page.',
+			wikimode_warning: 'If you want to continue, please click the button below.',
+			wikimode_blurb_disable: 'Because this will disable the wiki behavior on this page, several features, most notably the ability for users to vote to have this page deleted, will be disabled as they are not relevant to non-wiki pages. In addition, users will not be able to edit this page unless an ACL rule specifically permits them.',
+			wikimode_blurb_enable: 'Because this will enable the wiki behavior on this page, users will gain the ability to freely edit this page unless an ACL rule specifically denies them. If your site is public and gets good traffic, you should be aware of the possiblity of vandalism, and you need to be ready to revert malicious edits to this page.',
+			wikimode_btn_submit: 'Set wiki mode',
+			
+			detag_err_page_exists: 'The detag action is only valid for pages that have been deleted in the past.',
+			detag_success_title: 'Page detagged',
+			detag_success_body: 'All stale tags have been removed from this page.',
+			
+			err_custompage_function_missing_title: 'Page backend not found',
+			err_custompage_function_missing_body: 'The administration page you are looking for was properly registered using the page API, but the backend function (<tt>%function_name%</tt>) was not found. If this is a plugin page, then this is almost certainly a bug with the plugin.',
+			err_redirects_exceeded: 'The maximum number of internal redirects has been exceeded.',
+			err_redirect_to_nonexistent: 'This page redirects to another page that doesn\'t exist.',
+			err_redirect_infinite_loop: 'This page infinitely redirects with another page (or another series of pages), and the infinite redirect was trapped.',
+			err_redirect_to_special: 'This page redirects to a Special or Administration page, which is not allowed.',
+			err_access_denied_title: 'You don\'t have permission to view this page.',
+			err_access_denied_body: '<p>Your user account doesn\'t have the necessary permission to view this page. There are a number of possible reasons for this:</p>
+ 															<ul>
+ 																<li>You aren\'t logged in. Some pages are restricted to logged-in users.</li>
+ 																<li>The page you\'re trying to view is protected so that only members of a specific usergroup are allowed to read it.</li>
+ 															</ul>
+ 															<p>If you would like to inquire further about this message, you may contact the %site_administration%.</p>',
+			err_access_denied_siteadmin: 'site administrator',
+			err_seeking_living_among_dead: 'You are trying to un-delete a page that has since been restored.\n\n"But the men said to them, \'Why do you look for the living among the dead?\'" (Luke 24:5b/NIV)',
+			err_access_denied: 'Access to that action is denied.',
+			err_access_denied_need_reauth: '%this.etc_access_denied_need_reauth%',
+			err_invalid_parameter: 'An invalid value (parameter) was sent to this action.',
+			err_rb_action_not_supported: 'Rolling back actions of type "%action%" isn\'t supported.',
+			err_rb_file_rename_failed: 'Could not rename the file to its new name (1.1.x format)',
+			err_protection_already_there: 'The protection level you selected is already in effect for this page.',
+			err_page_not_exist: 'The page you tried to perform this action on does not exist.',
+			
+			msg_this_is_a_redirector: '<b>This page is a <i>redirector</i>.</b><br />
+										This means that this page will not show its own content by default. Instead it will display the contents of the page it redirects to.<br /><br />
+										To create a redirect page, make the <i>first characters</i> in the page content <tt>#redirect [[Page_ID]]</tt>. For more information, see the
+										Enano <a href="http://enanocms.org/Help:Wiki_formatting" onclick="window.open(this.href); return false;">Wiki formatting guide</a>.<br /><br />
+										This page redirects to %redirect_target%.',
+			msg_redirected_from: '(Redirected from %from%)',
+			msg_redirected_from_to: '(Redirected from %from% to %to%)',
+			msg_rb_success_rename: 'The page has been restored the name it had on %dateline%.',
+			// next 3 are mostly identical
+			msg_rb_success_prot: 'The page\'s protection has been undone and replaced with the previous level it had as of %dateline%.',
+			msg_rb_success_unprot: 'The page\'s unprotection has been undone and replaced with the previous level it had as of %dateline%.',
+			msg_rb_success_semiprot: 'The page\'s semi-protection has been undone and replaced with the previous level it had as of %dateline%.',
+			
+			msg_rb_success_delete: 'The deletion of this page, which occurred on %dateline%, has been undone. This page has been restored, but comments and categorization data may have been lost.',
+			msg_rb_success_reupload: 'The file has been restored to the version uploaded on %dateline%.',
+			
+			msg_rb_success_votereset: 'The votes for deletion of this page as of %dateline% have been restored and merged with any existing votes.',
+			
+			msg_passrequired_title: 'Password required',
+			msg_passrequired: 'Access to this page requires a password. Please enter the password for this page below:',
+			msg_pass_wrong: 'The password you entered for this page was incorrect. Please enter the password for this page below:',
+			lbl_password: 'Password:',
+			btn_password_submit: 'Submit',
+			
+			msg_404_title: 'There is no page with this title yet.',
+			msg_404_body: 'You have requested a page that doesn\'t exist yet.',
+			msg_404_title_userpage: 'No content',
+			msg_404_body_userpage: 'This user has not yet created any custom user page content.',
+			msg_404_create: 'You can <a %create_flags%>create this page</a>, or return to the <a href="%mainpage_link%">homepage</a>.',
+			msg_404_gohome: 'Return to the <a href="%mainpage_link%">homepage</a>.',
+			msg_404_was_deleted: '<b>This page was deleted on %delete_time%.</b> The stated reason was:</p><blockquote>%delete_reason%</blockquote><p>You can probably <a %rollback_flags%>roll back</a> the deletion.',
+			msg_404_admin_opts: 'Additional admin options: <a href="%detag_link%" title="Remove any tags on this page">detag page</a>',
+			msg_404_http_response: 'HTTP Error: 404 Not Found',
+			
+			msg_archived_title: 'Notice:',
+			msg_archived_body: 'The page you are viewing was archived on %archive_date% at %archive_time%.<br /><a href="%current_link%" onclick="ajaxReset(); return false;">View current version</a>  |  <a href="%restore_link%" onclick="%restore_onclick%">Restore this version</a>',
+			
+			msg_special_404_title: 'Can\'t load special page',
+			msg_special_404_body: 'The special page you requested could not be found. This may be due to a plugin failing to load. A list of all special pages on this website can be viewed <a href="%sp_link%">here</a>. You will be redirected to the main page in 15 seconds.',
+			msg_admin_404_title: 'Administration page not found',
+			msg_admin_404_body: '<p>You\'ve requested an administration page, but the function <tt>%func_name%</tt> doesn\'t exist, so the page can\'t be loaded.</p>
+ 														<h3>Plugin developer?</h3>
+															<p>Create a function called <tt>%func_name%</tt> - it should be loaded when you refresh this page.</p>
+ 														<h3>Otherwise...</h3>
+															<p>If you\'re trying to use a plugin\'s administration page, contact the developer of the plugin. If you\'re trying to use a function that is built into Enano (not added by a plugin), then please contact the Enano development team.</p>',
+			
+			msg_general_error: 'General error in page fetcher',
+										
+			autosuggest_heading: 'Page name matches',
+			autosuggest_col_name: 'Page title',
+			autosuggest_col_page_id: 'Page ID'
+		},
+		comment: {
+			lbl_subject: 'Subject',
+			lbl_mod_options: 'Moderator options:',
+			heading: 'Article comments',
+			btn_send_privmsg: 'Send PM',
+			btn_add_buddy: 'Add buddy',
+			btn_edit: 'edit',
+			btn_delete: 'delete',
+			btn_mod_approve: 'Approve',
+			btn_mod_unapprove: 'Unapprove',
+			btn_mod_delete: 'Delete',
+			btn_mod_ip_logged: 'View IP',
+			btn_mod_ip_missing: 'IP not logged',
+			btn_mod_ip_notimplemented: 'Use AJAX interface to view IPs',
+			btn_save: 'save',
+			
+			msg_comment_posted: 'Your comment has been posted. If it does not appear right away, it is probably awaiting approval.',
+			
+			msg_count_zero: 'There are <span id="comment_count_inner">no</span> comments on this %page_type%.',
+			msg_count_one: 'There is <span id="comment_count_inner">1</span> comment on this %page_type%.',
+			msg_count_plural: 'There are <span id="comment_count_inner">%num_comments%</span> comments on this %page_type%.',
+			
+			msg_count_unapp_mod: '<span id="comment_count_unapp_inner">%num_unapp%</span> of those are unapproved.',
+			msg_count_unapp_one: 'However, there is <span id="comment_count_unapp_inner">1</span> additional comment awaiting approval.',
+			msg_count_unapp_plural: 'However, there are <span id="comment_count_unapp_inner">%num_unapp%</span> additional comments awaiting approval.',
+			
+			msg_foe_comment_hidden: 'Post from foe hidden.',
+			btn_display_foe_comment: 'Display post',
+			
+			msg_note_unapp: '(Unapproved)',
+			msg_note_spam: '(Spam)',
+			
+			msg_ip_address: 'IP address:',
+			
+			msg_delete_confirm: 'Do you really want to delete this comment?',
+			
+			err_captcha_wrong: 'The confirmation code you entered was incorrect.',
+			err_spamcheck_failed_rejected: 'Your comment was rejected because it appears to be spam.',
+			err_spamcheck_failed_flagged: 'Your comment was posted, but it appears to be spam and has been flagged as such for a moderator to review.',
+			ajax_err_generic_title: 'Error fetching comment data',
+			
+			postform_title: 'Got something to say?',
+			postform_blurb: 'If you have comments or suggestions on this article, you can shout it out here.',
+			postform_blurb_unapp: 'Before your post will be visible to the public, a moderator will have to approve it.',
+			postform_blurb_captcha: 'Because you are not logged in, you will need to enter a visual confirmation before your comment will be posted.',
+			postform_blurb_link: 'Leave a comment...',
+			postform_field_name: 'Your name/screen name:',
+			postform_field_subject: 'Comment subject:',
+			postform_field_comment: 'Comment:',
+			postform_field_captcha_title: 'Visual confirmation:',
+			postform_field_captcha_blurb: 'Please enter the confirmation code seen in the image on the right into the box. If you cannot read the code, please click on the image to generate a new one. This helps to prevent automated bot posting.',
+			postform_field_captcha_label: 'Confirmation code:',
+			postform_field_captcha_cantread_js: 'If you can\'t read the code, click on the image to generate a new one.',
+			postform_field_captcha_cantread_nojs: 'If you can\'t read the code, please refresh this page to generate a new one.',
+			postform_btn_submit: 'Submit comment',
+			
+			on_friend_list: 'On your friend list',
+			on_foe_list: 'On your foe list'
+		},
+		onpage: {
+			lbl_pagetools: 'Page tools',
+			lbl_page_article: 'article',
+			lbl_page_admin: 'administration page',
+			lbl_page_system: 'system message',
+			lbl_page_file: 'uploaded file',
+			lbl_page_help: 'documentation page',
+			lbl_page_user: 'user page',
+			lbl_page_special: 'special page',
+			lbl_page_template: 'template',
+			lbl_page_project: 'project page',
+			lbl_page_category: 'category',
+			lbl_page_external: 'external page',
+			
+			lbl_sitetools: 'site tools',
+			lbl_changes: 'changes',
+			
+			btn_changes_mine: 'mine',
+			btn_changes_recent: 'recent',
+			btn_changes_history: 'this page',
+			
+			btn_discussion: 'discussion (%num_comments%)',
+			btn_discussion_unapp: '<span title="Approved: %num_app% | Unapproved: %num_unapp% | Spam: %num_spam%">discussion (%num_comments%) [!]</span>',
+			btn_edit: 'edit this page',
+			btn_viewsource: 'view source',
+			btn_history: 'history',
+			btn_moreoptions: 'more options',
+			btn_breadcrumbs_home: 'Home',
+			btn_rename_inline: 'Double-click to rename this page',
+			
+			btn_rename: 'rename',
+			btn_printable: 'view printable version',
+			btn_votedelete: 'vote to delete this page',
+			btn_votedelete_reset: 'reset deletion votes',
+			lbl_wikimode: 'page wiki mode:',
+			btn_wikimode_on: 'on',
+			btn_wikimode_off: 'off',
+			btn_wikimode_global: 'global',
+			lbl_protect: 'protection:',
+			btn_protect_on: 'on',
+			btn_protect_off: 'off',
+			btn_protect_semi: 'semi',
+			btn_protect_change: 'change',
+			btn_clearlogs: 'clear page logs',
+			btn_deletepage: 'delete this page',
+			btn_deletepage_votes: ' (<b>%num_votes%</b> vote%plural%)',
+			lbl_password: 'page password:',
+			btn_password_set: 'set',
+			btn_acl: 'manage page access',
+			btn_admin: 'administrative options',
+			
+			tip_article: 'View the page contents, all of the page contents, and nothing but the page contents (alt-a)',
+			tip_comments: 'View the comments that other users have posted about this page (alt-c)',
+			tip_edit: 'Edit the contents of this page (alt-e)',
+			tip_viewsource: 'View the source code (wiki markup) that this page uses (alt-e)',
+			tip_history: 'View a log of actions taken on this page (alt-h)',
+			tip_rename: 'Change the display name of this page (alt-r)',
+			tip_delvote: 'Vote to have this page deleted (alt-d)',
+			tip_resetvotes: 'Clear the list of votes for deletion against this page (alt-y)',
+			tip_printable: 'View a version of this page that is suitable for printing',
+			tip_protect: 'Change the protection level of this page (alt-p)',
+			tip_flushlogs: 'Remove all edit and action logs for this page from the database. IRREVERSIBLE! (alt-l)',
+			tip_deletepage: 'Delete this page. This is always reversible unless the logs are cleared. (alt-k)',
+			tip_adminoptions: 'Administrative options for this page',
+			tip_moreoptions: 'Additional options for working with this page',
+			tip_password: 'Require a password in order for this page to be viewed',
+			tip_aclmanager: 'Manage who can do what with this page (alt-m)',
+			
+			cat_heading_subcategories: 'Subcategories',
+			cat_msg_no_subcategories: 'No subcategories.',
+			cat_heading_pages: 'Pages',
+			cat_msg_no_pages: 'No pages in this category.',
+			
+			filebox_heading: 'Uploaded file',
+			filebox_msg_not_found: 'There are no files uploaded with this name yet. <a href="%upload_link%">Upload a file...</a>',
+			filebox_lbl_type: 'Type:',
+			filebox_lbl_size: 'Size: %size%',
+			filebox_lbl_uploaded: 'Uploaded:',
+			filebox_msg_virus_warning: 'This file type may contain viruses or other code that could harm your computer. You should exercise caution if you download it.',
+			filebox_btn_download: 'Download this file',
+			filebox_btn_upload_new: 'Upload new version',
+			filebox_heading_history: 'File history',
+			filebox_btn_this_version: 'this ver',
+			filebox_btn_revert: 'restore',
+			filebox_btn_current: 'current',
+			
+			protect_heading: 'Protect page',
+			protect_msg_select_level: 'Select a protection level:',
+			protect_btn_full: 'Full protection',
+			protect_btn_full_hint: 'Prevents everyone except moderators and administrators (by default) from editing the page.',
+			protect_btn_semi: 'Semi-protection',
+			protect_btn_semi_hint: 'Only users who have been members of this site for more than 4 days will be able to edit this page.',
+			protect_btn_none: 'No protection',
+			protect_btn_none_hint: 'Allows everybody to edit this page unless ACLs deny them.',
+			protect_lbl_current: 'current setting',
+			protect_lbl_reason: 'Reason:',
+			protect_lbl_reason_hint: 'Enter a short reason for protecting the page. The message you enter here will be displayed in the page\'s logs.',
+			protect_btn_submit: 'Protect page',
+			protect_err_need_reason: 'Please enter a reason for protecting this page.',
+		},
+		editor: {
+			err_server: 'There was a problem starting the editor',
+			err_access_denied_title: 'Not authorized to view source',
+			err_access_denied_body: 'You are not authorized to edit or view the source of this page.',
+			err_save_title: 'There was a problem saving the page.',
+			err_save_body: 'A few problems were encountered while your page was being saved:',
+			err_obsolete_title: 'Someone else modified this page while you were editing it',
+			err_obsolete_body: 'While you were editing this page, %author% modified this page. The edit took place on %timestamp%. You can <a href="%page_url%" onclick="window.open(this.href); return false;">view the latest version of the page</a>, or click %this.editor_btn_save% again to replace the page with your revision.',
+			err_need_captcha_title: 'Missing confirmation code',
+			err_need_captcha_body: 'Please enter the confirmation code in the box labeled "%this.editor_lbl_field_captcha_code%". %this.editor_msg_captcha_blind%',
+			err_no_text_title: 'No text entered',
+			err_no_text_body: 'Please enter the text that will be in this page.',
+			// Server-side errors
+			err_no_rows: 'Page doesn\'t exist in the database',
+			err_no_permission: 'You do not have permission to edit this page.',
+			err_page_protected: 'This page is protected, and you do not have permission to edit protected pages.',
+			err_captcha_wrong: 'The confirmation code you entered is incorrect.',
+			err_spamcheck_failed: 'Your edit was rejected because it looks like spam.',
+			
+			msg_editor_heading: 'Editing page',
+			msg_saved: 'Your changes to this page have been saved.',
+			msg_revert_confirm: 'Do you really want to revert your changes?',
+			msg_discard_confirm: 'Do you really want to discard your changes?',
+			msg_confirm_ajax: 'Do you really want to do this?\nYour changes to this page have not been saved. If you continue, your changes will be lost.',
+			msg_unload: 'If you do, any changes that you have made to this page will be lost.',
+			msg_revert_confirm_title: 'Revert changes?',
+			msg_revert_confirm_body: 'All of the changes you have made to this page will be lost, and the text will be replaced with the latest published (not draft) version of the page.',
+			msg_cancel_confirm_title: 'Close without saving?',
+			msg_cancel_confirm_body: 'You haven\'t saved your changes to the page. Closing the editor will discard your changes.',
+			msg_diff: 'Below is a summarization of the differences between the current revision of the page (left), and your version (right).',
+			msg_diff_empty: 'There are no differences between the text in the editor and the current revision of the page.',
+			msg_editing_old_revision: 'You are editing an old revision of this page. If you click Save, newer revisions of this page will be undone.',
+			msg_have_draft_title: 'A draft copy of this page is available.',
+			msg_have_draft_body: '%author% saved a draft version of this page on %time%. You can <a href="#use_draft" onclick="ajaxEditorUseDraft(); return false;">use the draft copy</a>, or edit the current published version (below). If you edit the published version, the draft copy will remain available, but will not reflect your changes. It is recommended that you edit the draft version instead of editing the published version. You can also <a href="#delete_draft" onclick="ajaxEditorDeleteDraft(); return false;">discard the draft revision</a>.',
+			msg_convert_confirm_title: 'Convert this page?',
+			msg_convert_confirm_body: 'This will change the format of the page. This may cause some loss of formatting information.',
+			msg_convert_draft_load_title: 'Format changed',
+			msg_convert_draft_load_body: 'The revision that was just loaded is in a different format than the current setting. The editor has been switched to match the format.',
+			btn_graphical: 'Convert to HTML',
+			btn_wikitext: 'Convert to wikitext',
+			lbl_edit_summary: 'Brief summary of your changes:',
+			lbl_edit_summary_explain: 'Please summarize and briefly explain what you changed on the page.',
+			lbl_minor_edit: 'Mark revision as trivial:',
+			lbl_minor_edit_field: 'This is a minor edit',
+			lbl_minor_edit_explain: 'Select this if you\'re only making a minor change to the page',
+			btn_save: 'Save',
+			btn_savedraft: 'Save draft',
+			btn_preview: 'Preview',
+			btn_revert: 'Revert',
+			btn_cancel: 'Cancel',
+			btn_diff: 'Show changes',
+			btn_closeviewer: 'Close viewer',
+			msg_draft_saving: 'Saving...',
+			msg_draft_saved: 'Saved at %time%',
+			btn_revert_confirm: 'Revert changes',
+			btn_cancel_confirm: 'Close editor',
+			btn_cancel_cancel: 'Keep editing',
+			preview_blurb: '<b>Reminder:</b> This is only a preview - your changes to this page have not yet been saved.',
+			msg_save_success_title: 'Changes saved',
+			msg_save_success_body: 'Your changes to this page have been saved. Redirecting...',
+			reversion_edit_summary: 'Undid %undo_count% revision(s) by %current_author% to revision %last_rev_id% by %old_author%',
+			msg_confirm_delete_draft_title: 'Delete the draft revision?',
+			msg_confirm_delete_draft_body: 'This will discard the saved draft version of this page.',
+			btn_delete_draft: 'Delete draft',
+			
+			msg_captcha_pleaseenter: 'Please enter the code shown in the image to the right into the text box. This process helps to ensure that this page is not being edited by an automated bot. If the image to the right is illegible, you can regenerate it by clicking on the image (only works if your browser supports Javascript).',
+			msg_captcha_blind: 'If you are visually impaired or otherwise cannot read the text shown to the right, please contact the site management and they will be able to make your requested edits.',
+			lbl_field_captcha: 'Visual confirmation',
+			lbl_field_captcha_code: 'Code:'
+		},
+		history: {
+			summary_clearlogs: 'Automatic backup created when logs were purged',
+			page_subtitle: 'History of edits and actions',
+			heading_edits: 'Edits:',
+			heading_other: 'Other changes:',
+			no_entries: 'No history entries in this category.',
+			btn_compare: 'Compare selected revisions',
+			col_diff: 'Diff',
+			col_datetime: 'Date/time',
+			col_user: 'User',
+			col_page: 'Page',
+			col_summary: 'Edit summary',
+			col_minor: 'Minor',
+			col_actions: 'Actions',
+			col_action_taken: 'Action taken',
+			col_extra: 'Extra info',
+			extra_reason: 'Reason:',
+			extra_oldtitle: 'Old title:',
+			extra_protection_reversion: '(Reversion of previous protection)',
+			extra_upload_reversion: '(Restoration of previous upload)',
+			extra_numvotes: 'Number of votes:',
+			tip_rdns: 'Click cell background for reverse DNS info',
+			action_view: 'View',
+			action_contrib: 'User contribs',
+			action_restore: 'Restore',
+			action_revert: 'Revert action',
+			log_protect: 'Protected page',
+			log_unprotect: 'Unprotected page',
+			log_semiprotect: 'Semi-protected page',
+			log_rename: 'Renamed page',
+			log_create: 'Created page',
+			log_delete: 'Deleted page',
+			log_uploadnew: 'Uploaded new file version',
+			log_votereset: 'Reset deletion votes',
+			lbl_comparingrevisions: 'Comparing revisions:',
+			summary_none_given: 'No edit summary provided.',
+			err_wrong_password: 'Please enter the password for this page before viewing its history.'
+		},
+		catedit: {
+			title: 'Select which categories this page should be included in.',
+			no_categories: 'There are no categories on this site yet.',
+			catbox_lbl_categories: 'Categories:',
+			catbox_lbl_uncategorized: '(Uncategorized)',
+			catbox_link_edit: 'edit categorization',
+			catbox_link_showcategorization: 'show page categorization'
+		},
+		tags: {
+			catbox_link: 'show page tags',
+			lbl_page_tags: 'Page tags:',
+			lbl_no_tags: 'No tags on this page',
+			btn_add_tag: '(add a tag)',
+			lbl_add_tag: 'Add a tag:',
+			btn_add: '+ Add'
+		},
+		delvote: {
+			lbl_votes_one: 'There is one user that thinks this page should be deleted.',
+			lbl_votes_plural: 'There are %num_users% users that think this page should be deleted.',
+			lbl_users_that_voted: 'Users that voted:',
+			btn_deletepage: 'Delete page',
+			btn_resetvotes: 'Reset votes'
+		},
+		ajax: {
+			// Client-side messages
+			protect_prompt_reason: 'Reason for (un)protecting:',
+			rename_prompt: 'What title should this page be renamed to?\n%this.ajax_rename_notice%',
+			rename_prompt_short: 'Enter a new name for this page',
+			rename_notice: 'This won\'t change the URL to this page. To change the URL to this page, use Page Manager in the administration panel.',
+			delete_header: 'Delete this page?',
+			delete_lbl_reason: 'Reason:',
+			delete_prompt_reason: 'Please enter your reason for deleting this page. This will be visible in the page history as well as on the deleted page.',
+			delete_msg_confirm: 'Deleting pages is reversible. Users with administrative access can restore the page, but not any comments, categorization data, tags, or files (if the page contains an uploaded file). If Wiki Mode is in effect globally, everyone can create this page again and see past revisions.',
+			delete_lbl_confirm: 'Yes, I\'m sure',
+			delete_btn_delete: 'Delete page',
+			delvote_confirm_title: 'Vote to delete this page?',
+			delvote_confirm_body: 'Administrators and moderators will see a notice on this page showing who voted.',
+			delvote_btn_submit: 'Vote',
+			delvote_reset_confirm_title: 'Reset votes for deletion?',
+			delvote_reset_confirm_body: 'This will reset the number of votes against this page to zero.',
+			delvote_reset_btn_submit: 'Reset',
+			clearlogs_confirm_title: 'Clear this page\'s logs?',
+			clearlogs_confirm_body: 'This will erase this page\'s entire history and is an irreversible operation.',
+			clearlogs_btn_submit: 'Clear Logs',
+			killphp_confirm: 'Are you really sure you want to do this? Some pages might not function if this emergency-only feature is activated.',
+			killphp_success: 'Embedded PHP in pages has been disabled.',
+			lbl_moreoptions_nojs: 'More options for this page',
+			msg_loading_component: 'Loading %component%...',
+			
+			thmsel_lbl_choosetheme: 'Choose a theme',
+			thmsel_lbl_choosestyle: 'Choose a style variation',
+			thmsel_msg_success: 'Theme changed',
+			thmsel_btn_reload: 'Reload page',
+			thmsel_btn_close: 'Close selector and reload later',
+			thmsel_btn_close_hint: 'You\'ll see your new theme on the next page reload.',
+			
+			badjson_title: 'The site encountered an error while processing your request.',
+			badjson_body: 'We unexpectedly received the following response from the server. The response should have been in the JSON serialization format, but the response wasn\'t composed only of the JSON response. There are three possible triggers for this problem:',
+			badjson_tip1: 'The server sent back a bad HTTP response code and thus sent an error page instead of running Enano. This indicates a possible problem with your server, and is not likely to be a bug with Enano.',
+			badjson_tip2: 'The server sent back the expected JSON response, but also injected some code into the response that should not be there. Typically this consists of advertisement code. In this case, the administrator of this site will have to contact their web host to have advertisements disabled.',
+			badjson_tip3: 'It\'s possible that Enano triggered a PHP error or warning. In this case, you may be looking at a bug in Enano.',
+			badjson_osc: 'This is KNOWN to be the case with the OpenSourceCMS.com demo version of Enano.',
+			badjson_msg_response: 'The response received from the server is as follows:',
+			badjson_msg_viewashtml: 'You may also choose to view the response as HTML.',
+			badjson_btn_viewashtml: 'View as HTML',
+			badjson_html_confirm_title: 'View the response as HTML?',
+			badjson_html_confirm_body: 'If the server\'s response was modified by an attacker to include malicious code, viewing the response as HTML might allow that malicious code to run. Only continue if you have inspected the response text and verified that it is safe.\n\nThe information in JSON responses is typically only meant to be useful to developers and the Enano applets.',
+			badjson_btn_close: 'Close',
+			
+			// Server-side responses
+			rename_too_short: 'The name you entered is too short. Please enter a longer name for this page.',
+			rename_success_title: 'Page renamed',
+			rename_success_body: 'This page has been renamed to "%page_name_new%". You are encouraged to leave a comment explaining your action.<br /><br />Please note that you might not see this change take effect until you reload the page.',
+			clearlogs_success: 'The logs for this page have been cleared. A backup of this page has been added to the logs table so that this page can be restored in case of vandalism or spam later.',
+			delete_need_reason: 'Invalid reason for deletion passed. Please enter a reason for deleting this page.',
+			delete_success: 'This page has been deleted. Note that there is still a log of edits and actions in the database, and anyone with admin rights can raise this page from the dead unless the log is cleared. If the deleted file is an image, there may still be cached thumbnails of it in the cache/ directory, which is inaccessible to users.',
+			delvote_success: 'Your vote to have this page deleted has been cast.\nYou are encouraged to leave a comment explaining the reason for your vote.',
+			delvote_already_voted: 'It appears that you have already voted to have this page deleted.',
+			delvote_reset_success: 'The number of votes for having this page deleted has been reset to zero.',
+			password_success: 'The password for this page has been set.',
+			password_disable_success: 'The password for this page has been disabled.'
+		},
+		sidebar: {
+			title_navigation: 'Navigation',
+			title_tools: 'Tools',
+			title_search: 'Search',
+			title_links: 'Links',
+			title_about: 'About',
+			
+			btn_home: 'Home',
+			btn_createpage: 'Create a page',
+			btn_uploadfile: 'Upload file',
+			btn_specialpages: 'Special pages',
+			btn_administration: 'Administration',
+			btn_editsidebar: 'Edit the sidebar',
+			btn_search_go: 'Go',
+			btn_enanopowered_admin_tip: 'You may disable this button in the admin panel under General Configuration.',
+			
+			btn_userpage: 'User page',
+			btn_mycontribs: 'My contributions',
+			btn_preferences: 'Preferences',
+			btn_preferences_short: 'User CP',
+			btn_privatemessages: 'Private messages',
+			btn_groupcp: 'Group control panel',
+			btn_register: 'Create an account',
+			btn_login: 'Log in',
+			btn_logout: 'Log out',
+			btn_changestyle: 'Change theme',
+			btn_recent_changes: 'Recent edits'
+		},
+		perm: {
+			read: 'Read page(s)',
+			post_comments: 'Post comments',
+			edit_comments: 'Edit own comments',
+			edit_page: 'Edit page',
+			view_source: 'View source',
+			edit_wysiwyg: 'Use graphical editor (<a href="http://docs.enanocms.org/Help:4.1#toc4">WEAK</a>)',
+			mod_comments: 'Moderate comments',
+			history_view: 'View history/diffs',
+			history_rollback: 'Rollback history',
+			history_rollback_extra: 'Undelete page(s)',
+			protect: 'Protect page(s)',
+			rename: 'Rename page(s)',
+			clear_logs: 'Clear page logs (dangerous)',
+			vote_delete: 'Vote to delete',
+			vote_reset: 'Reset delete votes',
+			delete_page: 'Delete page(s)',
+			tag_create: 'Tag page(s)',
+			tag_delete_own: 'Remove own page tags',
+			tag_delete_other: 'Remove others\' page tags',
+			set_wiki_mode: 'Set per-page wiki mode',
+			password_set: 'Set password',
+			password_reset: 'Disable/reset password',
+			mod_misc: 'Super moderator (generate SQL backtraces, view IP addresses, and send large numbers of private messages)',
+			edit_cat: 'Edit categorization',
+			even_when_protected: 'Allow editing, renaming, and categorization even when protected',
+			upload_files: 'Upload files',
+			upload_new_version: 'Upload new versions of files',
+			create_page: 'Create pages',
+			html_in_pages: 'Embed unrestricted HTML in pages',
+			php_in_pages: 'Embed PHP code in pages',
+			custom_user_title: 'Use a custom user title',
+			edit_acl: 'Edit access control lists'
+		},
+		plugin: {
+			author_enano: 'Enano CMS Project',
+			specialadmin_title: 'Administration panel',
+			specialadmin_desc: 'Provides the page Special:Administration, which is the AJAX frontend to the various Admin pagelets. This plugin cannot be disabled.',
+			privatemessages_title: 'Private Message frontend',
+			privatemessages_desc: 'Provides the page Special:PrivateMessages, which is used to manage private message functions. Also handles buddy lists.',
+			specialcss_title: 'CSS Backend',
+			specialcss_desc: 'Provides the page Special:CSS, which is used in template files to reference the style sheet. Disabling or deleting this plugin will result in site instability.',
+			specialgroups_title: 'Group control panel',
+			specialgroups_desc: 'Provides group moderators and site administrators with the ability to control who is part of their groups.',
+			specialpagefuncs_title: 'Special page-related pages',
+			specialpagefuncs_desc: 'Provides the page Special:CreatePage, which can be used to create new pages. Also adds the About Enano and GNU General Public License pages.',
+			specialsearch_title: 'Search UI/frontend',
+			specialsearch_desc: 'Provides the page Special:Search, which is a frontend to the Enano search engine.',
+			specialupdownload_title: 'Upload/download frontend',
+			specialupdownload_desc: 'Provides the pages Special:UploadFile and Special:DownloadFile. UploadFile is used to upload files to the site, and DownloadFile fetches the file from the database, creates thumbnails if necessary, and sends the file to the user.',
+			specialuserfuncs_title: 'Special user/login-related pages',
+			specialuserfuncs_desc: 'Provides the pages Special:Login, Special:Logout, Special:Register, and Special:Preferences.',
+			specialuserprefs_title: 'User control panel',
+			specialuserprefs_desc: 'Provides the page Special:Preferences.',
+			speciallog_title: 'Log displayer',
+			speciallog_desc: 'Provides the page Special:Log, which is used to view modifications to pages on the site.',
+			tagcloud_title: 'Tag cloud sidebar block',
+			tagcloud_desc: 'Brings back the "tag cloud" sidebar block that was removed in Enano 1.1.6.'
+		},
+		paginate: {
+			lbl_page: 'Page:',
+			btn_first: 'First',
+			btn_last: 'Last',
+			btn_prev: 'Prev',
+			btn_next: 'Next',
+			lbl_goto_page: 'Go to page:',
+			err_bad_page_title: 'Invalid entry',
+			err_bad_page_body: 'Please enter a page number between 1 and %max%.'
+		},
+		upload: {
+			err_disabled_site: 'File uploads are disabled this website.',
+			err_disabled_acl: 'File uploads are disabled for your user account or group.',
+			
+			err_title: 'Upload failed',
+			err_cant_get_file_meta: 'The server could not retrieve the array $_FILES[\'data\'].',
+			err_too_big_or_small: 'The file you uploaded is either too large or 0 bytes in length.',
+			err_banned_ext: 'The file type ".%ext%" is not allowed.',
+			err_banned_chars: 'The filename contains invalid characters.',
+			err_already_exists: 'The file already exists. You can <a href="%upload_link%">upload a new version of this file</a>.',
+			err_replace_protected: 'Either the file does not exist (and therefore cannot be updated) or the file is protected.',
+			err_move_failed: 'Could not move uploaded file to the new location.',
+			err_replace_denied: 'Uploading new versions of files has been disabled for your user account or group.',
+			
+			success_title: 'Upload complete',
+			success_body: 'Your file has been uploaded successfully. View the <a href="%file_link%">file\'s page</a>.',
+			
+			intro: 'Using this form you can upload a file to the %config.site_name% site.',
+			max_filesize: 'The maximum file size is %config.max_file_size% %this.etc_unit_bytes% (%size%).',
+			field_file: 'File:',
+			field_renameto: 'Rename to:',
+			field_comments: 'Comments:<br />(can be wiki-formatted)',
+			field_reason: 'Reason for uploading the new version:',
+			btn_upload: 'Upload file',
+			
+			err_not_found_title: 'File not found',
+			err_not_found_body: 'The file "%filename%" cannot be found.'
+		},
+		tz: {
+			// Thanks to phpBB for this timezone data.
+			// Do not add or remove from this list - contact the core team to have changes made to this list.
+			hrs_n12: 'UTC - 12 hours',
+			hrs_n11: 'UTC - 11 hours',
+			hrs_n10: 'UTC - 10 hours',
+			hrs_n9p5: 'UTC - 9:30 hours',
+			hrs_n9: 'UTC - 9 hours',
+			hrs_n8: 'UTC - 8 hours',
+			hrs_n7: 'UTC - 7 hours',
+			hrs_n6: 'UTC - 6 hours',
+			hrs_n5: 'UTC - 5 hours',
+			hrs_n4: 'UTC - 4 hours',
+			hrs_n3p5: 'UTC - 3:30 hours',
+			hrs_n3: 'UTC - 3 hours',
+			hrs_n2: 'UTC - 2 hours',
+			hrs_n1: 'UTC - 1 hour',
+			hrs_0: 'UTC',
+			hrs_1: 'UTC + 1 hour',
+			hrs_2: 'UTC + 2 hours',
+			hrs_3: 'UTC + 3 hours',
+			hrs_3p5: 'UTC + 3:30 hours',
+			hrs_4: 'UTC + 4 hours',
+			hrs_4p5: 'UTC + 4:30 hours',
+			hrs_5: 'UTC + 5 hours',
+			hrs_5p5: 'UTC + 5:30 hours',
+			hrs_5p75: 'UTC + 5:45 hours',
+			hrs_6: 'UTC + 6 hours',
+			hrs_6p5: 'UTC + 6:30 hours',
+			hrs_7: 'UTC + 7 hours',
+			hrs_8: 'UTC + 8 hours',
+			hrs_8p75: 'UTC + 8:45 hours',
+			hrs_9: 'UTC + 9 hours',
+			hrs_9p5: 'UTC + 9:30 hours',
+			hrs_10: 'UTC + 10 hours',
+			hrs_10p5: 'UTC + 10:30 hours',
+			hrs_11: 'UTC + 11 hours',
+			hrs_11p5: 'UTC + 11:30 hours',
+			hrs_12: 'UTC + 12 hours',
+			hrs_12p75: 'UTC + 12:45 hours',
+			hrs_13: 'UTC + 13 hours',
+			hrs_14: 'UTC + 14 hours',
+			title_n12: '[UTC - 12] Baker Island Time',
+			title_n11: '[UTC - 11] Niue Time, Samoa Standard Time',
+			title_n10: '[UTC - 10] Hawaii-Aleutian Standard Time, Cook Island Time',
+			title_n9p5: '[UTC - 9:30] Marquesas Islands Time',
+			title_n9: '[UTC - 9] Alaska Standard Time, Gambier Island Time',
+			title_n8: '[UTC - 8] Pacific Standard Time',
+			title_n7: '[UTC - 7] Mountain Standard Time',
+			title_n6: '[UTC - 6] Central Standard Time',
+			title_n5: '[UTC - 5] Eastern Standard Time',
+			title_n4: '[UTC - 4] Atlantic Standard Time',
+			title_n3p5: '[UTC - 3:30] Newfoundland Standard Time',
+			title_n3: '[UTC - 3] Amazon Standard Time, Central Greenland Time',
+			title_n2: '[UTC - 2] Fernando de Noronha Time, South Georgia &amp; the South Sandwich Islands Time',
+			title_n1: '[UTC - 1] Azores Standard Time, Cape Verde Time, Eastern Greenland Time',
+			title_0: '[UTC] Western European Time, Greenwich Mean Time',
+			title_1: '[UTC + 1] Central European Time, West African Time',
+			title_2: '[UTC + 2] Eastern European Time, Central African Time',
+			title_3: '[UTC + 3] Moscow Standard Time, Eastern African Time',
+			title_3p5: '[UTC + 3:30] Iran Standard Time',
+			title_4: '[UTC + 4] Gulf Standard Time, Samara Standard Time',
+			title_4p5: '[UTC + 4:30] Afghanistan Time',
+			title_5: '[UTC + 5] Pakistan Standard Time, Yekaterinburg Standard Time',
+			title_5p5: '[UTC + 5:30] Indian Standard Time, Sri Lanka Time',
+			title_5p75: '[UTC + 5:45] Nepal Time',
+			title_6: '[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirsk Standard Time',
+			title_6p5: '[UTC + 6:30] Cocos Islands Time, Myanmar Time',
+			title_7: '[UTC + 7] Indochina Time, Krasnoyarsk Standard Time',
+			title_8: '[UTC + 8] Chinese Standard Time, Australian Western Standard Time, Irkutsk Standard Time',
+			title_8p75: '[UTC + 8:45] Southeastern Western Australia Standard Time',
+			title_9: '[UTC + 9] Japan Standard Time, Korea Standard Time, Chita Standard Time',
+			title_9p5: '[UTC + 9:30] Australian Central Standard Time',
+			title_10: '[UTC + 10] Australian Eastern Standard Time, Vladivostok Standard Time',
+			title_10p5: '[UTC + 10:30] Lord Howe Standard Time',
+			title_11: '[UTC + 11] Solomon Island Time, Magadan Standard Time',
+			title_11p5: '[UTC + 11:30] Norfolk Island Time',
+			title_12: '[UTC + 12] New Zealand Time, Fiji Time, Kamchatka Standard Time',
+			title_12p75: '[UTC + 12:45] Chatham Islands Time',
+			title_13: '[UTC + 13] Tonga Time, Phoenix Islands Time',
+			title_14: '[UTC + 14] Line Island Time',
+			// This is a JSON string that lists all the timezones that are defined here.
+			list: '{"n12":-12,"n11":-11,"n10":-10,"n9p5":-9.5,"n9":-9,"n8":-8,"n7":-7,"n6":-6,"n5":-5,"n4":-4,"n3p5":-3.5,"n3":-3,"n2":-2,"n1":-1,"0":0,"1":1,"2":2,"3":3,"3p5":3.5,"4":4,"4p5":4.5,"5":5,"5p5":5.5,"5p75":5.75,"6":6,"6p5":6.5,"7":7,"8":8,"8p75":8.75,"9":9,"9p5":9.5,"10":10,"10p5":10.5,"11":11,"11p5":11.5,"12":12,"12p75":12.75,"13":13,"14":14}',
+			
+			// DST profiles
+			dst_off: 'My region doesn\'t observe DST',
+			dst_usa: 'United States: second Sunday of March to first Sunday of November',
+			dst_europe: 'Europe: last Sunday of March to last Sunday of October',
+			dst_australia: 'Australia (except Tasmania): last Sunday of October to last Sunday of March',
+			dst_tasmania: 'Tasmania: first Sunday of October to last Sunday of March'
+		},
+		etc: {
+			redirect_title: 'Redirecting...',
+			redirect_body: 'Please wait while you are redirected.',
+			redirect_timeout: 'If you are not redirected within %timeout% seconds, please <a href="%redirect_url%">click here</a>.',
+			// Generic "Save Changes" button
+			save_changes: 'Save changes',
+			// Generic "Cancel changes" button
+			cancel_changes: 'Cancel changes',
+			// Generic wizard buttons
+			wizard_next: 'Next >',
+			wizard_back: '< Back',
+			wizard_previous: '< Previous',
+			// Generic link to main page
+			btn_main_page: 'Main page',
+			// Generic switchable editor buttons
+			tinymce_btn_text: 'text editor',
+			tinymce_btn_graphical: 'graphical editor',
+			// Generic "Notice:" label
+			lbl_notice: 'Notice:',
+			// Generic "Access denied"
+			access_denied: 'Access to the specified file, resource, or action is denied.',
+			access_denied_short: 'Access denied',
+			access_denied_need_reauth: 'You need to re-authenticate before you can do that.',
+			return_to_page: 'Return to the page',
+			invalid_request_short: 'Invalid request',
+			// Message box buttons
+			ok: 'OK',
+			cancel: 'Cancel',
+			close: 'Close',
+			yes: 'Yes',
+			no: 'No',
+			go: 'Go',
+			unit_bytes: 'bytes',
+			unit_kilobytes: 'kilobytes',
+			unit_megabytes: 'megabytes',
+			unit_gigabytes: 'gigabytes',
+			unit_terabytes: 'terabytes',
+			unit_kilobytes_short: 'KB',
+			unit_megabytes_short: 'MB',
+			unit_gigabytes_short: 'GB',
+			unit_terabytes_short: 'TB',
+			unit_pixels: 'pixels',
+			unit_pixels_short: 'px',
+			unit_second: 'second',
+			unit_seconds: 'seconds',
+			unit_day: 'day',
+			unit_days: 'days',
+			unit_week: 'week',
+			unit_weeks: 'weeks',
+			unit_month: 'month',
+			unit_months: 'months',
+			unit_year: 'year',
+			unit_years: 'years',
+			unit_minute: 'minute',
+			unit_minutes: 'minutes',
+			unit_minute_short: 'min',
+			unit_minutes_short: 'mins'
+		}
+	}
 };
 
 // All done! :-)
--- a/language/english/install.json	Sun Mar 28 21:49:26 2010 -0400
+++ b/language/english/install.json	Sun Mar 28 23:10:46 2010 -0400
@@ -14,515 +14,515 @@
 // Language: ISO-639-3 eng (English)
 
 var enano_lang_install = {
-  categories: [
-    'meta', 'language', 'welcome', 'license', 'sysreqs', 'database', 'dbmysql', 'dbpgsql', 'website', 'login', 'confirm', 'install', 'finish', 'pophelp', 'upgrade', 'cli'
-  ],
-  strings: {
-    meta: {
-      site_name: 'Enano installation',
-      site_desc: 'Install Enano on your server.',
-      enano_copyright: 'Enano and its various components, related documentation, and artwork are copyright &copy; 2006 Dan Fuhry.<br />This program is Free Software; see the file "GPL" included with this package for details.',
-      sidebar_heading: 'Installation progress',
-      btn_article: 'installation page',
-      btn_continue: 'Continue',
-      lbl_before_continue: 'Before continuing:',
-      step: 'Step %step%: %title%',
-      
-      msg_err_verification: 'One or more of the form fields is incorrect. Please correct any information in the form that has an "X" next to it.',
-      
-      msg_err_stagefailed_title: 'Enano installation failed.',
-      msg_err_stagefailed_body: 'When you have corrected the error, click the button below to attempt to continue the installation.',
-      msg_err_stagefailed_mysqlerror: 'The error returned from MySQL was:',
-      btn_retry_installation: 'Retry installation',
-    },
-    language: {
-      modetitle: 'Language',
-    },
-    welcome: {
-      modetitle: 'Welcome',
-      heading: 'Welcome to Enano',
-      version: 'version',
-      branch_stable: 'stable',
-      branch_unstable: 'unstable',
-      aka: 'also affectionately known as "%codename%" <tt>:)</tt>',
-      btn_start: 'Start installation',
-    },
-    license: {
-      modetitle: 'License',
-      modetitle_long: 'License agreement',
-      heading: 'Welcome to the Enano installer.',
-      blurb_thankyou: 'Thank you for choosing Enano as your CMS. You\'ve selected the finest in design, the strongest in security, and the latest in Web 2.0 toys. Trust us, you\'ll like it.',
-      blurb_pleaseread: 'To get started, please read and accept the following license agreement. You\'ve probably seen it before.',
-      info_unstable_title: 'Notice for prerelease versions',
-      info_unstable_body: 'This version of Enano is designed only for testing and evaluation purposes. <b>It is not yet completely stable, and should not be used on production websites.</b> As with any Enano version, Dan Fuhry and the Enano team cannot be responsible for any damage, physical or otherwise, to any property as a result of the use of Enano. While security is a number one priority, sometimes things slip through.',
-      section_gpl_heading: 'Lawyer-readable version',
-      btn_i_agree: 'I agree to the license terms',
-      objective_ensure_agree: 'Ensure that you agree with the terms of the license',
-      objective_have_db_info: 'Have your database host, name, username, and password available',
-      gpl_blurb_inenglish: 'You may view a copy of the GNU General Public License in English in the file GPL-EN included with this package.',
-    },
-    sysreqs: {
-      modetitle: 'Requirements',
-      modetitle_long: 'Server requirements',
-      heading: 'Checking your server',
-      blurb: 'Enano has several requirements that must be met before it can be installed. If all is good then note any warnings and click Continue below.',
-      btn_refresh: 'Recheck server',
-      
-      req_supported: 'Supported',
-      req_notfound: 'Not found',
-      req_found: 'Found',
-      req_enabled: 'Enabled',
-      req_disabled: 'Disabled',
-      req_writable: 'Writable',
-      req_unwritable: 'Unwritable',
-      req_gmp: 'GNU Multi-Precision (GMP)',
-      req_bigint: 'Big_Int',
-      req_bcmath: 'BCMath',
-      
-      heading_serverenv: 'Server environment',
-      heading_dbms: 'Database servers',
-      heading_files: 'Writable files',
-      heading_images: 'Image manipulation',
-      
-      req_apache: 'Apache web server',
-      req_php: 'PHP version',
-      req_mysql: 'MySQL database support',
-      req_postgresql: 'PostgreSQL database support',
-      req_safemode: 'Safe Mode',
-      req_uploads: 'PHP file upload support',
-      req_ctype: 'PHP ctype_* validation functions',
-      req_crypto: 'Arbitrary precision (cryptographic) math',
-      req_config_writable: 'Configuration file: config.new.php',
-      req_htaccess_writable: 'Apache rewrite rules: .htaccess.new',
-      req_files_writable: 'File storage directory: files/',
-      req_cache_writable: 'Cache directory: cache/',
-      req_gd2: 'GD2 library',
-      req_imagemagick: 'ImageMagick',
-      
-      req_hint_htaccess_writable: 'Only needs to be writable if you plan to use Rewritten URLs.',
-      req_hint_gd2: 'Used for generating visual confirmation and resizing uploaded images',
-      req_hint_imagemagick: 'Faster alternative for resizing uploaded images',
-      
-      req_help_apache: 'Apache is the best server for Enano because it provides features that Enano can use, primarily support for Rewritten URLs.',
-      req_help_php: 'Enano requires PHP version 5.0.0 or later, and runs best under PHP 5.2.0 or later. (You are running PHP %php_version%).',
-      req_help_safemode: 'Safe Mode interferes with Enano\'s ability to operate properly. Thus, installing Enano with Safe Mode enabled is currently not supported.',
-      req_help_uploads: 'PHP file upload support is a prerequisite for uploading files via Enano\'s web interface.',
-      req_help_writable: 'Certain files need to be writable for installation and certain features to work properly. Use your FTP client\'s "CHMOD" feature to set numeric permissions on the items listed above: 666 for files, and 777 for directories. It\'s safest to CHMOD config.php to 444 once installation is complete.',
-      req_help_gd2: 'Without GD, Enano has to use a visual confirmation engine that generates weaker images that are easier to break.',
-      req_help_imagemagick: 'Without ImageMagick, Enano will use GD (if it is available) to scale images down. This is slightly slower.',
-      req_help_crypto_none: 'Your server has no support for arbitrary-precision math, which is required for encrypted logon. Your username and password will not be transmitted securely until one of the following PHP extensions is installed: GMP, Big_Int, or BCMath.',
-      req_help_crypto_bcmath: 'Your server supports arbitrary-precision math, but only through PHP\'s BCMath extension which is extremely slow. Expect the process of logging in to take 6-10 seconds.',
-      
-      err_no_dbms_title: 'No database backends are available.',
-      err_no_dbms_body: 'Enano requires at least one database backend to install. Please consult with your hosting provider to obtain help with this situation. If you are running a VPS or dedicated server, install the php-mysql or php-pgsql (on Red Hat&reg;-based distributions) or php5-mysql or php5-pgsql (on Debian-based distributions) packages. For Windows servers, ensure that the php_mysql or php_pgsql extension is enabled in php.ini.',
-      
-      summary_pass_title: 'Congratulations! Enano can run on this server.',
-      summary_pass_body: 'The system requirement check was successful and Enano has verified that it can run on this server with all available features. Below is a summary of what was checked. Click the Continue button below to proceed with installation.',
-      
-      summary_warn_title: 'Warnings encountered during server check',
-      summary_warn_body: 'Since some server features that Enano uses aren\'t available, Enano has turned its support for these features off. The warnings below provide information on how you can fix this.',
-      
-      summary_fail_title: 'Installation requirements not met',
-      summary_fail_body: 'As a precaution, Enano will not install until the above requirements have been met. Contact your server administrator or hosting company for support. If you still need help, please use the <a href="http://forum.enanocms.org/" onclick="window.open(this.href);">Enano support forums</a>.',
-      
-      objective_scalebacks: 'Review the list above to ensure that you are satisfied with any of Enano\'s workarounds for your server. If you need a particular feature and that feature is listed as disabled above, you should take the opportunity now to correct the problem.'
-    },
-    database: {
-      modetitle: 'Database',
-      modetitle_long: 'Database information',
-      heading_optionalinfo: 'Optional information',
-      
-      driver_heading: 'Choose a database driver',
-      driver_intro: 'The next step is to choose the database driver that Enano will use. In most cases this is MySQL, but there are certain advantages to PostgreSQL, which is made available only experimentally.',
-      driver_msg_virt_appliance: '<b>You\'re using the Enano virtual appliance.</b><br />Unless you configured the appliance manually, PostgreSQL support is not available. In 99% of cases you\'ll want to click MySQL below.',
-      driver_err_no_mysql: 'You don\'t have the MySQL PHP extension installed.',
-      driver_err_no_pgsql: 'You don\'t have the PostgreSQL PHP extensnion installed.',
-      driver_mysql: 'MySQL',
-      driver_mysql_intro: 'Click this button to use MySQL as the database backend for your site. Most web hosts support MySQL, and if you have administrative access to your MySQL server, you can create a new database and user during this installation process if you haven\'t done so already.',
-      driver_pgsql: 'PostgreSQL',
-      driver_pgsql_intro: 'Click this button to use PostgreSQL as the database backend for your site. While not as widely supported, PostgreSQL has more liberal licensing conditions and when properly configured is faster than MySQL. Some plugins may not work with the PostgreSQL driver.',
-      
-      objective_test: 'Check your MySQL connection using the "Test Connection" button.',
-      objective_uncrypt: 'Be aware that your database information will be transmitted unencrypted several times.',
-      
-      // database_post module
-      btn_go_back: 'Go back',
-      msg_success_title: 'Connection successful',
-      msg_success_body: 'The database has been contacted and initial tables created successfully. Redirecting...',
-      msg_success_redirect: 'Click if you\'re not redirected within 2 seconds',
-      
-      msg_post_fail_title: 'Database connection failed',
-      msg_post_fail_body: 'The installer couldn\'t connect to the database because something went wrong while the connection attempt was being made. Please press your browser\'s back button and correct your database information.',
-      msg_post_fail_desc: 'Error description:',
-      
-      msg_sql_fail_title: 'Database operation failed',
-      msg_sql_fail_body: 'The installer couldn\'t create one of the tables used for installation.',
-    },
-    dbmysql: {
-      msg_err_mysql_connect: '<b>Error:</b> The database server "%db_host%" couldn\'t be contacted.<br />%mysql_error%',
-      msg_err_mysql_auth: '<b>Error:</b> Access to MySQL under the specified credentials was denied.<br />%mysql_error%',
-      msg_err_mysql_dbperm: '<b>Error:</b> Access to the specified database using those login credentials was denied.<br />%mysql_error%',
-      msg_err_mysql_dbexist: '<b>Error:</b> The specified database does not exist<br />%mysql_error%',
-      msg_err_mysql_version: '<b>Error:</b> Your version of MySQL (%mysql_version%) is older than 4.1.17. Enano will still work, but there is a known bug with the comment system and MySQL 4.1.11 that involves some comments not being displayed, due to an issue with the PHP function mysql_fetch_row().',
-      
-      msg_warn_creating_db: '<b>Warning:</b> The database you specified does not exist. It will be created during installation.',
-      msg_warn_creating_user: '<b>Warning:</b> The specified regular user does not exist or the password is incorrect. The user will be created during installation. If the user already exists, the password will be reset.',
-      msg_warn_mysql_version: 'The MySQL version that your server is running could not be determined.',
-      
-      msg_info_mysql_good: 'Your version of MySQL meets Enano requirements.',
-      msg_test_success: 'All checks passed! You can use this database configuration with Enano.',
-      
-      blurb_needdb: 'Now we need some information that will allow Enano to contact your database server. Enano uses MySQL as a data storage backend, and we need to have access to a MySQL server in order to continue.',
-      blurb_howtomysql: 'If you do not have access to a MySQL server, and you are using your own server, you can download MySQL for free from <a href="http://www.mysql.com/">MySQL.com</a>. <b>Please note that, like Enano, MySQL is licensed under the GNU GPL.</b> If you need to modify MySQL and then distribute your modifications, you must either distribute them under the terms of the GPL or purchase a proprietary license.',
-      
-      vm_login_info: '<b>MySQL login information for this virtual appliance:</b><br /><br />Database hostname: %host%<br />Database login: username "%user%", password: "%pass%" (without quotes)<br />Database name: %name%',
-      
-      table_title: 'Database information',
-      
-      field_hostname_title: 'Database hostname',
-      field_hostname_body: 'This is the hostname (or sometimes the IP address) of your MySQL server. In many cases, this is "localhost". To connect through a Unix socket, use the format ":/path/to/mysql.sock".',
-      field_port_title: 'Database port',
-      field_port_body: 'The TCP port Enano will use to connect to MySQL. This is 3306 on the vast majority of servers. This isn\'t relevant if you use a socket file.',
-      field_dbname_title: 'Database name',
-      field_dbname_body: 'The name of the actual database. If you don\'t already have a database, you can create one here, if you have the username and password of a MySQL user with administrative rights.',
-      field_dbauth_title: 'Database login',
-      field_dbauth_body: 'These fields should be the username and password of a user with "select", "insert", "update", "delete", "create table", and "replace" privileges for your database.',
-      field_tableprefix_title: 'Table prefix',
-      field_tableprefix_body: 'The value that you enter here will be added to the beginning of the name of each Enano table. You may use lowercase letters (a-z), numbers (0-9), and underscores (_).',
-      field_rootauth_title: 'Database administrative login',
-      field_rootauth_body: 'If the MySQL database or username that you entered above does not exist yet, you can create them here, assuming that you have the login information for an administrative user (such as root). Leave these fields blank unless you need to use them.',
-      field_mysqlversion_title: 'MySQL version',
-      field_mysqlversion_blurb_willbechecked: 'MySQL version information will be checked when you click "Test Connection".',
-      field_droptables_title: 'Delete existing tables?',
-      field_droptables_body: 'If this option is checked, all the tables that will be used by Enano will be dropped (deleted) before the schema is executed. Do NOT use this option unless specifically instructed to.',
-      field_droptables_lbl: 'Drop existing tables',
-      
-      btn_testconnection: 'Test connection',
-    },
-    dbpgsql: {
-      msg_err_connect: 'There was a problem connecting to PostgreSQL. Please check your connection information above.',
-      msg_err_version: '<b>Error:</b> Your version of PostgreSQL (%pg_version%) is older than 8.2.5. Enano cannot be installed.',
-      
-      msg_warn_pg_version: 'The PostgreSQL version that your server is running could not be determined.',
-      msg_err_auth: 'Access to the database was denied. Ensure that your database exists and that your username and password are correct.',
-      
-      msg_info_version_good: 'Your version of PostgreSQL meets Enano requirements.',
-      msg_test_success: 'All checks passed! You can use this database configuration with Enano.',
-      
-      blurb_needdb: 'Now we need some information that will allow Enano to contact your database server. Enano uses PostgreSQL as a data storage backend, and we need to have access to a PostgreSQL server in order to continue.',
-      blurb_howtomysql: 'If you do not have access to a PostgreSQL server, and you are using your own server, you can download PostgreSQL for free from <a href="http://www.mysql.com/">PostgreSQL.com</a>. <b>Please note that, like Enano, PostgreSQL is licensed under the GNU GPL.</b> If you need to modify PostgreSQL and then distribute your modifications, you must either distribute them under the terms of the GPL or purchase a proprietary license.',
-      
-      vm_login_info: '<b>PostgreSQL login information for this virtual appliance:</b><br /><br />Database hostname: %host%<br />Database login: username "%user%", password: "%pass%" (without quotes)<br />Database name: %name%',
-      
-      table_title: 'Database information',
-      
-      field_hostname_title: 'Database hostname',
-      field_hostname_body: 'This is the hostname (or sometimes the IP address) of your PostgreSQL server. In many cases, this is "localhost".',
-      field_port_title: 'Database port',
-      field_port_body: 'The TCP port Enano will use to connect to PostgreSQL. This is 5432 on the vast majority of servers.',
-      field_dbname_title: 'Database name',
-      field_dbname_body: 'The name of the actual database. If you don\'t already have a database, you can create one here, if you have the username and password of a PostgreSQL user with administrative rights.',
-      field_dbauth_title: 'Database login',
-      field_dbauth_body: 'These fields should be the username and password of a user with "select", "insert", "update", "delete", "create table", and "replace" privileges for your database.',
-      field_tableprefix_title: 'Table prefix',
-      field_tableprefix_body: 'The value that you enter here will be added to the beginning of the name of each Enano table. You may use lowercase letters (a-z), numbers (0-9), and underscores (_).',
-      field_rootauth_title: 'Database administrative login',
-      field_rootauth_body: 'If the PostgreSQL database or username that you entered above does not exist yet, you can create them here, assuming that you have the login information for an administrative user (such as root). Leave these fields blank unless you need to use them.',
-      field_pgsqlversion_title: 'PostgreSQL version',
-      field_pgsqlversion_blurb_willbechecked: 'PostgreSQL version information will be checked when you click "Test Connection".',
-      field_droptables_title: 'Delete existing tables?',
-      field_droptables_body: 'If this option is checked, all the tables that will be used by Enano will be dropped (deleted) before the schema is executed. Do NOT use this option unless specifically instructed to.',
-      field_droptables_lbl: 'Drop existing tables',
-      
-      btn_testconnection: 'Test connection',
-    },
-    website: {
-      modetitle: 'Site info',
-      modetitle_long: 'Website information',
-      header_blurb: 'The next step is to enter some information about your website. You can always change this information later, using the administration panel.',
-      
-      msg_ajax_test_fail_title: 'All tests failed',
-      msg_ajax_test_fail_body: 'None of the URL handling tests worked; you may have problems using Enano on your server.',
-      msg_bestmethod_rewrite: 'The installer has detected that using rewritten URLs is the best level that will work.',
-      msg_bestmethod_shortened: 'The installer has detected that using shortened URLs is the best level that will work.',
-      msg_bestmethod_tiny: 'The installer has detected that using tiny URLs is the best level that will work.',
-      msg_bestmethod_standard: 'The installer has detected that using standard URLs is the only level that will work.',
-      msg_modrewrite_enabled: 'Your copy of Enano has the Windows mod_rewrite workaround patch enabled.',
-      msg_modrewrite_necessary: 'If you don\'t plan to use the Rewritten URLs option below, you should go back and re-download Enano without this workaround enabled. The mod_rewrite issues Enano faces under Windows are caused by a security change in Apache 2.2 that is not present in Apache 2.0. <a href="http://enanocms.org/windows-patch" onclick="window.open(this.href); return false;">Learn more</a>',
-      msg_modrewrite_unnecessary: 'The Enano installer has detected that you\'re probably not running Apache 2.2 under Windows. You don\'t need this workaround unless you\'re using this specific software stack. Unless you\'re sure that you are running Apache 2.2 under Windows on your server, it is recommended that you disable the <tt>WINDOWS_MOD_REWRITE_WORKAROUNDS</tt> constant in includes/constants.php. <a href="http://enanocms.org/windows-patch" onclick="window.open(this.href); return false;">Learn more</a>',
-      msg_modrewrite_disabled: 'Your server is running Apache 2.2 on Windows, but the Windows mod_rewrite workaround patch is disabled.',
-      msg_modrewrite_maybeneeded: 'Do not use Rewritten URLs unless you re-download Enano with the Windows Patch enabled or uncomment the <tt>WINDOWS_MOD_REWRITE_WORKAROUNDS</tt> line in <tt>includes/constants.php</tt>. Otherwise you may have trouble accessing login and administration pages due to a bug in Apache. <a href="http://enanocms.org/windows-patch" onclick="window.open(this.href); return false;">Learn more</a>',
-      
-      field_name: 'Pick a name',
-      field_name_hint: 'Now for the fun part - it\'s time to name your website. Try to pick something that doesn\'t include any special characters, since this can make project-page URLs look botched.',
-      field_desc: 'Enter a short description',
-      field_desc_hint: 'Here you should enter a very short description of your site. Sometimes this is a slogan or, depending on the theme you\'ve chosen, a set of keywords that can go into a META description tag.',
-      field_copyright: 'Copyright info',
-      field_copyright_hint: 'The text you enter here will be shown at the bottom of most pages. Typically this is where a copyright notice would go. Keep it short and sweet; you can use <a href="http://docs.enanocms.org/Help:3.1">internal links</a> to link to project pages you\'ll create later.',
-      field_startwith: 'Start with:',
-      field_startwith_blank: 'A blank site with only a main page',
-      field_startwith_blank_hint: 'This is best if you\'ve worked with Enano or other wiki software before and for production installations. It installs one page which you can edit to immediately get started on your site.',
-      field_startwith_tutorial: 'The Enano tutorial suite',
-      field_startwith_tutorial_hint: 'This option installs several tutorial pages that help you to get started quickly if you\'re new to Enano.',
-      field_urlscheme: 'URL formatting',
-      field_urlscheme_hint: 'This lets you choose how URLs within your site will be formatted. If the setting you pick doesn\'t work, you can change it by editing config.php after installation.',
-      field_urlscheme_lbl_example: 'Example:',
-      field_urlscheme_opt_standard: 'Standard URLs',
-      field_urlscheme_opt_standard_hint: 'Compatible with all servers. This is the default option and should be used unless you\'re sure that one of the other options below will work.',
-      field_urlscheme_opt_shortened: 'Shortened URLs',
-      field_urlscheme_opt_shortened_hint: 'This eliminates the "?title=" portion of your URL, and instead uses a slash. This is occasionally more friendly to search engines.',
-      field_urlscheme_opt_rewrite: 'Rewritten URLs',
-      field_urlscheme_opt_rewrite_hint: 'Using this option, you can completely eliminate the "index.php" from URLs. This is the most friendly option to search engines and looks very professional, but requires support for URL rewriting on your server. Enano can configure this automatically on Apache and some IIS7 servers. Otherwise, you\'ll need a knowledge of regular expressions and it will be necessary to configure your server manually for this option to work.',
-      field_urlscheme_opt_tiny: '<span style="font-weight: bold; color: #a00;">(Experimental)</span> Tiny URLs',
-      field_urlscheme_opt_tiny_hint: 'This makes URLs very small - close to the size of Rewritten. However it doesn\'t work with every server, and we\'re still trying to experiment to find which servers it works with. If you can\'t use Rewritten URLs, you are encouraged to give this a try and let the Enano team know whether it works for you or not.',
-      btn_urlscheme_detect: 'Auto-detect the best formatting scheme',
-      
-      objective_verify: 'Verify that your site information is correct. Again, all of the above settings can be changed from the administration panel.',
-    },
-    login: {
-      modetitle: 'Admin login',
-      header_blurb: 'Next, enter your desired username and password. The account you create here will be used to administer your site.',
-      modetitle_long: 'Administration login',
-      
-      welcome_title: 'Administration account',
-      welcome_body: '<p>Now it\'s time to create the account you\'ll use to administer your site. The e-mail address you enter here will also be used for the global contact address; you can change this after installation is finished if need be.</p>
-                     <p>Do not forget the information you enter here. Otherwise you will be unable to administer your site.</p>',
-      err_verify_failure: 'One or more of the form fields contains an incorrect value. Please correct any fields that have an X next to them.',
-      err_rijndael_failed: 'Received a bad response from rijndaelEncrypt(). Shift-click "reload" or "refresh" (depending on your browser) and try again.',
-      field_username: 'Username',
-      field_password: 'Password',
-      aes_blurb: 'This will be encrypted with AES before it\'s sent to the server.',
-      field_password_confirm: '(confirm)',
-      field_email: 'E-mail',
-      
-      objective_remember: 'Remember the username and password you enter here! You will not be able to administer your site without the information you enter on this page.',
-    },
-    confirm: {
-      modetitle: 'Review',
-      modetitle_long: 'Confirm installation',
-      
-      title: 'Enano is ready to install.',
-      body: 'Almost there! You\'ve entered all the information we need for now. Click the button below to install the Enano database.',
-      info_aes_title: 'A note on AES encryption:',
-      info_aes_body: 'Enano is currently configured to use %aes_bits%-bit AES encryption. While the default value of 192 bits is perfectly acceptable for most sites, those in need of extreme security will want to change this value to 256 bits (the maximum available strength). If you need to change the cipher strength, please edit the file includes/constants.php and then <u>restart</u> this installation. Do not click Continue below until you redo the installation process up until this point, or you will experience severe problems with logging into your site.',
-      
-      msg_installing_unstable_title: 'Unstable release',
-      msg_installing_unstable_body: 'You are installing an unstable release of Enano. Except for bug reports, no support of any kind is provided for this release. If this is a Release Candidate (RC), limited support is available via the Enano forums.',
-      
-      btn_install_enano: 'Install Enano!',
-    },
-    install: {
-      modetitle: 'Install',
-      modetitle_long: 'Database installation',
-      
-      title: 'Installing Enano',
-      body: 'Please wait while Enano creates its database and initial content on your server.',
-      heading_progress: 'Installation progress',
-      
-      stg_load_title: 'Load installer files',
-      stg_load_body: 'One of the files needed for installation couldn\'t be loaded. Please check your Enano directory.',
-      stg_setpass_title: 'Retrieve administrator password',
-      stg_setpass_body: 'The administrator password couldn\'t be decrypted. This really shouldn\'t happen.',
-      stg_genaes_title: 'Generate private key',
-      stg_genaes_body: 'Couldn\'t generate a private key for the site. This really shouldn\'t happen.',
-      stg_sqlparse_title: 'Prepare database schema',
-      stg_sqlparse_body: 'Couldn\'t load or parse the schema file. This really shouldn\'t happen.',
-      stg_payload_title: 'Install database',
-      stg_payload_body: 'There was a problem with an SQL query. Details are above.',
-      stg_writeconfig_title: 'Write configuration files',
-      stg_writeconfig_body: 'Enano was unable to write the configuration file with your site\'s database credentials. This is almost always because your configuration file does not have the correct permissions. On Windows servers, you may see this message even if the check on the System Requirements page passed. Temporarily running IIS as the Administrator user may help.',
-      
-      stg_startapi_title: 'Start the Enano API',
-      stg_startapi_body: 'The Enano API could not be started. This is an error that should never occur; please contact the Enano team for support.',
-      stg_importlang_title: 'Import default language',
-      stg_importlang_body: 'Enano couldn\'t import the English language file.',
-      stg_importcontent_title: 'Import default content',
-      stg_importcontent_body: 'Enano couldn\'t import the default site content.',
-      stg_initlogs_title: 'Initialize logs',
-      stg_initlogs_body: '<b>The session manager denied the request to flush logs for the main page.</b><br />
-                           While under most circumstances you can still <a href="install.php?mode=finish">finish the installation</a>, you should be aware that some servers cannot
-                           properly set cookies due to limitations with PHP. These limitations are exposed primarily when this issue is encountered during installation. If you choose
-                           to finish the installation, please be aware that you may be unable to log into your site.',
-      
-      stg_cleanup_title: 'Clean up encryption keys',
-      stg_cleanup_body: 'There was a database error while removing the temporary encryption keys from the database. For maximum site security you should delete the config entries install_aes_key and site_aes_key manually.',
-      stg_rename_title: 'Rename configuration files',
-      stg_rename_body: 'Enano couldn\'t rename the configuration files to their correct production names. <span style="font-weight: bold; color: red;">Please perform the following rename operations and then follow the instructions to finish the installation below.</span>
-                          <ul>
-                            <li>Rename config.new.php to config.php</li>
-                            <li>Rename .htaccess.new to .htaccess (only if you selected the Rewrite URL scheme)</li>
-                          </ul>
-                        %this.finish_body%
-                        %this.finish_link_mainpage%',
-      stg_buildindex_title: 'Initialize search index',
-      stg_buildindex_body: 'Something went wrong while the page manager was attempting to build a search index.',
-    },
-    finish: {
-      modetitle: 'Finish',
-      modetitle_long: 'Complete installation',
-      
-      heading_progress: 'Performing final installation steps',
-      msg_progress: 'Enano is cleaning up and performing some final installation tasks. Please wait...',
-      msg_success_title: 'Congratulations! You\'ve finished installing Enano.',
-      msg_success_body: 'Enano has finished setting up on your server. Now you can go to your <a href="%mainpage_link%">new website</a> and start creating content!',
-      
-      body: '<h3>Wait... Now what?</h3>
-             <p>Click the link below to see the main page for your website. Where to go from here:</p>
-             <ul>
-               <li>The first thing you should do is log into your site using the Log in link on the sidebar.</li>
-               <li>Go into the Administration panel, expand General, and click General Configuration. There you will be able to configure some basic information about your site.</li>
-               <li>Visit the <a href="http://enanocms.org/Category:Plugins" onclick="window.open(this.href); return false;">Enano Plugin Gallery</a> to download and use plugins on your site.</li>
-               <li>Periodically create a backup of your database and filesystem, in case something goes wrong. This should be done at least once a week &ndash; more for wiki-based sites.</li>
-               <li>Hire some moderators, to help you keep rowdy users tame.</li>
-               <li>Tell the <a href="http://enanocms.org/Contact_us">Enano team</a> what you think.</li>
-               <li><b>Spread the word about Enano by adding a link to the Enano homepage on your sidebar!</b> You can enable this option in the General Configuration section of the administration panel.</li>
-             </ul>',
-      link_mainpage: '<a href="%mainpage_link%">Go to your website...</a>',
-    },
-    pophelp: {
-      admin_embed_php_title: 'Allow administrators to embed PHP',
-      admin_embed_php_body: '<p>This option allows you to control whether anything between the standard &lt;?php and ?&gt; tags will be treated as
-                                PHP code by Enano. If this option is enabled, and members of the Administrators group use these tags, Enano will
-                                execute that code when the page is loaded. There are obvious potential security implications here, which should
-                                be carefully considered before enabling this option.</p>
-                             <p>If you are the only administrator of this site, or if you have a high level of trust for those will be administering
-                                the site with you, you should enable this to allow extreme customization of pages.</p>
-                             <p>Leave this option off if you are at all concerned about security – if your account is compromised and PHP embedding
-                                is enabled, an attacker can run arbitrary code on your server! Enabling this will also allow administrators to
-                                embed Javascript and arbitrary HTML and CSS.</p>
-                             <p>If you don\'t have experience coding in PHP, you can safely disable this option. You may change this at any time
-                                using the ACL editor by selecting the Administrators group and This Entire Website under the scope selection.</p>',
-      url_schemes_title: 'URL schemes',
-      url_schemes_body: '<p>The URL scheme allows you to decide how the URLs to your Enano pages will look.</p>
-                         <p>The first option (Standard URLs) works on any web server. You should select it if your server doesn\'t run Apache, or
-                            if you are at all unsure of your server\'s configuration. With this scheme, URLs at your site will look like <tt>
-                            http://yoursite.com/path-to-enano/index.php?title=Main_Page</tt>.</p>
-                         <p>The second option, Small URLs, will be selected by default if Enano detects Apache. Small URLs are more friendly towards
-                            search engines, but they don\'t work on very many non-Apache servers, or if PHP is set up through CGI on your server. Many
-                            free and low-cost web hosts will configure PHP through CGI in order to keep your user account as the owner of any files that
-                            Enano generates. With this scheme, URLs at your site will look like <tt>http://yoursite.com/path-to-enano/index.php/Main_Page</tt>.
-                            </p>
-                         <p>The last option, Tiny URLs, is the most friendly URL scheme for search engines, because your URLs won\'t have any special characters
-                            at all in them. However, this only works if your webhost has configured Apache with support for mod_rewrite. Most of the time if your
-                            host supports this you will see a listing for it in their feature matrix. None of the popular Linux distributions (such as Ubuntu,
-                            Debian, Red Hat Enterprise Linux&trade;, Fedora, openSUSE&trade;, or CentOS) come with mod_rewrite enabled, so if you run a
-                            home-brew server, you should consult your distribution\'s documentation for enabling mod_rewrite before selecting this option.
-                            With this scheme, URLs at your site will look like <tt>http://yoursite.com/path-to-enano/Main_Page</tt>.</p>
-                            </p>',
-      btn_close_window: 'Close window',
-    },
-    upgrade: {
-      system_title: 'Enano upgrade tool',
-      stg_welcome: 'Welcome',
-      stg_login: 'Login',
-      stg_confirm: 'Confirmation',
-      stg_upgrade: 'Perform upgrade',
-      stg_finish: 'Finish',
-      
-      welcome_banshee_heading: 'Welcome to Enano %enano_version%.',
-      welcome_banshee_para1: 'Enano 1.2 features support for multiple languages, better security, faster page loading, more tools, a revamped page editor, and much more. This migration tool will automatically make the changes to your database required for Enano 1.2.',
-      welcome_banshee_para2: 'The migration tool has been tested under many different installation scenarios, but isn\'t fully guaranteed to work. The Enano team can\'t be responsible if the migration fails. If you encounted problems, please be sure to post on the <a href="http://forum.enanocms.org/">Enano forums</a>.',
-      welcome_caoineag_heading: 'Welcome to the Enano upgrade tool.',
-      welcome_caoineag_para1: 'To use this version of Enano, some parts of your database need to be upgraded. This tool will automatically perform the upgrade for you.',
-      welcome_btn_continue: 'Start upgrade',
-      
-      login_msg_auth_needed_title: 'Authentication needed',
-      login_msg_auth_needed_body_level1: 'To continue, you need to log in. Please enter an administrator username and password below.',
-      login_msg_auth_needed_body_level2: 'To confirm the upgrade, you need to re-enter your login information. Please enter your username and password below.',
-      login_msg_local_auth: '<b>Note:</b> The upgrade tool doesn\'t recognize any authentication plugins. Even if you use authentication plugins in Enano, you need to enter a local administrator username and password to use this upgrade tool.',
-      login_btn_login: 'Log in',
-      login_err_failed: 'The following error occurred during the login process: %error_code%.',
-      
-      confirm_title: 'Confirm upgrade',
-      confirm_body: 'You are about to upgrade to Enano version <b>%enano_version%</b>. You should make sure that you\'ve done the following before you continue:',
-      confirm_objective_backup_fs: 'Back up Enano installation directory (<b>%dir%</b>)',
-      confirm_objective_backup_db: 'Back up Enano database, including non-Enano tables if any (<b>%dbname%</b>)',
-      confirm_warning_langimport: '<b>Warning!</b> This release of Enano has some changed strings. If you\'ve customized any language strings, those changes will be lost.',
-      confirm_btn_upgrade: 'Pimp my Enano!',
-      
-      msg_schema_complete_title: 'Database upgrades complete',
-      msg_schema_complete_body: 'You\'re past the hard part - all of the modifications to your database were successful. We need to perform a few final steps to finish the upgrade; just click the button below to finish everything up.',
-      btn_continue: 'Finish upgrade',
-      
-      err_current_title: 'Already running current version',
-      err_current_body: 'No database upgrades are needed right now, you\'re already running the Enano version set in this installer. Try <a href="%mainpage_link%">heading back to your site</a>. Think there\'s a mistake somewhere? <a href="http://forum.enanocms.org/">Let the Enano team know about it</a>.',
-      err_current_body_para2: 'You might also want to use the administration panel to check for updates, or <a href="http://enanocms.org/download">download the latest release</a> of Enano.',
-      err_post_not_available: 'You\'re trying to run the post-upgrade process, but your site isn\'t in the correct state for this.',
-      
-      post_status_title: 'Finishing upgrade',
-      post_status_body: 'Enano is cleaning up some data and finalizing the upgrade.',
-      post_status_finish_title: 'All done!',
-      post_status_finish_body: 'That\'s it - Enano has been upgraded. You should <a href="%mainpage_link%">go back to your site</a> now and make sure that everything works right. If you find a problem, be sure to report it to the <a href="http://forum.enanocms.org/">Enano development team</a>. (Make sure you disable any plugins first, since we can\'t easily tell if your problem is caused by the Enano core or by a plugin, unless all of your non-system plugins are disabled.)',
-      
-      stg_flushcache_title: 'Flush caches',
-      stg_flushcache_body: 'The upgrader failed to delete some cached data. You may experience some problems with file corruption or badly drawn pages until the caches expire, which is often no longer than 20 minutes.',
-      stg_setversion_title: 'Set Enano version and log upgrade',
-      stg_setversion_body: 'There was a problem finalizing the upgrade and inserting logs. You really shouldn\'t ever see this message, but calling setConfig(\'enano_version\', installer_enano_version()) should get you rolling again since everything else is probably done by now.',
-    },
-    cli: {
-      welcome_line1: '<c 1>Welcome to the <c 34>Enano</c></c> CMS<c 1> installation wizard.</c>\n',
-      welcome_line2: 'Installing Enano version <c 1>%enano_version%</c> on PHP <c 1>%php_version%</c>\n',
-      
-      prompt_driver: 'Database type (mysql or postgresql)',
-      prompt_dbhost: 'Hostname of database server',
-      prompt_dbport: 'Database server port',
-      prompt_dbuser: 'Database username',
-      prompt_dbpasswd: 'Database password',
-      prompt_dbname: 'Database name',
-      prompt_tblpfx: 'Table prefix',
-      prompt_user: 'Enano administrator username',
-      prompt_pass: 'Enano administrator password',
-      prompt_email: 'Contact e-mail',
-      prompt_sitename: 'Site name',
-      prompt_sitedesc: 'Site description',
-      prompt_copyright: 'Copyright notice',
-      prompt_urlscheme: 'URL scheme (standard, short, rewrite, or tiny)',
-      prompt_scriptpath: 'Now we need the path to Enano, relative to your website\'s document root,\nwithout a trailing slash. This should be based on the URL your site will\nuse. For example, if your site is http://yoursite.com/enano/, use the value\n"/enano" here. Leave this blank if Enano is in your document root, e.g.\nhttp://yoursite.com/index.php?title=Enano_page.\nSite URL',
-      prompt_confirm: 'Enter again to confirm',
-      msg_echo_warning: '(WARNING, will be echoed)',
-      
-      default_site_name: 'Enano site',
-      default_site_desc: 'My first Enano site',
-      default_copyright: '&copy; %year%',
-      
-      msg_testing_db: 'Testing database connectivity...',
-      
-      stage_sysreqs: '<c 34;1>Checking system.</c>\n',
-      test_pass: '<c 1;32>PASS</c>',
-      test_vwarn: '<c 1;33>WARNING</c>',
-      test_warn: '<c 0;31>FAIL</c><c 1> (optional)</c>',
-      test_fail: '<c 1;31>FAIL</c>',
-      
-      msg_test_warnings: '<c 1;33>The following warnings were generated during the check:</c>',
-      msg_tests_passed: '<c 32><c 1>All tests passed.</c> Installing Enano.</c>\n',
-      msg_installing_db_stage1: '<c 1>Installing database, stage 1...</c>',
-      msg_parsing_schema: '<c 1>Preparing installation payload...</c>',
-      msg_installing_db_stage2: '<c 1>Installing database, stage 2...</c>',
-      msg_writing_config: '<c 1>Generating config file...</c>',
-      msg_starting_api: '<c 1>Starting up the Enano API...</c>',
-      msg_importing_language: '<c 1>Importing default language...</c>',
-      msg_importing_content: '<c 1>Installing default content...</c>',
-      msg_initting_logs: '<c 1>Initializing logs...</c>',
-      msg_cleaning_up: '<c 1>Removing temporary data...</c>',
-      msg_initting_index: '<c 1>Initializing search index...</c>',
-      msg_renaming_config: '<c 1>Renaming config file...</c>',
-      
-      msg_ok: '<c 1;32>OK</c>',
-      msg_install_success: '<c 1;32>Congratulations!</c> <c 1>Enano was successfully installed.</c>\n<c 34>Now, navigate your browser to your Enano installation to make sure everything\nworks right. If you encounter problems, contact the Enano Team for support at\nhttp://forum.enanocms.org or in #enano on irc.freenode.net.</c> <c 34;1>Be sure to\nmention that you used the CLI installer.</c>\n',
-      
-      err_pass_no_match: '  <c 1>Passwords do not match.</c>\n',
-      err_db_connect_fail: 'Could not connect to the database',
-      err_sysreqs_fail: 'One or more system requirement checks has failed.',
-      err_no_drivers: 'Support for at least one database driver is required.',
-      err_schema_load: 'Could not load the database schema file.',
-      err_db_query: 'A database error occurred; see the message above for details.',
-      err_query_sanity_failed: 'The sanity check on a database query failed.',
-      err_writing_config: 'Could not write the configuration file.',
-      err_importing_language: 'Could not import the default language.',
-      err_initting_logs: 'Could not initialize Enano\'s logs.',
-      err_cleaning_up: 'Could not clean up the temporary data.',
-      err_initting_index: 'Could not initialize the search index.',
-      err_renaming_config: 'Could not rename config.new.php to config.php; please do this manually.',
-    }
-  }
+	categories: [
+		'meta', 'language', 'welcome', 'license', 'sysreqs', 'database', 'dbmysql', 'dbpgsql', 'website', 'login', 'confirm', 'install', 'finish', 'pophelp', 'upgrade', 'cli'
+	],
+	strings: {
+		meta: {
+			site_name: 'Enano installation',
+			site_desc: 'Install Enano on your server.',
+			enano_copyright: 'Enano and its various components, related documentation, and artwork are copyright &copy; 2006 Dan Fuhry.<br />This program is Free Software; see the file "GPL" included with this package for details.',
+			sidebar_heading: 'Installation progress',
+			btn_article: 'installation page',
+			btn_continue: 'Continue',
+			lbl_before_continue: 'Before continuing:',
+			step: 'Step %step%: %title%',
+			
+			msg_err_verification: 'One or more of the form fields is incorrect. Please correct any information in the form that has an "X" next to it.',
+			
+			msg_err_stagefailed_title: 'Enano installation failed.',
+			msg_err_stagefailed_body: 'When you have corrected the error, click the button below to attempt to continue the installation.',
+			msg_err_stagefailed_mysqlerror: 'The error returned from MySQL was:',
+			btn_retry_installation: 'Retry installation',
+		},
+		language: {
+			modetitle: 'Language',
+		},
+		welcome: {
+			modetitle: 'Welcome',
+			heading: 'Welcome to Enano',
+			version: 'version',
+			branch_stable: 'stable',
+			branch_unstable: 'unstable',
+			aka: 'also affectionately known as "%codename%" <tt>:)</tt>',
+			btn_start: 'Start installation',
+		},
+		license: {
+			modetitle: 'License',
+			modetitle_long: 'License agreement',
+			heading: 'Welcome to the Enano installer.',
+			blurb_thankyou: 'Thank you for choosing Enano as your CMS. You\'ve selected the finest in design, the strongest in security, and the latest in Web 2.0 toys. Trust us, you\'ll like it.',
+			blurb_pleaseread: 'To get started, please read and accept the following license agreement. You\'ve probably seen it before.',
+			info_unstable_title: 'Notice for prerelease versions',
+			info_unstable_body: 'This version of Enano is designed only for testing and evaluation purposes. <b>It is not yet completely stable, and should not be used on production websites.</b> As with any Enano version, Dan Fuhry and the Enano team cannot be responsible for any damage, physical or otherwise, to any property as a result of the use of Enano. While security is a number one priority, sometimes things slip through.',
+			section_gpl_heading: 'Lawyer-readable version',
+			btn_i_agree: 'I agree to the license terms',
+			objective_ensure_agree: 'Ensure that you agree with the terms of the license',
+			objective_have_db_info: 'Have your database host, name, username, and password available',
+			gpl_blurb_inenglish: 'You may view a copy of the GNU General Public License in English in the file GPL-EN included with this package.',
+		},
+		sysreqs: {
+			modetitle: 'Requirements',
+			modetitle_long: 'Server requirements',
+			heading: 'Checking your server',
+			blurb: 'Enano has several requirements that must be met before it can be installed. If all is good then note any warnings and click Continue below.',
+			btn_refresh: 'Recheck server',
+			
+			req_supported: 'Supported',
+			req_notfound: 'Not found',
+			req_found: 'Found',
+			req_enabled: 'Enabled',
+			req_disabled: 'Disabled',
+			req_writable: 'Writable',
+			req_unwritable: 'Unwritable',
+			req_gmp: 'GNU Multi-Precision (GMP)',
+			req_bigint: 'Big_Int',
+			req_bcmath: 'BCMath',
+			
+			heading_serverenv: 'Server environment',
+			heading_dbms: 'Database servers',
+			heading_files: 'Writable files',
+			heading_images: 'Image manipulation',
+			
+			req_apache: 'Apache web server',
+			req_php: 'PHP version',
+			req_mysql: 'MySQL database support',
+			req_postgresql: 'PostgreSQL database support',
+			req_safemode: 'Safe Mode',
+			req_uploads: 'PHP file upload support',
+			req_ctype: 'PHP ctype_* validation functions',
+			req_crypto: 'Arbitrary precision (cryptographic) math',
+			req_config_writable: 'Configuration file: config.new.php',
+			req_htaccess_writable: 'Apache rewrite rules: .htaccess.new',
+			req_files_writable: 'File storage directory: files/',
+			req_cache_writable: 'Cache directory: cache/',
+			req_gd2: 'GD2 library',
+			req_imagemagick: 'ImageMagick',
+			
+			req_hint_htaccess_writable: 'Only needs to be writable if you plan to use Rewritten URLs.',
+			req_hint_gd2: 'Used for generating visual confirmation and resizing uploaded images',
+			req_hint_imagemagick: 'Faster alternative for resizing uploaded images',
+			
+			req_help_apache: 'Apache is the best server for Enano because it provides features that Enano can use, primarily support for Rewritten URLs.',
+			req_help_php: 'Enano requires PHP version 5.0.0 or later, and runs best under PHP 5.2.0 or later. (You are running PHP %php_version%).',
+			req_help_safemode: 'Safe Mode interferes with Enano\'s ability to operate properly. Thus, installing Enano with Safe Mode enabled is currently not supported.',
+			req_help_uploads: 'PHP file upload support is a prerequisite for uploading files via Enano\'s web interface.',
+			req_help_writable: 'Certain files need to be writable for installation and certain features to work properly. Use your FTP client\'s "CHMOD" feature to set numeric permissions on the items listed above: 666 for files, and 777 for directories. It\'s safest to CHMOD config.php to 444 once installation is complete.',
+			req_help_gd2: 'Without GD, Enano has to use a visual confirmation engine that generates weaker images that are easier to break.',
+			req_help_imagemagick: 'Without ImageMagick, Enano will use GD (if it is available) to scale images down. This is slightly slower.',
+			req_help_crypto_none: 'Your server has no support for arbitrary-precision math, which is required for encrypted logon. Your username and password will not be transmitted securely until one of the following PHP extensions is installed: GMP, Big_Int, or BCMath.',
+			req_help_crypto_bcmath: 'Your server supports arbitrary-precision math, but only through PHP\'s BCMath extension which is extremely slow. Expect the process of logging in to take 6-10 seconds.',
+			
+			err_no_dbms_title: 'No database backends are available.',
+			err_no_dbms_body: 'Enano requires at least one database backend to install. Please consult with your hosting provider to obtain help with this situation. If you are running a VPS or dedicated server, install the php-mysql or php-pgsql (on Red Hat&reg;-based distributions) or php5-mysql or php5-pgsql (on Debian-based distributions) packages. For Windows servers, ensure that the php_mysql or php_pgsql extension is enabled in php.ini.',
+			
+			summary_pass_title: 'Congratulations! Enano can run on this server.',
+			summary_pass_body: 'The system requirement check was successful and Enano has verified that it can run on this server with all available features. Below is a summary of what was checked. Click the Continue button below to proceed with installation.',
+			
+			summary_warn_title: 'Warnings encountered during server check',
+			summary_warn_body: 'Since some server features that Enano uses aren\'t available, Enano has turned its support for these features off. The warnings below provide information on how you can fix this.',
+			
+			summary_fail_title: 'Installation requirements not met',
+			summary_fail_body: 'As a precaution, Enano will not install until the above requirements have been met. Contact your server administrator or hosting company for support. If you still need help, please use the <a href="http://forum.enanocms.org/" onclick="window.open(this.href);">Enano support forums</a>.',
+			
+			objective_scalebacks: 'Review the list above to ensure that you are satisfied with any of Enano\'s workarounds for your server. If you need a particular feature and that feature is listed as disabled above, you should take the opportunity now to correct the problem.'
+		},
+		database: {
+			modetitle: 'Database',
+			modetitle_long: 'Database information',
+			heading_optionalinfo: 'Optional information',
+			
+			driver_heading: 'Choose a database driver',
+			driver_intro: 'The next step is to choose the database driver that Enano will use. In most cases this is MySQL, but there are certain advantages to PostgreSQL, which is made available only experimentally.',
+			driver_msg_virt_appliance: '<b>You\'re using the Enano virtual appliance.</b><br />Unless you configured the appliance manually, PostgreSQL support is not available. In 99% of cases you\'ll want to click MySQL below.',
+			driver_err_no_mysql: 'You don\'t have the MySQL PHP extension installed.',
+			driver_err_no_pgsql: 'You don\'t have the PostgreSQL PHP extensnion installed.',
+			driver_mysql: 'MySQL',
+			driver_mysql_intro: 'Click this button to use MySQL as the database backend for your site. Most web hosts support MySQL, and if you have administrative access to your MySQL server, you can create a new database and user during this installation process if you haven\'t done so already.',
+			driver_pgsql: 'PostgreSQL',
+			driver_pgsql_intro: 'Click this button to use PostgreSQL as the database backend for your site. While not as widely supported, PostgreSQL has more liberal licensing conditions and when properly configured is faster than MySQL. Some plugins may not work with the PostgreSQL driver.',
+			
+			objective_test: 'Check your MySQL connection using the "Test Connection" button.',
+			objective_uncrypt: 'Be aware that your database information will be transmitted unencrypted several times.',
+			
+			// database_post module
+			btn_go_back: 'Go back',
+			msg_success_title: 'Connection successful',
+			msg_success_body: 'The database has been contacted and initial tables created successfully. Redirecting...',
+			msg_success_redirect: 'Click if you\'re not redirected within 2 seconds',
+			
+			msg_post_fail_title: 'Database connection failed',
+			msg_post_fail_body: 'The installer couldn\'t connect to the database because something went wrong while the connection attempt was being made. Please press your browser\'s back button and correct your database information.',
+			msg_post_fail_desc: 'Error description:',
+			
+			msg_sql_fail_title: 'Database operation failed',
+			msg_sql_fail_body: 'The installer couldn\'t create one of the tables used for installation.',
+		},
+		dbmysql: {
+			msg_err_mysql_connect: '<b>Error:</b> The database server "%db_host%" couldn\'t be contacted.<br />%mysql_error%',
+			msg_err_mysql_auth: '<b>Error:</b> Access to MySQL under the specified credentials was denied.<br />%mysql_error%',
+			msg_err_mysql_dbperm: '<b>Error:</b> Access to the specified database using those login credentials was denied.<br />%mysql_error%',
+			msg_err_mysql_dbexist: '<b>Error:</b> The specified database does not exist<br />%mysql_error%',
+			msg_err_mysql_version: '<b>Error:</b> Your version of MySQL (%mysql_version%) is older than 4.1.17. Enano will still work, but there is a known bug with the comment system and MySQL 4.1.11 that involves some comments not being displayed, due to an issue with the PHP function mysql_fetch_row().',
+			
+			msg_warn_creating_db: '<b>Warning:</b> The database you specified does not exist. It will be created during installation.',
+			msg_warn_creating_user: '<b>Warning:</b> The specified regular user does not exist or the password is incorrect. The user will be created during installation. If the user already exists, the password will be reset.',
+			msg_warn_mysql_version: 'The MySQL version that your server is running could not be determined.',
+			
+			msg_info_mysql_good: 'Your version of MySQL meets Enano requirements.',
+			msg_test_success: 'All checks passed! You can use this database configuration with Enano.',
+			
+			blurb_needdb: 'Now we need some information that will allow Enano to contact your database server. Enano uses MySQL as a data storage backend, and we need to have access to a MySQL server in order to continue.',
+			blurb_howtomysql: 'If you do not have access to a MySQL server, and you are using your own server, you can download MySQL for free from <a href="http://www.mysql.com/">MySQL.com</a>. <b>Please note that, like Enano, MySQL is licensed under the GNU GPL.</b> If you need to modify MySQL and then distribute your modifications, you must either distribute them under the terms of the GPL or purchase a proprietary license.',
+			
+			vm_login_info: '<b>MySQL login information for this virtual appliance:</b><br /><br />Database hostname: %host%<br />Database login: username "%user%", password: "%pass%" (without quotes)<br />Database name: %name%',
+			
+			table_title: 'Database information',
+			
+			field_hostname_title: 'Database hostname',
+			field_hostname_body: 'This is the hostname (or sometimes the IP address) of your MySQL server. In many cases, this is "localhost". To connect through a Unix socket, use the format ":/path/to/mysql.sock".',
+			field_port_title: 'Database port',
+			field_port_body: 'The TCP port Enano will use to connect to MySQL. This is 3306 on the vast majority of servers. This isn\'t relevant if you use a socket file.',
+			field_dbname_title: 'Database name',
+			field_dbname_body: 'The name of the actual database. If you don\'t already have a database, you can create one here, if you have the username and password of a MySQL user with administrative rights.',
+			field_dbauth_title: 'Database login',
+			field_dbauth_body: 'These fields should be the username and password of a user with "select", "insert", "update", "delete", "create table", and "replace" privileges for your database.',
+			field_tableprefix_title: 'Table prefix',
+			field_tableprefix_body: 'The value that you enter here will be added to the beginning of the name of each Enano table. You may use lowercase letters (a-z), numbers (0-9), and underscores (_).',
+			field_rootauth_title: 'Database administrative login',
+			field_rootauth_body: 'If the MySQL database or username that you entered above does not exist yet, you can create them here, assuming that you have the login information for an administrative user (such as root). Leave these fields blank unless you need to use them.',
+			field_mysqlversion_title: 'MySQL version',
+			field_mysqlversion_blurb_willbechecked: 'MySQL version information will be checked when you click "Test Connection".',
+			field_droptables_title: 'Delete existing tables?',
+			field_droptables_body: 'If this option is checked, all the tables that will be used by Enano will be dropped (deleted) before the schema is executed. Do NOT use this option unless specifically instructed to.',
+			field_droptables_lbl: 'Drop existing tables',
+			
+			btn_testconnection: 'Test connection',
+		},
+		dbpgsql: {
+			msg_err_connect: 'There was a problem connecting to PostgreSQL. Please check your connection information above.',
+			msg_err_version: '<b>Error:</b> Your version of PostgreSQL (%pg_version%) is older than 8.2.5. Enano cannot be installed.',
+			
+			msg_warn_pg_version: 'The PostgreSQL version that your server is running could not be determined.',
+			msg_err_auth: 'Access to the database was denied. Ensure that your database exists and that your username and password are correct.',
+			
+			msg_info_version_good: 'Your version of PostgreSQL meets Enano requirements.',
+			msg_test_success: 'All checks passed! You can use this database configuration with Enano.',
+			
+			blurb_needdb: 'Now we need some information that will allow Enano to contact your database server. Enano uses PostgreSQL as a data storage backend, and we need to have access to a PostgreSQL server in order to continue.',
+			blurb_howtomysql: 'If you do not have access to a PostgreSQL server, and you are using your own server, you can download PostgreSQL for free from <a href="http://www.mysql.com/">PostgreSQL.com</a>. <b>Please note that, like Enano, PostgreSQL is licensed under the GNU GPL.</b> If you need to modify PostgreSQL and then distribute your modifications, you must either distribute them under the terms of the GPL or purchase a proprietary license.',
+			
+			vm_login_info: '<b>PostgreSQL login information for this virtual appliance:</b><br /><br />Database hostname: %host%<br />Database login: username "%user%", password: "%pass%" (without quotes)<br />Database name: %name%',
+			
+			table_title: 'Database information',
+			
+			field_hostname_title: 'Database hostname',
+			field_hostname_body: 'This is the hostname (or sometimes the IP address) of your PostgreSQL server. In many cases, this is "localhost".',
+			field_port_title: 'Database port',
+			field_port_body: 'The TCP port Enano will use to connect to PostgreSQL. This is 5432 on the vast majority of servers.',
+			field_dbname_title: 'Database name',
+			field_dbname_body: 'The name of the actual database. If you don\'t already have a database, you can create one here, if you have the username and password of a PostgreSQL user with administrative rights.',
+			field_dbauth_title: 'Database login',
+			field_dbauth_body: 'These fields should be the username and password of a user with "select", "insert", "update", "delete", "create table", and "replace" privileges for your database.',
+			field_tableprefix_title: 'Table prefix',
+			field_tableprefix_body: 'The value that you enter here will be added to the beginning of the name of each Enano table. You may use lowercase letters (a-z), numbers (0-9), and underscores (_).',
+			field_rootauth_title: 'Database administrative login',
+			field_rootauth_body: 'If the PostgreSQL database or username that you entered above does not exist yet, you can create them here, assuming that you have the login information for an administrative user (such as root). Leave these fields blank unless you need to use them.',
+			field_pgsqlversion_title: 'PostgreSQL version',
+			field_pgsqlversion_blurb_willbechecked: 'PostgreSQL version information will be checked when you click "Test Connection".',
+			field_droptables_title: 'Delete existing tables?',
+			field_droptables_body: 'If this option is checked, all the tables that will be used by Enano will be dropped (deleted) before the schema is executed. Do NOT use this option unless specifically instructed to.',
+			field_droptables_lbl: 'Drop existing tables',
+			
+			btn_testconnection: 'Test connection',
+		},
+		website: {
+			modetitle: 'Site info',
+			modetitle_long: 'Website information',
+			header_blurb: 'The next step is to enter some information about your website. You can always change this information later, using the administration panel.',
+			
+			msg_ajax_test_fail_title: 'All tests failed',
+			msg_ajax_test_fail_body: 'None of the URL handling tests worked; you may have problems using Enano on your server.',
+			msg_bestmethod_rewrite: 'The installer has detected that using rewritten URLs is the best level that will work.',
+			msg_bestmethod_shortened: 'The installer has detected that using shortened URLs is the best level that will work.',
+			msg_bestmethod_tiny: 'The installer has detected that using tiny URLs is the best level that will work.',
+			msg_bestmethod_standard: 'The installer has detected that using standard URLs is the only level that will work.',
+			msg_modrewrite_enabled: 'Your copy of Enano has the Windows mod_rewrite workaround patch enabled.',
+			msg_modrewrite_necessary: 'If you don\'t plan to use the Rewritten URLs option below, you should go back and re-download Enano without this workaround enabled. The mod_rewrite issues Enano faces under Windows are caused by a security change in Apache 2.2 that is not present in Apache 2.0. <a href="http://enanocms.org/windows-patch" onclick="window.open(this.href); return false;">Learn more</a>',
+			msg_modrewrite_unnecessary: 'The Enano installer has detected that you\'re probably not running Apache 2.2 under Windows. You don\'t need this workaround unless you\'re using this specific software stack. Unless you\'re sure that you are running Apache 2.2 under Windows on your server, it is recommended that you disable the <tt>WINDOWS_MOD_REWRITE_WORKAROUNDS</tt> constant in includes/constants.php. <a href="http://enanocms.org/windows-patch" onclick="window.open(this.href); return false;">Learn more</a>',
+			msg_modrewrite_disabled: 'Your server is running Apache 2.2 on Windows, but the Windows mod_rewrite workaround patch is disabled.',
+			msg_modrewrite_maybeneeded: 'Do not use Rewritten URLs unless you re-download Enano with the Windows Patch enabled or uncomment the <tt>WINDOWS_MOD_REWRITE_WORKAROUNDS</tt> line in <tt>includes/constants.php</tt>. Otherwise you may have trouble accessing login and administration pages due to a bug in Apache. <a href="http://enanocms.org/windows-patch" onclick="window.open(this.href); return false;">Learn more</a>',
+			
+			field_name: 'Pick a name',
+			field_name_hint: 'Now for the fun part - it\'s time to name your website. Try to pick something that doesn\'t include any special characters, since this can make project-page URLs look botched.',
+			field_desc: 'Enter a short description',
+			field_desc_hint: 'Here you should enter a very short description of your site. Sometimes this is a slogan or, depending on the theme you\'ve chosen, a set of keywords that can go into a META description tag.',
+			field_copyright: 'Copyright info',
+			field_copyright_hint: 'The text you enter here will be shown at the bottom of most pages. Typically this is where a copyright notice would go. Keep it short and sweet; you can use <a href="http://docs.enanocms.org/Help:3.1">internal links</a> to link to project pages you\'ll create later.',
+			field_startwith: 'Start with:',
+			field_startwith_blank: 'A blank site with only a main page',
+			field_startwith_blank_hint: 'This is best if you\'ve worked with Enano or other wiki software before and for production installations. It installs one page which you can edit to immediately get started on your site.',
+			field_startwith_tutorial: 'The Enano tutorial suite',
+			field_startwith_tutorial_hint: 'This option installs several tutorial pages that help you to get started quickly if you\'re new to Enano.',
+			field_urlscheme: 'URL formatting',
+			field_urlscheme_hint: 'This lets you choose how URLs within your site will be formatted. If the setting you pick doesn\'t work, you can change it by editing config.php after installation.',
+			field_urlscheme_lbl_example: 'Example:',
+			field_urlscheme_opt_standard: 'Standard URLs',
+			field_urlscheme_opt_standard_hint: 'Compatible with all servers. This is the default option and should be used unless you\'re sure that one of the other options below will work.',
+			field_urlscheme_opt_shortened: 'Shortened URLs',
+			field_urlscheme_opt_shortened_hint: 'This eliminates the "?title=" portion of your URL, and instead uses a slash. This is occasionally more friendly to search engines.',
+			field_urlscheme_opt_rewrite: 'Rewritten URLs',
+			field_urlscheme_opt_rewrite_hint: 'Using this option, you can completely eliminate the "index.php" from URLs. This is the most friendly option to search engines and looks very professional, but requires support for URL rewriting on your server. Enano can configure this automatically on Apache and some IIS7 servers. Otherwise, you\'ll need a knowledge of regular expressions and it will be necessary to configure your server manually for this option to work.',
+			field_urlscheme_opt_tiny: '<span style="font-weight: bold; color: #a00;">(Experimental)</span> Tiny URLs',
+			field_urlscheme_opt_tiny_hint: 'This makes URLs very small - close to the size of Rewritten. However it doesn\'t work with every server, and we\'re still trying to experiment to find which servers it works with. If you can\'t use Rewritten URLs, you are encouraged to give this a try and let the Enano team know whether it works for you or not.',
+			btn_urlscheme_detect: 'Auto-detect the best formatting scheme',
+			
+			objective_verify: 'Verify that your site information is correct. Again, all of the above settings can be changed from the administration panel.',
+		},
+		login: {
+			modetitle: 'Admin login',
+			header_blurb: 'Next, enter your desired username and password. The account you create here will be used to administer your site.',
+			modetitle_long: 'Administration login',
+			
+			welcome_title: 'Administration account',
+			welcome_body: '<p>Now it\'s time to create the account you\'ll use to administer your site. The e-mail address you enter here will also be used for the global contact address; you can change this after installation is finished if need be.</p>
+ 										<p>Do not forget the information you enter here. Otherwise you will be unable to administer your site.</p>',
+			err_verify_failure: 'One or more of the form fields contains an incorrect value. Please correct any fields that have an X next to them.',
+			err_rijndael_failed: 'Received a bad response from rijndaelEncrypt(). Shift-click "reload" or "refresh" (depending on your browser) and try again.',
+			field_username: 'Username',
+			field_password: 'Password',
+			aes_blurb: 'This will be encrypted with AES before it\'s sent to the server.',
+			field_password_confirm: '(confirm)',
+			field_email: 'E-mail',
+			
+			objective_remember: 'Remember the username and password you enter here! You will not be able to administer your site without the information you enter on this page.',
+		},
+		confirm: {
+			modetitle: 'Review',
+			modetitle_long: 'Confirm installation',
+			
+			title: 'Enano is ready to install.',
+			body: 'Almost there! You\'ve entered all the information we need for now. Click the button below to install the Enano database.',
+			info_aes_title: 'A note on AES encryption:',
+			info_aes_body: 'Enano is currently configured to use %aes_bits%-bit AES encryption. While the default value of 192 bits is perfectly acceptable for most sites, those in need of extreme security will want to change this value to 256 bits (the maximum available strength). If you need to change the cipher strength, please edit the file includes/constants.php and then <u>restart</u> this installation. Do not click Continue below until you redo the installation process up until this point, or you will experience severe problems with logging into your site.',
+			
+			msg_installing_unstable_title: 'Unstable release',
+			msg_installing_unstable_body: 'You are installing an unstable release of Enano. Except for bug reports, no support of any kind is provided for this release. If this is a Release Candidate (RC), limited support is available via the Enano forums.',
+			
+			btn_install_enano: 'Install Enano!',
+		},
+		install: {
+			modetitle: 'Install',
+			modetitle_long: 'Database installation',
+			
+			title: 'Installing Enano',
+			body: 'Please wait while Enano creates its database and initial content on your server.',
+			heading_progress: 'Installation progress',
+			
+			stg_load_title: 'Load installer files',
+			stg_load_body: 'One of the files needed for installation couldn\'t be loaded. Please check your Enano directory.',
+			stg_setpass_title: 'Retrieve administrator password',
+			stg_setpass_body: 'The administrator password couldn\'t be decrypted. This really shouldn\'t happen.',
+			stg_genaes_title: 'Generate private key',
+			stg_genaes_body: 'Couldn\'t generate a private key for the site. This really shouldn\'t happen.',
+			stg_sqlparse_title: 'Prepare database schema',
+			stg_sqlparse_body: 'Couldn\'t load or parse the schema file. This really shouldn\'t happen.',
+			stg_payload_title: 'Install database',
+			stg_payload_body: 'There was a problem with an SQL query. Details are above.',
+			stg_writeconfig_title: 'Write configuration files',
+			stg_writeconfig_body: 'Enano was unable to write the configuration file with your site\'s database credentials. This is almost always because your configuration file does not have the correct permissions. On Windows servers, you may see this message even if the check on the System Requirements page passed. Temporarily running IIS as the Administrator user may help.',
+			
+			stg_startapi_title: 'Start the Enano API',
+			stg_startapi_body: 'The Enano API could not be started. This is an error that should never occur; please contact the Enano team for support.',
+			stg_importlang_title: 'Import default language',
+			stg_importlang_body: 'Enano couldn\'t import the English language file.',
+			stg_importcontent_title: 'Import default content',
+			stg_importcontent_body: 'Enano couldn\'t import the default site content.',
+			stg_initlogs_title: 'Initialize logs',
+			stg_initlogs_body: '<b>The session manager denied the request to flush logs for the main page.</b><br />
+ 													While under most circumstances you can still <a href="install.php?mode=finish">finish the installation</a>, you should be aware that some servers cannot
+ 													properly set cookies due to limitations with PHP. These limitations are exposed primarily when this issue is encountered during installation. If you choose
+ 													to finish the installation, please be aware that you may be unable to log into your site.',
+			
+			stg_cleanup_title: 'Clean up encryption keys',
+			stg_cleanup_body: 'There was a database error while removing the temporary encryption keys from the database. For maximum site security you should delete the config entries install_aes_key and site_aes_key manually.',
+			stg_rename_title: 'Rename configuration files',
+			stg_rename_body: 'Enano couldn\'t rename the configuration files to their correct production names. <span style="font-weight: bold; color: red;">Please perform the following rename operations and then follow the instructions to finish the installation below.</span>
+													<ul>
+														<li>Rename config.new.php to config.php</li>
+														<li>Rename .htaccess.new to .htaccess (only if you selected the Rewrite URL scheme)</li>
+													</ul>
+												%this.finish_body%
+												%this.finish_link_mainpage%',
+			stg_buildindex_title: 'Initialize search index',
+			stg_buildindex_body: 'Something went wrong while the page manager was attempting to build a search index.',
+		},
+		finish: {
+			modetitle: 'Finish',
+			modetitle_long: 'Complete installation',
+			
+			heading_progress: 'Performing final installation steps',
+			msg_progress: 'Enano is cleaning up and performing some final installation tasks. Please wait...',
+			msg_success_title: 'Congratulations! You\'ve finished installing Enano.',
+			msg_success_body: 'Enano has finished setting up on your server. Now you can go to your <a href="%mainpage_link%">new website</a> and start creating content!',
+			
+			body: '<h3>Wait... Now what?</h3>
+ 						<p>Click the link below to see the main page for your website. Where to go from here:</p>
+ 						<ul>
+ 							<li>The first thing you should do is log into your site using the Log in link on the sidebar.</li>
+ 							<li>Go into the Administration panel, expand General, and click General Configuration. There you will be able to configure some basic information about your site.</li>
+ 							<li>Visit the <a href="http://enanocms.org/Category:Plugins" onclick="window.open(this.href); return false;">Enano Plugin Gallery</a> to download and use plugins on your site.</li>
+ 							<li>Periodically create a backup of your database and filesystem, in case something goes wrong. This should be done at least once a week &ndash; more for wiki-based sites.</li>
+ 							<li>Hire some moderators, to help you keep rowdy users tame.</li>
+ 							<li>Tell the <a href="http://enanocms.org/Contact_us">Enano team</a> what you think.</li>
+ 							<li><b>Spread the word about Enano by adding a link to the Enano homepage on your sidebar!</b> You can enable this option in the General Configuration section of the administration panel.</li>
+ 						</ul>',
+			link_mainpage: '<a href="%mainpage_link%">Go to your website...</a>',
+		},
+		pophelp: {
+			admin_embed_php_title: 'Allow administrators to embed PHP',
+			admin_embed_php_body: '<p>This option allows you to control whether anything between the standard &lt;?php and ?&gt; tags will be treated as
+																PHP code by Enano. If this option is enabled, and members of the Administrators group use these tags, Enano will
+																execute that code when the page is loaded. There are obvious potential security implications here, which should
+																be carefully considered before enabling this option.</p>
+ 														<p>If you are the only administrator of this site, or if you have a high level of trust for those will be administering
+																the site with you, you should enable this to allow extreme customization of pages.</p>
+ 														<p>Leave this option off if you are at all concerned about security – if your account is compromised and PHP embedding
+																is enabled, an attacker can run arbitrary code on your server! Enabling this will also allow administrators to
+																embed Javascript and arbitrary HTML and CSS.</p>
+ 														<p>If you don\'t have experience coding in PHP, you can safely disable this option. You may change this at any time
+																using the ACL editor by selecting the Administrators group and This Entire Website under the scope selection.</p>',
+			url_schemes_title: 'URL schemes',
+			url_schemes_body: '<p>The URL scheme allows you to decide how the URLs to your Enano pages will look.</p>
+ 												<p>The first option (Standard URLs) works on any web server. You should select it if your server doesn\'t run Apache, or
+														if you are at all unsure of your server\'s configuration. With this scheme, URLs at your site will look like <tt>
+														http://yoursite.com/path-to-enano/index.php?title=Main_Page</tt>.</p>
+ 												<p>The second option, Small URLs, will be selected by default if Enano detects Apache. Small URLs are more friendly towards
+														search engines, but they don\'t work on very many non-Apache servers, or if PHP is set up through CGI on your server. Many
+														free and low-cost web hosts will configure PHP through CGI in order to keep your user account as the owner of any files that
+														Enano generates. With this scheme, URLs at your site will look like <tt>http://yoursite.com/path-to-enano/index.php/Main_Page</tt>.
+														</p>
+ 												<p>The last option, Tiny URLs, is the most friendly URL scheme for search engines, because your URLs won\'t have any special characters
+														at all in them. However, this only works if your webhost has configured Apache with support for mod_rewrite. Most of the time if your
+														host supports this you will see a listing for it in their feature matrix. None of the popular Linux distributions (such as Ubuntu,
+														Debian, Red Hat Enterprise Linux&trade;, Fedora, openSUSE&trade;, or CentOS) come with mod_rewrite enabled, so if you run a
+														home-brew server, you should consult your distribution\'s documentation for enabling mod_rewrite before selecting this option.
+														With this scheme, URLs at your site will look like <tt>http://yoursite.com/path-to-enano/Main_Page</tt>.</p>
+														</p>',
+			btn_close_window: 'Close window',
+		},
+		upgrade: {
+			system_title: 'Enano upgrade tool',
+			stg_welcome: 'Welcome',
+			stg_login: 'Login',
+			stg_confirm: 'Confirmation',
+			stg_upgrade: 'Perform upgrade',
+			stg_finish: 'Finish',
+			
+			welcome_banshee_heading: 'Welcome to Enano %enano_version%.',
+			welcome_banshee_para1: 'Enano 1.2 features support for multiple languages, better security, faster page loading, more tools, a revamped page editor, and much more. This migration tool will automatically make the changes to your database required for Enano 1.2.',
+			welcome_banshee_para2: 'The migration tool has been tested under many different installation scenarios, but isn\'t fully guaranteed to work. The Enano team can\'t be responsible if the migration fails. If you encounted problems, please be sure to post on the <a href="http://forum.enanocms.org/">Enano forums</a>.',
+			welcome_caoineag_heading: 'Welcome to the Enano upgrade tool.',
+			welcome_caoineag_para1: 'To use this version of Enano, some parts of your database need to be upgraded. This tool will automatically perform the upgrade for you.',
+			welcome_btn_continue: 'Start upgrade',
+			
+			login_msg_auth_needed_title: 'Authentication needed',
+			login_msg_auth_needed_body_level1: 'To continue, you need to log in. Please enter an administrator username and password below.',
+			login_msg_auth_needed_body_level2: 'To confirm the upgrade, you need to re-enter your login information. Please enter your username and password below.',
+			login_msg_local_auth: '<b>Note:</b> The upgrade tool doesn\'t recognize any authentication plugins. Even if you use authentication plugins in Enano, you need to enter a local administrator username and password to use this upgrade tool.',
+			login_btn_login: 'Log in',
+			login_err_failed: 'The following error occurred during the login process: %error_code%.',
+			
+			confirm_title: 'Confirm upgrade',
+			confirm_body: 'You are about to upgrade to Enano version <b>%enano_version%</b>. You should make sure that you\'ve done the following before you continue:',
+			confirm_objective_backup_fs: 'Back up Enano installation directory (<b>%dir%</b>)',
+			confirm_objective_backup_db: 'Back up Enano database, including non-Enano tables if any (<b>%dbname%</b>)',
+			confirm_warning_langimport: '<b>Warning!</b> This release of Enano has some changed strings. If you\'ve customized any language strings, those changes will be lost.',
+			confirm_btn_upgrade: 'Pimp my Enano!',
+			
+			msg_schema_complete_title: 'Database upgrades complete',
+			msg_schema_complete_body: 'You\'re past the hard part - all of the modifications to your database were successful. We need to perform a few final steps to finish the upgrade; just click the button below to finish everything up.',
+			btn_continue: 'Finish upgrade',
+			
+			err_current_title: 'Already running current version',
+			err_current_body: 'No database upgrades are needed right now, you\'re already running the Enano version set in this installer. Try <a href="%mainpage_link%">heading back to your site</a>. Think there\'s a mistake somewhere? <a href="http://forum.enanocms.org/">Let the Enano team know about it</a>.',
+			err_current_body_para2: 'You might also want to use the administration panel to check for updates, or <a href="http://enanocms.org/download">download the latest release</a> of Enano.',
+			err_post_not_available: 'You\'re trying to run the post-upgrade process, but your site isn\'t in the correct state for this.',
+			
+			post_status_title: 'Finishing upgrade',
+			post_status_body: 'Enano is cleaning up some data and finalizing the upgrade.',
+			post_status_finish_title: 'All done!',
+			post_status_finish_body: 'That\'s it - Enano has been upgraded. You should <a href="%mainpage_link%">go back to your site</a> now and make sure that everything works right. If you find a problem, be sure to report it to the <a href="http://forum.enanocms.org/">Enano development team</a>. (Make sure you disable any plugins first, since we can\'t easily tell if your problem is caused by the Enano core or by a plugin, unless all of your non-system plugins are disabled.)',
+			
+			stg_flushcache_title: 'Flush caches',
+			stg_flushcache_body: 'The upgrader failed to delete some cached data. You may experience some problems with file corruption or badly drawn pages until the caches expire, which is often no longer than 20 minutes.',
+			stg_setversion_title: 'Set Enano version and log upgrade',
+			stg_setversion_body: 'There was a problem finalizing the upgrade and inserting logs. You really shouldn\'t ever see this message, but calling setConfig(\'enano_version\', installer_enano_version()) should get you rolling again since everything else is probably done by now.',
+		},
+		cli: {
+			welcome_line1: '<c 1>Welcome to the <c 34>Enano</c></c> CMS<c 1> installation wizard.</c>\n',
+			welcome_line2: 'Installing Enano version <c 1>%enano_version%</c> on PHP <c 1>%php_version%</c>\n',
+			
+			prompt_driver: 'Database type (mysql or postgresql)',
+			prompt_dbhost: 'Hostname of database server',
+			prompt_dbport: 'Database server port',
+			prompt_dbuser: 'Database username',
+			prompt_dbpasswd: 'Database password',
+			prompt_dbname: 'Database name',
+			prompt_tblpfx: 'Table prefix',
+			prompt_user: 'Enano administrator username',
+			prompt_pass: 'Enano administrator password',
+			prompt_email: 'Contact e-mail',
+			prompt_sitename: 'Site name',
+			prompt_sitedesc: 'Site description',
+			prompt_copyright: 'Copyright notice',
+			prompt_urlscheme: 'URL scheme (standard, short, rewrite, or tiny)',
+			prompt_scriptpath: 'Now we need the path to Enano, relative to your website\'s document root,\nwithout a trailing slash. This should be based on the URL your site will\nuse. For example, if your site is http://yoursite.com/enano/, use the value\n"/enano" here. Leave this blank if Enano is in your document root, e.g.\nhttp://yoursite.com/index.php?title=Enano_page.\nSite URL',
+			prompt_confirm: 'Enter again to confirm',
+			msg_echo_warning: '(WARNING, will be echoed)',
+			
+			default_site_name: 'Enano site',
+			default_site_desc: 'My first Enano site',
+			default_copyright: '&copy; %year%',
+			
+			msg_testing_db: 'Testing database connectivity...',
+			
+			stage_sysreqs: '<c 34;1>Checking system.</c>\n',
+			test_pass: '<c 1;32>PASS</c>',
+			test_vwarn: '<c 1;33>WARNING</c>',
+			test_warn: '<c 0;31>FAIL</c><c 1> (optional)</c>',
+			test_fail: '<c 1;31>FAIL</c>',
+			
+			msg_test_warnings: '<c 1;33>The following warnings were generated during the check:</c>',
+			msg_tests_passed: '<c 32><c 1>All tests passed.</c> Installing Enano.</c>\n',
+			msg_installing_db_stage1: '<c 1>Installing database, stage 1...</c>',
+			msg_parsing_schema: '<c 1>Preparing installation payload...</c>',
+			msg_installing_db_stage2: '<c 1>Installing database, stage 2...</c>',
+			msg_writing_config: '<c 1>Generating config file...</c>',
+			msg_starting_api: '<c 1>Starting up the Enano API...</c>',
+			msg_importing_language: '<c 1>Importing default language...</c>',
+			msg_importing_content: '<c 1>Installing default content...</c>',
+			msg_initting_logs: '<c 1>Initializing logs...</c>',
+			msg_cleaning_up: '<c 1>Removing temporary data...</c>',
+			msg_initting_index: '<c 1>Initializing search index...</c>',
+			msg_renaming_config: '<c 1>Renaming config file...</c>',
+			
+			msg_ok: '<c 1;32>OK</c>',
+			msg_install_success: '<c 1;32>Congratulations!</c> <c 1>Enano was successfully installed.</c>\n<c 34>Now, navigate your browser to your Enano installation to make sure everything\nworks right. If you encounter problems, contact the Enano Team for support at\nhttp://forum.enanocms.org or in #enano on irc.freenode.net.</c> <c 34;1>Be sure to\nmention that you used the CLI installer.</c>\n',
+			
+			err_pass_no_match: '  <c 1>Passwords do not match.</c>\n',
+			err_db_connect_fail: 'Could not connect to the database',
+			err_sysreqs_fail: 'One or more system requirement checks has failed.',
+			err_no_drivers: 'Support for at least one database driver is required.',
+			err_schema_load: 'Could not load the database schema file.',
+			err_db_query: 'A database error occurred; see the message above for details.',
+			err_query_sanity_failed: 'The sanity check on a database query failed.',
+			err_writing_config: 'Could not write the configuration file.',
+			err_importing_language: 'Could not import the default language.',
+			err_initting_logs: 'Could not initialize Enano\'s logs.',
+			err_cleaning_up: 'Could not clean up the temporary data.',
+			err_initting_index: 'Could not initialize the search index.',
+			err_renaming_config: 'Could not rename config.new.php to config.php; please do this manually.',
+		}
+	}
 }
--- a/language/english/meta.json	Sun Mar 28 21:49:26 2010 -0400
+++ b/language/english/meta.json	Sun Mar 28 23:10:46 2010 -0400
@@ -1,6 +1,6 @@
 {
-  // Language metadata
-  lang_name_english: 'English',
-  lang_name_native: 'English',
-  lang_code: 'eng'
+	// Language metadata
+	lang_name_english: 'English',
+	lang_name_native: 'English',
+	lang_code: 'eng'
 }
--- a/language/english/tools.json	Sun Mar 28 21:49:26 2010 -0400
+++ b/language/english/tools.json	Sun Mar 28 23:10:46 2010 -0400
@@ -11,191 +11,191 @@
  */
 
 var enano_lang = {
-  categories: [
-    'meta', 'search', 'specialpage', 'pagetools', 'log'
-  ],
-  strings: {
-    meta: {
-      search: 'Search page',
-      specialpage: 'Special pages',
-      pagetools: 'Userspace page-management tools',
-      log: 'Log and history display',
-    },
-    specialpage: {
-      administration: 'Administration',
-      manage_sidebar: 'Manage the Sidebar',
-      css: 'Templated style sheet generator',
-      groupcp: 'Group Membership',
-      create_page: 'Create page',
-      all_pages: 'All pages',
-      special_pages: 'List of special pages',
-      about_enano: 'About Enano',
-      gnu_gpl: 'GNU General Public License',
-      tag_cloud: 'Tag cloud',
-      search_rebuild: 'Rebuild search index',
-      search: 'Search',
-      upload_file: 'Upload file',
-      download_file: 'Download file',
-      log_in: 'Log in',
-      log_out: 'Log out',
-      register: 'Register',
-      preferences: 'Edit Profile',
-      autofill: 'Javascript suggestion servlet',
-      contributions: 'User contributions',
-      change_theme: 'Change my preferred theme',
-      activate_account: 'Activate user account',
-      captcha: 'CAPTCHA image generator',
-      password_reset: 'Reset forgotten password',
-      member_list: 'Member list',
-      language_export: 'Language exporter',
-      private_messages: 'Private Messages',
-      log: 'Log',
-      avatar: 'Fetch avatar'
-    },
-    search: {
-      th_advanced_search: 'Advanced Search',
-      
-      err_query_title: 'Some problems were encountered during your search.',
-      err_query_body: 'There was a problem with your search query, and as a result there may be a reduced number of search results.',
-      err_query_too_many_terms: 'Some of your search terms were excluded because searches are limited to 20 terms to prevent excessive server load.',
-      err_query_has_stopwords: 'One or more of your search terms was excluded because either it was less than 2 characters in length or is a common word (a stopword) that is typically found on a large number of pages. Examples of stopwords include "the", "this", "which", "with", etc.',
-      err_query_dup_terms: 'One or more of your search terms was excluded because duplicate terms were encountered.',
-      err_query_term_too_short: 'One or more of your search terms was excluded because terms must be at least 4 characters in length.',
-      err_query_no_positive: 'You need to have at least one keyword in your search query. Searching only for pages not containing a term is not allowed.',
-      
-      btn_search: 'Search',
-      // note the case difference with th_advanced_search
-      btn_advanced_search: 'Advanced search',
-      
-      msg_no_results: 'No results.',
-      msg_result_detail: 'Results <b>%start_string%</b> - <b>%per_string%</b> of about <b>%num_results%</b> for <b>%q_trim%</b> in %search_time%s.',
-      body_no_results_title: 'Your search for <b>"%query%"</b> didn\'t turn up any results.',
-      body_no_results_body: '<p>There are a few things you can try:</p>
-                             <ul>
-                               <li>Were you looking for a specific Special page? Special pages are not searchable. You may want to see a <a href="%special_url%">list of special pages</a>.</li>
-                               <li>If you have the appropriate permissions, you can <a href="%create_url%">start the %query% page</a>.</li>
-                               <li>Try using fewer keywords. You can get broader results if you remove quotes from your search query.</li>
-                               <li>Did your search trigger any warnings? Sometimes a search can be cancelled if there aren\'t any terms in a search query that are 4 characters or greater in length.</li>
-                             </ul>',
-      
-      lbl_site_search: 'Site search',
-      lbl_relevance: 'Relevance:',
-      lbl_field_any: 'Search for pages with <b>any of these words</b>:',
-      lbl_field_exact: 'with <b>this exact phrase</b>:',
-      lbl_field_none: 'with <b>none of these words</b>:',
-      lbl_field_all: 'with <b>all of these words</b>:',
-      lbl_field_casesensitive: 'Case-sensitive search:',
-      
-      result_tag_special: 'Special page',
-    },
-    pagetools: {
-      
-      // Create a page
-      create_err_invalid_namespace: 'You have selected an invalid page type.',
-      create_err_invalid_urlname: 'Please enter a title for your page and a custom URL if desired.',
-      create_err_already_exists: 'A page with that URL already exists. Please enter another title or enter a custom URL. (You can have two pages with the same name, but not two pages with the same URL.)',
-      create_err_no_permission: 'You don\'t have permission to create this page. Try another URL or title; if that does not work, please contact the site administration for permission to create pages.',
-      create_err_nodb_namespace: 'You cannot create Special or Admin pages - they can\'t be stored in the database.',
-      create_err_reserved_prefix: 'The prefix "Project:" is reserved for internal links and can\'t be used on a page name.',
-      
-      create_blurb: 'Add a new page to the site.',
-      create_field_title: 'Page title:',
-      create_field_namespace: 'Page type:',
-      create_group_advanced: 'Advanced options',
-      create_field_url_auto: 'Generate a URL based on the title',
-      create_field_url_manual: 'Enter a custom page URL',
-      create_field_url: 'Page ID:',
-      create_field_preview: 'Preview of URL:',
-      create_field_preview_hint: '(Requires Javascript support)',
-      create_btn_create: 'Create page',
-      
-      // All pages
-      allpages_blurb: 'Below is a list of all of the pages on this website.',
-      
-      // Special pages
-      specialpages_blurb: 'Below is a list of all of the special pages on this website.',
-      
-      // GPL page
-      gpl_blurb: 'The following text represents the license that the <a href="%about_url%">Enano</a> content management system is under. To make it easier to read, the text has been wiki-formatted; in no other way has it been changed.',
-      
-      // !!
-      // !! The following three strings will be used ONLY in non-English
-      // !! languages. A Spanish example is provided here.
-      // !!
-      
-      // "Version in Spanish"
-      gpl_title_native: 'Versión en español',
-      // "Version in English"
-      gpl_title_english: 'Versión en inglés',
-      // "View the license in English"
-      gpl_link_to_english: 'Vea la licencia en inglés',
-      
-      gpl_err_file_missing: 'It appears that the file "GPL" is missing from your Enano installation. You may find a wiki-formatted copy of the GPL at: <a href="http://enanocms.org/GPL">http://enanocms.org/GPL</a>. In the mean time, you may wish to contact the site administration and ask them to replace the GPL file.',
-      
-      // Tag cloud
-      tagcloud_pagelist_th: 'Pages tagged "%tag%"',
-      tagcloud_blurb: 'Summary of page tagging',
-      tagcloud_msg_no_tags: 'No pages are tagged yet.',
-      tagcloud_btn_return: 'Return to tag cloud',
-      tagcloud_instructions: 'Hover your mouse over a tag to see how many pages have the tag. Click on a tag to see a list of the pages that have it.',
-      tagcloud_sidebar_title: 'Tag cloud',
-      tagcloud_sidebar_btn_larger: 'Larger version',
-      tagcloud_tip_popularity_one: '1 page',
-      tagcloud_tip_popularity_plural: '%popularity% pages',
-      
-      // Recent changes
-      rc_btn_diff: 'diff',
-      rc_btn_hist: 'hist',
-      rc_btn_undo: 'undo',
-      rc_btn_view: 'view',
-      rc_btn_pm: 'PM',
-      rc_btn_usertalk: 'comment',
-    },
-    log: {
-      action_rename: 'Renamed (old name: %old_name%)',
-      action_create: 'Created page',
-      action_delete: 'Deleted page (%reason%)',
-      action_protect_none: 'Unprotected page (%reason%)',
-      action_protect_semi: 'Semiprotected page (%reason%)',
-      action_protect_full: 'Protected page (%reason%)',
-      action_reupload: 'Uploaded new revision: %reason%',
-      action_votereset: 'Reset deletion votes (had %num_votes% vote%plural%)',
-      action_protect: 'Protect and unprotect',
-      action_edit: 'Edit',
-      
-      breadcrumb_author: 'Author: %user%',
-      breadcrumb_within: 'Newer than: %time%',
-      breadcrumb_page: 'Page: %page%',
-      breadcrumb_action: 'Action: %action%',
-      
-      msg_no_reason_provided: 'No reason provided',
-      msg_reversion: 'Reversion of previous action',
-      msg_file_restored: 'Restored previous version',
-      msg_no_results: 'No results',
-      msg_no_filters: 'All site logs',
-      
-      form_filtertype_user: 'Author',
-      form_filtertype_within: 'Within',
-      form_filtertype_page: 'Page',
-      form_filtertype_action: 'Action',
-      form_filtertype_minor: 'Minor edit',
-      form_filtertype_minor_yes: 'Show only minor edits',
-      form_filtertype_minor_no: 'Hide minor edits',
-      formaction_rename: 'Rename',
-      formaction_create: 'Create page',
-      formaction_delete: 'Delete page',
-      formaction_reupload: 'File reupload',
-      formaction_votereset: 'Reset of deletion votes',
-      
-      heading_addfilter: 'Add a filter',
-      heading_logdisplay: 'Log filter results',
-      
-      btn_add_filter: 'Add filter',
-      err_addfilter_field_empty: 'The filter was not added because you didn\'t enter a valid value in the field.',
-      
-      err_access_denied: 'You don\'t have permission to view page logs.',
-    }
-  }
+	categories: [
+		'meta', 'search', 'specialpage', 'pagetools', 'log'
+	],
+	strings: {
+		meta: {
+			search: 'Search page',
+			specialpage: 'Special pages',
+			pagetools: 'Userspace page-management tools',
+			log: 'Log and history display',
+		},
+		specialpage: {
+			administration: 'Administration',
+			manage_sidebar: 'Manage the Sidebar',
+			css: 'Templated style sheet generator',
+			groupcp: 'Group Membership',
+			create_page: 'Create page',
+			all_pages: 'All pages',
+			special_pages: 'List of special pages',
+			about_enano: 'About Enano',
+			gnu_gpl: 'GNU General Public License',
+			tag_cloud: 'Tag cloud',
+			search_rebuild: 'Rebuild search index',
+			search: 'Search',
+			upload_file: 'Upload file',
+			download_file: 'Download file',
+			log_in: 'Log in',
+			log_out: 'Log out',
+			register: 'Register',
+			preferences: 'Edit Profile',
+			autofill: 'Javascript suggestion servlet',
+			contributions: 'User contributions',
+			change_theme: 'Change my preferred theme',
+			activate_account: 'Activate user account',
+			captcha: 'CAPTCHA image generator',
+			password_reset: 'Reset forgotten password',
+			member_list: 'Member list',
+			language_export: 'Language exporter',
+			private_messages: 'Private Messages',
+			log: 'Log',
+			avatar: 'Fetch avatar'
+		},
+		search: {
+			th_advanced_search: 'Advanced Search',
+			
+			err_query_title: 'Some problems were encountered during your search.',
+			err_query_body: 'There was a problem with your search query, and as a result there may be a reduced number of search results.',
+			err_query_too_many_terms: 'Some of your search terms were excluded because searches are limited to 20 terms to prevent excessive server load.',
+			err_query_has_stopwords: 'One or more of your search terms was excluded because either it was less than 2 characters in length or is a common word (a stopword) that is typically found on a large number of pages. Examples of stopwords include "the", "this", "which", "with", etc.',
+			err_query_dup_terms: 'One or more of your search terms was excluded because duplicate terms were encountered.',
+			err_query_term_too_short: 'One or more of your search terms was excluded because terms must be at least 4 characters in length.',
+			err_query_no_positive: 'You need to have at least one keyword in your search query. Searching only for pages not containing a term is not allowed.',
+			
+			btn_search: 'Search',
+			// note the case difference with th_advanced_search
+			btn_advanced_search: 'Advanced search',
+			
+			msg_no_results: 'No results.',
+			msg_result_detail: 'Results <b>%start_string%</b> - <b>%per_string%</b> of about <b>%num_results%</b> for <b>%q_trim%</b> in %search_time%s.',
+			body_no_results_title: 'Your search for <b>"%query%"</b> didn\'t turn up any results.',
+			body_no_results_body: '<p>There are a few things you can try:</p>
+ 														<ul>
+ 															<li>Were you looking for a specific Special page? Special pages are not searchable. You may want to see a <a href="%special_url%">list of special pages</a>.</li>
+ 															<li>If you have the appropriate permissions, you can <a href="%create_url%">start the %query% page</a>.</li>
+ 															<li>Try using fewer keywords. You can get broader results if you remove quotes from your search query.</li>
+ 															<li>Did your search trigger any warnings? Sometimes a search can be cancelled if there aren\'t any terms in a search query that are 4 characters or greater in length.</li>
+ 														</ul>',
+			
+			lbl_site_search: 'Site search',
+			lbl_relevance: 'Relevance:',
+			lbl_field_any: 'Search for pages with <b>any of these words</b>:',
+			lbl_field_exact: 'with <b>this exact phrase</b>:',
+			lbl_field_none: 'with <b>none of these words</b>:',
+			lbl_field_all: 'with <b>all of these words</b>:',
+			lbl_field_casesensitive: 'Case-sensitive search:',
+			
+			result_tag_special: 'Special page',
+		},
+		pagetools: {
+			
+			// Create a page
+			create_err_invalid_namespace: 'You have selected an invalid page type.',
+			create_err_invalid_urlname: 'Please enter a title for your page and a custom URL if desired.',
+			create_err_already_exists: 'A page with that URL already exists. Please enter another title or enter a custom URL. (You can have two pages with the same name, but not two pages with the same URL.)',
+			create_err_no_permission: 'You don\'t have permission to create this page. Try another URL or title; if that does not work, please contact the site administration for permission to create pages.',
+			create_err_nodb_namespace: 'You cannot create Special or Admin pages - they can\'t be stored in the database.',
+			create_err_reserved_prefix: 'The prefix "Project:" is reserved for internal links and can\'t be used on a page name.',
+			
+			create_blurb: 'Add a new page to the site.',
+			create_field_title: 'Page title:',
+			create_field_namespace: 'Page type:',
+			create_group_advanced: 'Advanced options',
+			create_field_url_auto: 'Generate a URL based on the title',
+			create_field_url_manual: 'Enter a custom page URL',
+			create_field_url: 'Page ID:',
+			create_field_preview: 'Preview of URL:',
+			create_field_preview_hint: '(Requires Javascript support)',
+			create_btn_create: 'Create page',
+			
+			// All pages
+			allpages_blurb: 'Below is a list of all of the pages on this website.',
+			
+			// Special pages
+			specialpages_blurb: 'Below is a list of all of the special pages on this website.',
+			
+			// GPL page
+			gpl_blurb: 'The following text represents the license that the <a href="%about_url%">Enano</a> content management system is under. To make it easier to read, the text has been wiki-formatted; in no other way has it been changed.',
+			
+			// !!
+			// !! The following three strings will be used ONLY in non-English
+			// !! languages. A Spanish example is provided here.
+			// !!
+			
+			// "Version in Spanish"
+			gpl_title_native: 'Versión en español',
+			// "Version in English"
+			gpl_title_english: 'Versión en inglés',
+			// "View the license in English"
+			gpl_link_to_english: 'Vea la licencia en inglés',
+			
+			gpl_err_file_missing: 'It appears that the file "GPL" is missing from your Enano installation. You may find a wiki-formatted copy of the GPL at: <a href="http://enanocms.org/GPL">http://enanocms.org/GPL</a>. In the mean time, you may wish to contact the site administration and ask them to replace the GPL file.',
+			
+			// Tag cloud
+			tagcloud_pagelist_th: 'Pages tagged "%tag%"',
+			tagcloud_blurb: 'Summary of page tagging',
+			tagcloud_msg_no_tags: 'No pages are tagged yet.',
+			tagcloud_btn_return: 'Return to tag cloud',
+			tagcloud_instructions: 'Hover your mouse over a tag to see how many pages have the tag. Click on a tag to see a list of the pages that have it.',
+			tagcloud_sidebar_title: 'Tag cloud',
+			tagcloud_sidebar_btn_larger: 'Larger version',
+			tagcloud_tip_popularity_one: '1 page',
+			tagcloud_tip_popularity_plural: '%popularity% pages',
+			
+			// Recent changes
+			rc_btn_diff: 'diff',
+			rc_btn_hist: 'hist',
+			rc_btn_undo: 'undo',
+			rc_btn_view: 'view',
+			rc_btn_pm: 'PM',
+			rc_btn_usertalk: 'comment',
+		},
+		log: {
+			action_rename: 'Renamed (old name: %old_name%)',
+			action_create: 'Created page',
+			action_delete: 'Deleted page (%reason%)',
+			action_protect_none: 'Unprotected page (%reason%)',
+			action_protect_semi: 'Semiprotected page (%reason%)',
+			action_protect_full: 'Protected page (%reason%)',
+			action_reupload: 'Uploaded new revision: %reason%',
+			action_votereset: 'Reset deletion votes (had %num_votes% vote%plural%)',
+			action_protect: 'Protect and unprotect',
+			action_edit: 'Edit',
+			
+			breadcrumb_author: 'Author: %user%',
+			breadcrumb_within: 'Newer than: %time%',
+			breadcrumb_page: 'Page: %page%',
+			breadcrumb_action: 'Action: %action%',
+			
+			msg_no_reason_provided: 'No reason provided',
+			msg_reversion: 'Reversion of previous action',
+			msg_file_restored: 'Restored previous version',
+			msg_no_results: 'No results',
+			msg_no_filters: 'All site logs',
+			
+			form_filtertype_user: 'Author',
+			form_filtertype_within: 'Within',
+			form_filtertype_page: 'Page',
+			form_filtertype_action: 'Action',
+			form_filtertype_minor: 'Minor edit',
+			form_filtertype_minor_yes: 'Show only minor edits',
+			form_filtertype_minor_no: 'Hide minor edits',
+			formaction_rename: 'Rename',
+			formaction_create: 'Create page',
+			formaction_delete: 'Delete page',
+			formaction_reupload: 'File reupload',
+			formaction_votereset: 'Reset of deletion votes',
+			
+			heading_addfilter: 'Add a filter',
+			heading_logdisplay: 'Log filter results',
+			
+			btn_add_filter: 'Add filter',
+			err_addfilter_field_empty: 'The filter was not added because you didn\'t enter a valid value in the field.',
+			
+			err_access_denied: 'You don\'t have permission to view page logs.',
+		}
+	}
 };
 
 // All done! :-)
--- a/language/english/user.json	Sun Mar 28 21:49:26 2010 -0400
+++ b/language/english/user.json	Sun Mar 28 23:10:46 2010 -0400
@@ -11,211 +11,211 @@
  */
 
 var enano_lang = {
-  categories: [
-    'meta', 'user', 'usercp', 'groupcp', 'privmsgs', 'userfuncs', 'userpage',
-  ],
-  strings: {
-    meta: {
-      user: 'Login, logout, and authentication',
-      usercp: 'User control panel',
-      groupcp: 'Group control panel',
-      privmsgs: 'Private message and buddy list CP',
-      userfuncs: 'User management pages',
-      userpage: 'User pages',
-    },
-    user: {
-      login_message_short: 'Please enter your username and password to log in.',
-      login_message_short_elev: 'Please re-enter your login details',
-      login_body: 'Logging in enables you to use your preferences and access member information. If you don\'t have a username and password here, you can <a href="%reg_link%">create an account</a>.',
-      login_body_elev: 'You are requesting that a sensitive operation be performed. To continue, please re-enter your password to confirm your identity.',
-      login_field_username: 'Username',
-      login_field_password: 'Password',
-      login_field_remember: 'Remember session:',
-      login_forgotpass_blurb: 'Forgot your password? <a href="%forgotpass_link%">No problem.</a>',
-      login_createaccount_blurb: 'Maybe you need to <a href="%reg_link%">create an account</a>.',
-      login_field_captcha: 'Code in image',
-      login_nocrypt_title: 'Important note regarding cryptography:',
-      login_nocrypt_body: 'Some countries do not allow the import or use of cryptographic technology. If you live in one of the countries listed below, you should <a href="%nocrypt_link%">log in without using encryption</a>.',
-      login_nocrypt_countrylist: 'This restriction applies to the following countries: Belarus, China, India, Israel, Kazakhstan, Mongolia, Pakistan, Russia, Saudi Arabia, Singapore, Tunisia, Venezuela, and Vietnam.',
-      login_dh_notice: 'If your browser supports it, strong encryption will be used to protect your password as it it sent over the Internet. The encryption process takes from about 1 to 7 seconds, depending on the speed of your computer, while a key is being generated. Even if your browser prompts you about an unresponsive script, please allow the script to continue or your login may fail.',
-      login_usecrypt_title: 'Encryption is currently turned off.',
-      login_usecrypt_body: 'If you are not in one of the countries listed below, you should <a href="%usecrypt_link%">enable encryption</a> to secure the logon process.',
-      login_usecrypt_countrylist: 'The cryptography restriction applies to the following countries: Belarus, China, India, Israel, Kazakhstan, Mongolia, Pakistan, Russia, Saudi Arabia, Singapore, Tunisia, Venezuela, and Vietnam.',
-      login_success_title: 'Login successful',
-      login_success_body: 'You have successfully logged into the %config.site_name% site as "%username%". Redirecting to %redir_target%...',
-      login_success_body_mainpage: 'the main page',
-      login_success_short: 'Success.',
-      login_check_remember: 'Keep me logged in on this computer for %session_length% %length_units% unless I log out',
-      login_check_remember_infinite: 'Keep me logged in on this computer until I log out',
-      login_btn_log_in: 'Log in',
-      
-      login_noact_title: 'Account error',
-      login_noact_msg_intro: 'It appears that your user account has not yet been activated.',
-      login_noact_solution_none: 'Your account was most likely deactivated by an administrator. Please contact the site administration for further assistance.',
-      login_noact_solution_user: 'Please check your e-mail; you should have been sent a message with instructions on how to activate your account. If you do not receive an e-mail from this site within 24 hours, please contact the site administration for further assistance.',
-      login_noact_solution_admin: 'This website has been configured so that all user accounts must be activated by the administrator before they can be used, so your account will most likely be activated the next time an administrator visits the site.',
-      login_noact_msg_logout_success_title: 'Logged out',
-      login_noact_msg_logout_success_body: 'You have successfully been logged out. All cookies cleared.',
-      login_noact_msg_ask_admins: 'If you are having trouble or did not receive the e-mail, you can request account activation from the administrators of this site.',
-      login_noact_msg_admins_just_asked: 'A request has just been sent to the administrators of this site. They will be able to activate your account or send you another activation e-mail if needed.',
-      login_noact_msg_admins_asked: 'There is an active request in the administrators\' control panel for your account to be activated.',
-      login_noact_btn_request_activation: 'Request account activation',
-      login_noact_btn_log_out: 'Log out',
-      
-      login_ajax_fetching_key: 'Fetching an encryption key...',
-      login_ajax_generating_key: 'Performing Diffie-Hellman key generation...',
-      login_ajax_prompt_title: 'Please enter your username and password to continue.',
-      login_ajax_prompt_title_elev: 'You are requesting a sensitive operation.',
-      login_ajax_prompt_body_elev: 'Please re-enter your login details to verify your identity.',
-      login_ajax_link_fullform: '<a href="%link_full_form%">HTML login</a>',
-      login_ajax_link_fullform_dh: 'Don\'t stop this process even if your browser asks you to. If this takes more than 10 seconds, you might want to try the <a href="%link_full_form%">full login form</a>.',
-      login_ajax_link_forgotpass: '<a href="%forgotpass_link%">Forgot password</a>',
-      login_ajax_createaccount_blurb: '<a href="%reg_link%">Register</a>',
-      login_ajax_loggingin: 'Logging in...',
-      login_ajax_msg_used_temp_pass: 'You have logged in using a temporary password. Before you can log in, you must finish resetting your password. Do you want to reset your real password now?',
-      login_ajax_check_remember: 'Stay logged in (%session_length% %length_units%)',
-      login_ajax_check_remember_infinite: 'Stay logged in',
-      login_ajax_check_dh: 'Encrypt my details (<a class="no_external" href="http://docs.enanocms.org/Help:Appendix_B#dh" onclick="window.open(this.href); return false;">What?</a>)',
-      login_ajax_check_dh_ie: 'Browser doesn\'t support encryption (<a class="no_external" href="http://docs.enanocms.org/Help:Appendix_B#dh" onclick="window.open(this.href); return false;">What?</a>)',
-      login_ajax_err_crypto: 'Encryption failed.',
-      login_ajax_err_crypto_details: 'If you have a Javascript debugger, details are available on the console.',
-      login_ajax_err_crypto_link: 'Use HTML login interface',
-      
-      err_login_generic_title: 'There was an error in the login process',
-      err_key_not_found: 'Enano couldn\'t look up the encryption key used to encrypt your password. This most often happens if a cache rotation occurred during your login attempt, or if you refreshed the login page.',
-      err_key_not_found_cleared: 'It seems that the list of encryption keys used for login information has reached its maximum length, thus preventing new keys from being inserted. The list has been automatically cleared. Please try logging in again; if you are still unable to log in, please contact the site administration.',
-      err_dh_key_not_found: 'Enano couldn\'t retrieve the private key used for the high-strength encrypted logon. It is possible that the list of keys was cleared during your logon process as this happens approximately once every 72 hours. Please try logging in again; if you are still unable to log in, please contact the site administration.',
-      err_dh_key_not_numeric: 'The Diffie-Hellman public key you sent through was not an arbitrary-precision decimal integer.',
-      err_dh_hash_no_match: 'The Diffie-Hellman key was not calculated correctly by one of the parties (I got a different shared secret than you did).',
-      err_userinfo_decode_failed: 'An internal error occurred while decrypting the encrypted information packet.',
-      err_key_wrong_length: 'The encryption key was the wrong length.',
-      err_too_big_for_britches: 'You are trying to authenticate at a level that your user account does not permit.',
-      err_invalid_credentials: 'You have entered an invalid username or password. Please enter your login details again.',
-      err_invalid_credentials_lockout: ' You have used up %fails% out of %threshold% login attempts. After you have used up all %threshold% login attempts, you will be locked out from logging in for %duration% minutes.',
-      err_invalid_credentials_captcha: ' You have used up %fails% out of %threshold% login attempts. After you have used up all %threshold% login attempts, you will have to enter a visual confirmation code while logging in, effective for %duration% minutes.',
-      err_backend_fail: 'You entered the right credentials and everything was validated, but for some reason Enano couldn\'t register your session. This is an internal problem with the site and you are encouraged to contact site administration.',
-      err_locked_out_initial_lockout: 'Your computer is locked out from logging in because you entered invalid credentials too many times. This restriction will expire in %time_rem% minute%plural%.',
-      err_locked_out_initial_captcha: 'Please enter the correct visual confirmation code to log in. This restriction will expire in %time_rem% minute%plural%.',
-      err_lockout_bad_captcha: 'The confirmation code you entered was incorrect.',
-      err_admin_session_timed_out: 'Your session has timed out; please log in again using the form above.',
-      err_email_not_valid: 'The e-mail address you entered is invalid.',
-      
-      logout_success_title: 'Logged out',
-      logout_success_body: 'You have been successfully logged out, and all cookies have been cleared. You will now be transferred to the main page.',
-      logout_confirm_title: 'Log out of this website?',
-      logout_confirm_body_normal: 'Logging out will keep you from accessing your preferences and any special privileges your account might have until you log in again.',
-      logout_confirm_body_nelev: '%this.user_logout_confirm_body_normal%\n\nYou may also choose to discard your elevated authorization (gained when you re-entered your password). You will be asked for your password if you try to use any privileged functions.',
-      logout_confirm_btn_logout: 'Log out',
-      logout_confirm_btn_deauth: 'Forget authorization',
-      logout_confirm_title_elev: 'Are you sure you want to de-authenticate?',
-      logout_confirm_body_elev: 'If you de-authenticate, you will no longer be able to use the administration panel until you re-authenticate again. You may do so at any time using the Administration button on the sidebar.',
-      logout_confirm_btn_logout: 'Log Out',
-      logout_err_title: 'An error occurred during the logout process.',
-      // Unused at this point
-      logout_err_not_loggedin: 'You don\'t seem to be logged in.',
-      
-      // User levels
-      level_short_guest: 'Guest',
-      level_short_member: 'Member',
-      level_short_chpref: 'Sensitive preferences changeable',
-      level_short_mod: 'Moderator',
-      level_short_admin: 'Administrative',
-      level_short_unknown: 'Unknown (level %user_level%)',
-      level_long_guest: 'Low - guest privileges',
-      level_long_member: 'Standard - normal member level',
-      level_long_chpref: 'Medium - user can change his/her own e-mail address and password',
-      level_long_mod: 'High - moderator privileges',
-      level_long_admin: 'Highest - administrative privileges',
-      level_long_unknown: 'Unknown privileges (level %user_level%)',
-      
-      ban_msg_title: 'Ban notice',
-      ban_msg_body: 'You have been banned from this website. Please contact the site administrator for more information.',
-      ban_lbl_reason: 'Reason:',
-      
-      keepalive_info_title: 'About the keep-alive feature',
-      keepalive_info_body: 'Keep-alive is a new Enano feature that keeps your administrative session from timing out while you are using the administration panel. This feature can be useful if you are editing a large page or doing something in the administration interface that will take longer than 15 minutes.<br /><br />For security reasons, Enano mandates that high-privilege logins last only 15 minutes, with the time being reset each time a page is loaded (or, more specifically, each time the session API is started). The consequence of this is that if you are performing an action in the administration panel that takes more than 15 minutes, your session may be terminated. The keep-alive feature attempts to relieve this by sending a "ping" to the server every 10 minutes.<br /><br />Please note that keep-alive state is determined by a cookie. Thus, if you log out and then back in as a different administrator, keep-alive will use the same setting that was used when you were logged in as the first administrative user. In the same way, if you log into the administration panel under your account from another computer, keep-alive will be set to "off".<br /><br /><b>For more information:</b><br /><a href="http://docs.enanocms.org/Help:Appendix_B" onclick="window.open(this.href); return false;">Overview of Enano\'s security model</a>',
-      
-      type_guest: 'Guest',
-      type_member: 'Member',
-      type_mod: 'Moderator',
-      type_admin: 'Administrator',
-      
-      rank_member: '%this.user_type_member%',
-      rank_mod: '%this.user_type_mod%',
-      rank_admin: '%this.user_type_admin%',
-      rank_guest: '%this.user_type_guest%',
-      
-      msg_elev_timed_out: '<b>Your administrative session has timed out.</b> <a href="%login_link%" onclick="ajaxLogonToElev(); return false;">Log in again</a>',
-      
-      reg_err_locked_out: 'Registration is disabled because you are currently locked out from logging in. Please wait %time% minutes before attempting to register again.',
-      reg_err_captcha: 'The confirmation code you entered was incorrect.',
-      reg_err_disabled_title: 'Registration disabled',
-      reg_err_disabled_body: 'The administrator has disabled the registration of new accounts on this site.',
-      reg_err_disabled_body_adminblurb: 'Oops...it seems that you <em>are</em> the administrator...hehe...you can also <a href="%reg_link%">force account registration to work</a>.',
-      reg_err_username_invalid: 'Your username must be at least two characters in length and may not contain any of the following characters: &lt; &gt; _ &amp; ? \' " % / \\.',
-      // Not exactly an error
-      reg_err_password_good: 'The password you entered is valid.',
-      reg_err_alert_password_tooshort: 'Your password must be 6 characters or greater in length.',
-      reg_err_alert_password_nomatch: 'The passwords you entered do not match.',
-      reg_err_missing_key: 'Couldn\'t look up public encryption key',
-      reg_err_accept_tou: 'Please read and accept the Terms of Use before creating your account.',
-      reg_err_username_banned_chars: 'The username you chose contains invalid characters.',
-      reg_err_dupe_username: 'The username you selected is already in use by another user.',
-      reg_err_dupe_username_email: 'The username and e-mail address you selected are already in use by another user.',
-      reg_err_dupe_username_email_realname: 'The username, e-mail address, and real name you selected are already in use by another user.',
-      reg_err_dupe_email: 'The e-mail address you selected is already in use by another user.',
-      reg_err_dupe_email_realname: 'The e-mail address and real name you selected are already in use by another user.',
-      reg_err_dupe_realname: 'The real name you selected is already in use by another user.',
-      reg_err_password_too_weak: 'The password you entered did not meet the complexity requirements for this site. Please choose a stronger password.',
-      reg_err_actmail_failed: 'The activation e-mail could not be sent due to an internal error. This could possibly be due to an incorrect SMTP configuration. A request has been sent to the administrator to activate your account for you.',
-      
-      reg_msg_greatercontrol: 'A user account enables you to have greater control over your browsing experience.',
-      reg_msg_table_title: 'Create a user account',
-      reg_msg_table_subtitle: 'Please tell us a little bit about yourself.',
-      reg_msg_username_checking: 'Checking availability...',
-      reg_msg_username_available: 'This username is available.',
-      reg_msg_username_unavailable: 'This username is already taken.',
-      reg_msg_password_length: 'Your password must be at least six characters in length.',
-      reg_msg_password_score: 'It needs to score at least <b>%config.pw_strength_minimum%</b> for your registration to be accepted.',
-      reg_msg_password_needmatch: 'The passwords you entered do not match.',
-      reg_msg_email_activuser: 'An e-mail with an account activation key will be sent to this address, so please ensure that it is correct.',
-      reg_msg_realname_optional: 'Giving your real name is totally optional. If you choose to provide your real name, it will be used to provide attribution for any edits or contributions you may make to this site.',
-      reg_msg_captcha_pleaseenter: 'Please enter the code shown in the image to the right into the text box. The code is case-insensitive and the numeral zero is never used. This process helps to ensure that this registration is not being performed by an automated bot. If the image to the right is illegible, you can <a %regen_flags%>generate a new image</a>.',
-      reg_msg_captcha_blind: 'If you are visually impaired or otherwise cannot read the text shown to the right, please contact the site management and they will create an account for you.',
-      reg_msg_please_read_tou:'Please read and agree to the following terms before registering. By continuing to create an account you agree to be legally bound by the terms below.',
-      reg_msg_success_title: 'Registration successful',
-      reg_msg_success_body: 'Thank you for registering, your user account has been created.',
-      reg_msg_success_activ_none: 'You may now <a href="%login_link%">log in</a> with the username and password that you created.',
-      reg_msg_success_activ_user: 'Because this site requires account activation, you have been sent an e-mail with further instructions. Please follow the instructions in that e-mail to continue your registration.',
-      reg_msg_success_activ_admin: 'Because this site requires administrative account activation, you cannot use your account at the moment. A notice has been sent to the site administration team that will alert them that your account has been created.',
-      reg_msg_success_activ_coppa: 'However, in compliance with the Childrens\' Online Privacy Protection Act, you must have your parent or legal guardian activate your account. Please ask them to check their e-mail for further information.',
-      
-      reg_lbl_field_username: 'Preferred username:',
-      reg_lbl_field_password: 'Password:',
-      reg_lbl_field_password_confirm: 'Enter your password again to confirm.',
-      reg_lbl_field_email: 'E-mail address:',
-      reg_lbl_field_email_coppa: 'Your parent or guardian\'s e-mail address:',
-      reg_lbl_field_realname: 'Real name:',
-      reg_lbl_field_captcha: 'Visual confirmation',
-      reg_lbl_field_captcha_code: 'Code:',
-      reg_lbl_field_tou: 'I agree to abide by the terms and conditions stated above',
-      
-      reg_btn_create_account: 'Create my account',
-      
-      reg_coppa_title: 'Before you can register, please tell us your age.',
-      reg_coppa_link_atleast13: 'I was born <b>on or before</b> %yo13_date% and am <b>at least</b> 13 years of age',
-      reg_coppa_link_not13: 'I was born <b>after</b> %yo13_date% and am <b>less than</b> 13 years of age',
-      
-      reg_activation_email_subject: '%config.site_name% website account activation',
-      reg_activation_email: 'Dear %username%,
+	categories: [
+		'meta', 'user', 'usercp', 'groupcp', 'privmsgs', 'userfuncs', 'userpage',
+	],
+	strings: {
+		meta: {
+			user: 'Login, logout, and authentication',
+			usercp: 'User control panel',
+			groupcp: 'Group control panel',
+			privmsgs: 'Private message and buddy list CP',
+			userfuncs: 'User management pages',
+			userpage: 'User pages',
+		},
+		user: {
+			login_message_short: 'Please enter your username and password to log in.',
+			login_message_short_elev: 'Please re-enter your login details',
+			login_body: 'Logging in enables you to use your preferences and access member information. If you don\'t have a username and password here, you can <a href="%reg_link%">create an account</a>.',
+			login_body_elev: 'You are requesting that a sensitive operation be performed. To continue, please re-enter your password to confirm your identity.',
+			login_field_username: 'Username',
+			login_field_password: 'Password',
+			login_field_remember: 'Remember session:',
+			login_forgotpass_blurb: 'Forgot your password? <a href="%forgotpass_link%">No problem.</a>',
+			login_createaccount_blurb: 'Maybe you need to <a href="%reg_link%">create an account</a>.',
+			login_field_captcha: 'Code in image',
+			login_nocrypt_title: 'Important note regarding cryptography:',
+			login_nocrypt_body: 'Some countries do not allow the import or use of cryptographic technology. If you live in one of the countries listed below, you should <a href="%nocrypt_link%">log in without using encryption</a>.',
+			login_nocrypt_countrylist: 'This restriction applies to the following countries: Belarus, China, India, Israel, Kazakhstan, Mongolia, Pakistan, Russia, Saudi Arabia, Singapore, Tunisia, Venezuela, and Vietnam.',
+			login_dh_notice: 'If your browser supports it, strong encryption will be used to protect your password as it it sent over the Internet. The encryption process takes from about 1 to 7 seconds, depending on the speed of your computer, while a key is being generated. Even if your browser prompts you about an unresponsive script, please allow the script to continue or your login may fail.',
+			login_usecrypt_title: 'Encryption is currently turned off.',
+			login_usecrypt_body: 'If you are not in one of the countries listed below, you should <a href="%usecrypt_link%">enable encryption</a> to secure the logon process.',
+			login_usecrypt_countrylist: 'The cryptography restriction applies to the following countries: Belarus, China, India, Israel, Kazakhstan, Mongolia, Pakistan, Russia, Saudi Arabia, Singapore, Tunisia, Venezuela, and Vietnam.',
+			login_success_title: 'Login successful',
+			login_success_body: 'You have successfully logged into the %config.site_name% site as "%username%". Redirecting to %redir_target%...',
+			login_success_body_mainpage: 'the main page',
+			login_success_short: 'Success.',
+			login_check_remember: 'Keep me logged in on this computer for %session_length% %length_units% unless I log out',
+			login_check_remember_infinite: 'Keep me logged in on this computer until I log out',
+			login_btn_log_in: 'Log in',
+			
+			login_noact_title: 'Account error',
+			login_noact_msg_intro: 'It appears that your user account has not yet been activated.',
+			login_noact_solution_none: 'Your account was most likely deactivated by an administrator. Please contact the site administration for further assistance.',
+			login_noact_solution_user: 'Please check your e-mail; you should have been sent a message with instructions on how to activate your account. If you do not receive an e-mail from this site within 24 hours, please contact the site administration for further assistance.',
+			login_noact_solution_admin: 'This website has been configured so that all user accounts must be activated by the administrator before they can be used, so your account will most likely be activated the next time an administrator visits the site.',
+			login_noact_msg_logout_success_title: 'Logged out',
+			login_noact_msg_logout_success_body: 'You have successfully been logged out. All cookies cleared.',
+			login_noact_msg_ask_admins: 'If you are having trouble or did not receive the e-mail, you can request account activation from the administrators of this site.',
+			login_noact_msg_admins_just_asked: 'A request has just been sent to the administrators of this site. They will be able to activate your account or send you another activation e-mail if needed.',
+			login_noact_msg_admins_asked: 'There is an active request in the administrators\' control panel for your account to be activated.',
+			login_noact_btn_request_activation: 'Request account activation',
+			login_noact_btn_log_out: 'Log out',
+			
+			login_ajax_fetching_key: 'Fetching an encryption key...',
+			login_ajax_generating_key: 'Performing Diffie-Hellman key generation...',
+			login_ajax_prompt_title: 'Please enter your username and password to continue.',
+			login_ajax_prompt_title_elev: 'You are requesting a sensitive operation.',
+			login_ajax_prompt_body_elev: 'Please re-enter your login details to verify your identity.',
+			login_ajax_link_fullform: '<a href="%link_full_form%">HTML login</a>',
+			login_ajax_link_fullform_dh: 'Don\'t stop this process even if your browser asks you to. If this takes more than 10 seconds, you might want to try the <a href="%link_full_form%">full login form</a>.',
+			login_ajax_link_forgotpass: '<a href="%forgotpass_link%">Forgot password</a>',
+			login_ajax_createaccount_blurb: '<a href="%reg_link%">Register</a>',
+			login_ajax_loggingin: 'Logging in...',
+			login_ajax_msg_used_temp_pass: 'You have logged in using a temporary password. Before you can log in, you must finish resetting your password. Do you want to reset your real password now?',
+			login_ajax_check_remember: 'Stay logged in (%session_length% %length_units%)',
+			login_ajax_check_remember_infinite: 'Stay logged in',
+			login_ajax_check_dh: 'Encrypt my details (<a class="no_external" href="http://docs.enanocms.org/Help:Appendix_B#dh" onclick="window.open(this.href); return false;">What?</a>)',
+			login_ajax_check_dh_ie: 'Browser doesn\'t support encryption (<a class="no_external" href="http://docs.enanocms.org/Help:Appendix_B#dh" onclick="window.open(this.href); return false;">What?</a>)',
+			login_ajax_err_crypto: 'Encryption failed.',
+			login_ajax_err_crypto_details: 'If you have a Javascript debugger, details are available on the console.',
+			login_ajax_err_crypto_link: 'Use HTML login interface',
+			
+			err_login_generic_title: 'There was an error in the login process',
+			err_key_not_found: 'Enano couldn\'t look up the encryption key used to encrypt your password. This most often happens if a cache rotation occurred during your login attempt, or if you refreshed the login page.',
+			err_key_not_found_cleared: 'It seems that the list of encryption keys used for login information has reached its maximum length, thus preventing new keys from being inserted. The list has been automatically cleared. Please try logging in again; if you are still unable to log in, please contact the site administration.',
+			err_dh_key_not_found: 'Enano couldn\'t retrieve the private key used for the high-strength encrypted logon. It is possible that the list of keys was cleared during your logon process as this happens approximately once every 72 hours. Please try logging in again; if you are still unable to log in, please contact the site administration.',
+			err_dh_key_not_numeric: 'The Diffie-Hellman public key you sent through was not an arbitrary-precision decimal integer.',
+			err_dh_hash_no_match: 'The Diffie-Hellman key was not calculated correctly by one of the parties (I got a different shared secret than you did).',
+			err_userinfo_decode_failed: 'An internal error occurred while decrypting the encrypted information packet.',
+			err_key_wrong_length: 'The encryption key was the wrong length.',
+			err_too_big_for_britches: 'You are trying to authenticate at a level that your user account does not permit.',
+			err_invalid_credentials: 'You have entered an invalid username or password. Please enter your login details again.',
+			err_invalid_credentials_lockout: ' You have used up %fails% out of %threshold% login attempts. After you have used up all %threshold% login attempts, you will be locked out from logging in for %duration% minutes.',
+			err_invalid_credentials_captcha: ' You have used up %fails% out of %threshold% login attempts. After you have used up all %threshold% login attempts, you will have to enter a visual confirmation code while logging in, effective for %duration% minutes.',
+			err_backend_fail: 'You entered the right credentials and everything was validated, but for some reason Enano couldn\'t register your session. This is an internal problem with the site and you are encouraged to contact site administration.',
+			err_locked_out_initial_lockout: 'Your computer is locked out from logging in because you entered invalid credentials too many times. This restriction will expire in %time_rem% minute%plural%.',
+			err_locked_out_initial_captcha: 'Please enter the correct visual confirmation code to log in. This restriction will expire in %time_rem% minute%plural%.',
+			err_lockout_bad_captcha: 'The confirmation code you entered was incorrect.',
+			err_admin_session_timed_out: 'Your session has timed out; please log in again using the form above.',
+			err_email_not_valid: 'The e-mail address you entered is invalid.',
+			
+			logout_success_title: 'Logged out',
+			logout_success_body: 'You have been successfully logged out, and all cookies have been cleared. You will now be transferred to the main page.',
+			logout_confirm_title: 'Log out of this website?',
+			logout_confirm_body_normal: 'Logging out will keep you from accessing your preferences and any special privileges your account might have until you log in again.',
+			logout_confirm_body_nelev: '%this.user_logout_confirm_body_normal%\n\nYou may also choose to discard your elevated authorization (gained when you re-entered your password). You will be asked for your password if you try to use any privileged functions.',
+			logout_confirm_btn_logout: 'Log out',
+			logout_confirm_btn_deauth: 'Forget authorization',
+			logout_confirm_title_elev: 'Are you sure you want to de-authenticate?',
+			logout_confirm_body_elev: 'If you de-authenticate, you will no longer be able to use the administration panel until you re-authenticate again. You may do so at any time using the Administration button on the sidebar.',
+			logout_confirm_btn_logout: 'Log Out',
+			logout_err_title: 'An error occurred during the logout process.',
+			// Unused at this point
+			logout_err_not_loggedin: 'You don\'t seem to be logged in.',
+			
+			// User levels
+			level_short_guest: 'Guest',
+			level_short_member: 'Member',
+			level_short_chpref: 'Sensitive preferences changeable',
+			level_short_mod: 'Moderator',
+			level_short_admin: 'Administrative',
+			level_short_unknown: 'Unknown (level %user_level%)',
+			level_long_guest: 'Low - guest privileges',
+			level_long_member: 'Standard - normal member level',
+			level_long_chpref: 'Medium - user can change his/her own e-mail address and password',
+			level_long_mod: 'High - moderator privileges',
+			level_long_admin: 'Highest - administrative privileges',
+			level_long_unknown: 'Unknown privileges (level %user_level%)',
+			
+			ban_msg_title: 'Ban notice',
+			ban_msg_body: 'You have been banned from this website. Please contact the site administrator for more information.',
+			ban_lbl_reason: 'Reason:',
+			
+			keepalive_info_title: 'About the keep-alive feature',
+			keepalive_info_body: 'Keep-alive is a new Enano feature that keeps your administrative session from timing out while you are using the administration panel. This feature can be useful if you are editing a large page or doing something in the administration interface that will take longer than 15 minutes.<br /><br />For security reasons, Enano mandates that high-privilege logins last only 15 minutes, with the time being reset each time a page is loaded (or, more specifically, each time the session API is started). The consequence of this is that if you are performing an action in the administration panel that takes more than 15 minutes, your session may be terminated. The keep-alive feature attempts to relieve this by sending a "ping" to the server every 10 minutes.<br /><br />Please note that keep-alive state is determined by a cookie. Thus, if you log out and then back in as a different administrator, keep-alive will use the same setting that was used when you were logged in as the first administrative user. In the same way, if you log into the administration panel under your account from another computer, keep-alive will be set to "off".<br /><br /><b>For more information:</b><br /><a href="http://docs.enanocms.org/Help:Appendix_B" onclick="window.open(this.href); return false;">Overview of Enano\'s security model</a>',
+			
+			type_guest: 'Guest',
+			type_member: 'Member',
+			type_mod: 'Moderator',
+			type_admin: 'Administrator',
+			
+			rank_member: '%this.user_type_member%',
+			rank_mod: '%this.user_type_mod%',
+			rank_admin: '%this.user_type_admin%',
+			rank_guest: '%this.user_type_guest%',
+			
+			msg_elev_timed_out: '<b>Your administrative session has timed out.</b> <a href="%login_link%" onclick="ajaxLogonToElev(); return false;">Log in again</a>',
+			
+			reg_err_locked_out: 'Registration is disabled because you are currently locked out from logging in. Please wait %time% minutes before attempting to register again.',
+			reg_err_captcha: 'The confirmation code you entered was incorrect.',
+			reg_err_disabled_title: 'Registration disabled',
+			reg_err_disabled_body: 'The administrator has disabled the registration of new accounts on this site.',
+			reg_err_disabled_body_adminblurb: 'Oops...it seems that you <em>are</em> the administrator...hehe...you can also <a href="%reg_link%">force account registration to work</a>.',
+			reg_err_username_invalid: 'Your username must be at least two characters in length and may not contain any of the following characters: &lt; &gt; _ &amp; ? \' " % / \\.',
+			// Not exactly an error
+			reg_err_password_good: 'The password you entered is valid.',
+			reg_err_alert_password_tooshort: 'Your password must be 6 characters or greater in length.',
+			reg_err_alert_password_nomatch: 'The passwords you entered do not match.',
+			reg_err_missing_key: 'Couldn\'t look up public encryption key',
+			reg_err_accept_tou: 'Please read and accept the Terms of Use before creating your account.',
+			reg_err_username_banned_chars: 'The username you chose contains invalid characters.',
+			reg_err_dupe_username: 'The username you selected is already in use by another user.',
+			reg_err_dupe_username_email: 'The username and e-mail address you selected are already in use by another user.',
+			reg_err_dupe_username_email_realname: 'The username, e-mail address, and real name you selected are already in use by another user.',
+			reg_err_dupe_email: 'The e-mail address you selected is already in use by another user.',
+			reg_err_dupe_email_realname: 'The e-mail address and real name you selected are already in use by another user.',
+			reg_err_dupe_realname: 'The real name you selected is already in use by another user.',
+			reg_err_password_too_weak: 'The password you entered did not meet the complexity requirements for this site. Please choose a stronger password.',
+			reg_err_actmail_failed: 'The activation e-mail could not be sent due to an internal error. This could possibly be due to an incorrect SMTP configuration. A request has been sent to the administrator to activate your account for you.',
+			
+			reg_msg_greatercontrol: 'A user account enables you to have greater control over your browsing experience.',
+			reg_msg_table_title: 'Create a user account',
+			reg_msg_table_subtitle: 'Please tell us a little bit about yourself.',
+			reg_msg_username_checking: 'Checking availability...',
+			reg_msg_username_available: 'This username is available.',
+			reg_msg_username_unavailable: 'This username is already taken.',
+			reg_msg_password_length: 'Your password must be at least six characters in length.',
+			reg_msg_password_score: 'It needs to score at least <b>%config.pw_strength_minimum%</b> for your registration to be accepted.',
+			reg_msg_password_needmatch: 'The passwords you entered do not match.',
+			reg_msg_email_activuser: 'An e-mail with an account activation key will be sent to this address, so please ensure that it is correct.',
+			reg_msg_realname_optional: 'Giving your real name is totally optional. If you choose to provide your real name, it will be used to provide attribution for any edits or contributions you may make to this site.',
+			reg_msg_captcha_pleaseenter: 'Please enter the code shown in the image to the right into the text box. The code is case-insensitive and the numeral zero is never used. This process helps to ensure that this registration is not being performed by an automated bot. If the image to the right is illegible, you can <a %regen_flags%>generate a new image</a>.',
+			reg_msg_captcha_blind: 'If you are visually impaired or otherwise cannot read the text shown to the right, please contact the site management and they will create an account for you.',
+			reg_msg_please_read_tou:'Please read and agree to the following terms before registering. By continuing to create an account you agree to be legally bound by the terms below.',
+			reg_msg_success_title: 'Registration successful',
+			reg_msg_success_body: 'Thank you for registering, your user account has been created.',
+			reg_msg_success_activ_none: 'You may now <a href="%login_link%">log in</a> with the username and password that you created.',
+			reg_msg_success_activ_user: 'Because this site requires account activation, you have been sent an e-mail with further instructions. Please follow the instructions in that e-mail to continue your registration.',
+			reg_msg_success_activ_admin: 'Because this site requires administrative account activation, you cannot use your account at the moment. A notice has been sent to the site administration team that will alert them that your account has been created.',
+			reg_msg_success_activ_coppa: 'However, in compliance with the Childrens\' Online Privacy Protection Act, you must have your parent or legal guardian activate your account. Please ask them to check their e-mail for further information.',
+			
+			reg_lbl_field_username: 'Preferred username:',
+			reg_lbl_field_password: 'Password:',
+			reg_lbl_field_password_confirm: 'Enter your password again to confirm.',
+			reg_lbl_field_email: 'E-mail address:',
+			reg_lbl_field_email_coppa: 'Your parent or guardian\'s e-mail address:',
+			reg_lbl_field_realname: 'Real name:',
+			reg_lbl_field_captcha: 'Visual confirmation',
+			reg_lbl_field_captcha_code: 'Code:',
+			reg_lbl_field_tou: 'I agree to abide by the terms and conditions stated above',
+			
+			reg_btn_create_account: 'Create my account',
+			
+			reg_coppa_title: 'Before you can register, please tell us your age.',
+			reg_coppa_link_atleast13: 'I was born <b>on or before</b> %yo13_date% and am <b>at least</b> 13 years of age',
+			reg_coppa_link_not13: 'I was born <b>after</b> %yo13_date% and am <b>less than</b> 13 years of age',
+			
+			reg_activation_email_subject: '%config.site_name% website account activation',
+			reg_activation_email: 'Dear %username%,
 Thank you for registering on %config.site_name%. Your account creation is almost complete. To complete the registration process, please click the following link or paste it into your web browser:
 
-  %activation_link%
-  
+	%activation_link%
+	
 Sincerely yours,
 The %config.site_name% administration team',
 
-      reg_activation_email_coppa: 'Dear parent or legal guardian,
+			reg_activation_email_coppa: 'Dear parent or legal guardian,
 A child under the username %username% recently registered on our website. The child provided your e-mail address as the one of his or her authorized parent or legal guardian, and to comply with the United States Childrens\' Online Privacy Protection act, we ask that all parents of children ages 13 or under please mail us a written form authorizing their child\'s use of our website.
 
 If you wish for your child to be allowed access to our website, please print and fill out the form below, and mail it to this address:
@@ -244,351 +244,351 @@
 
 Sincerely yours,
 %admin_user% and the %config.site_name% administration team',
-      
-      autofill_heading_suggestions: 'Username suggestions',
-      autofill_msg_no_suggestions: 'No suggestions',
-      
-      // CSRF confirmation form
-      csrf_confirm_title: 'Invalid form confirmation key',
-      csrf_confirm_body: 'Your browser sent an invalid confirmation key for a form. Your session may have expired, or you may have been redirected here from a remote site in an attack known as Cross-Site Request Forgery (CSRF). If you are sure you want to continue with this action, you may click the button below. Otherwise, return to the main page and do not proceed.',
-      csrf_confirm_btn_viewrequest: 'View request and form data',
-      csrf_confirm_btn_continue: 'Continue',
-    },
-    usercp: {
-      // Meta
-      sec_profile: 'Profile/membership',
-      sec_pm: 'Private messages',
-      
-      sec_profile_emailpassword: 'Edit e-mail address and password',
-      sec_profile_signature: 'Edit signature',
-      sec_profile_publicinfo: 'Edit public profile',
-      sec_profile_usergroups: 'Group memberships',
-      sec_profile_avatar: 'Avatar settings',
-      
-      sec_pm_inbox: 'Inbox',
-      sec_pm_outbox: 'Outbox',
-      sec_pm_sent: 'Sent items',
-      sec_pm_drafts: 'Drafts',
-      sec_pm_archive: 'Archive',
-      
-      btn_memberlist: 'list of registered members',
-      
-      // CP home
-      intro_heading_main: '%username%, welcome to your control panel',
-      intro: '<p>Your User Control Panel lets you change settings on the site.</p><p>If you haven\'t created a <a href="%userpage_link%">user page</a> yet, consider doing so - it\'s your free writing space. It\'s also where information you put on this page can be viewed by others. People can also leave <a href="%userpage_link%#do:comments">comments</a> on your user page.</p>',
-      
-      // E-mail / password change form
-      emailpassword_title: 'Change E-mail Address or Password',
-      emailpassword_err_email_no_match: 'The e-mail addresses you entered did not match.',
-      emailpassword_err_list: 'The following errors were encountered while saving your e-mail address:',
-      emailpassword_err_title: 'Error updating e-mail address',
-      emailpassword_err_demo: 'You can\'t change your password in demo mode.',
-      emailpassword_err_password_too_short: 'The new password must be 6 characters or greater in length.',
-      emailpassword_err_password_too_weak: 'Your password did not meet the complexity score requirement for this site. Your password scored %score%, while a score of at least %config.pw_strength_minimum% is needed.',
-      emailpassword_msg_change_disabled: 'You cannot change your password here because either a single sign-on is being used and your password is stored in a different location, or password authentication is disabled for this site.',
-      emailpassword_msg_change_disabled_url: 'To manage or change your login details, use the following link:',
-      emailpassword_msg_profile_success: 'Profile changed',
-      emailpassword_msg_pass_success: 'Password changed',
-      emailpassword_msg_email_success: 'E-mail address changed',
-      emailpassword_msg_need_activ_user: 'Your profile has been changed. Since e-mail activation is required on this site, you will need to re-activate your account to continue. An e-mail has been sent to the new e-mail address with an activation link. You must click that link in order to log in again.',
-      emailpassword_msg_need_activ_admin: 'Your profile has been changed. Since account activation is required on this site, you will need to re-activate your account to continue. An e-mail has been sent to the new e-mail address with an activation link. You must click that link in order to log in again.',
-      emailpassword_msg_password_changed: 'Your profile has been changed successfully. You will now be redirected back to the user control panel.',
-      emailpassword_err_password_no_match: 'The passwords you entered do not match.',
-      emailpassword_grp_chpasswd: 'Change password',
-      emailpassword_field_newpass: 'Type a new password:',
-      emailpassword_field_newpass_confirm: 'Type the password again to confirm:',
-      emailpassword_msg_password_min_score: 'Your password needs to score at least <b>%config.pw_strength_minimum%</b> in order to be accepted.',
-      // The following is NOT an in-joke. ;-)
-      emailpassword_grp_chemail: 'Change e-mail address',
-      emailpassword_field_newemail: 'New e-mail address:',
-      emailpassword_field_newemail_confirm: 'Confirm e-mail address:',
-      
-      // Signature editor
-      signature_title: 'Editing signature',
-      signature_msg_saved: 'Your signature has been saved.',
-      signature_btn_save: 'Save signature',
-      
-      // Additional profile info
-      publicinfo_title: 'Editing public profile',
-      publicinfo_msg_save_success: 'Your profile has been updated.',
-      publicinfo_heading_main: 'Your public profile',
-      publicinfo_note_optional: 'Please note that all of the information you enter here will be <b>publicly viewable.</b> All of the fields on this page are optional and may be left blank if you so desire.',
-      publicinfo_field_realname: 'Real name:',
-      publicinfo_field_language: 'Preferred language:',
-      publicinfo_field_language_hint: 'Select the language special pages and page controls should appear in.',
-      publicinfo_field_changetheme_title: 'Change theme:',
-      publicinfo_field_changetheme_hint: 'If you don\'t like the look of the site, need a visual break, or are just curious, we might have some different themes for you to try out!',
-      publicinfo_field_changetheme: 'Change my theme...',
-      publicinfo_field_dateformat: 'Date format:',
-      publicinfo_field_timeformat: 'Time format:',
-      publicinfo_field_timezone: 'Time zone:',
-      publicinfo_field_timezone_hint: 'Select the time zone you live in and when Daylight Savings Time occurs, if at all.',
-      publicinfo_field_dst: 'Daylight saving time:',
-      publicinfo_field_usertitle_title: 'User title:',
-      publicinfo_field_usertitle_hint: 'This can be some text that will be displayed underneath your username.',
-      publicinfo_field_rank_title: 'Display rank:',
-      publicinfo_field_rank_hint: 'Select the rank you want to be shown with your username.',
-      publicinfo_th_im: 'Instant messenger contact information',
-      publicinfo_field_aim: 'AIM handle:',
-      publicinfo_field_wlm: '<acronym title="Windows&trade; Live Messenger">WLM</acronym> handle:<br /><small>If you don\'t specify the domain (@whatever.com), "@hotmail.com" will be assumed.</small>',
-      publicinfo_field_yim: 'Yahoo! IM handle:',
-      publicinfo_field_xmpp: 'Jabber&trade;/XMPP handle:',
-      publicinfo_th_contact: 'Extra contact information',
-      publicinfo_field_homepage: 'Your homepage:<br /><small>Please remember the http:// prefix.</small>',
-      publicinfo_field_location: 'Your location:',
-      publicinfo_field_job: 'Your job:',
-      publicinfo_field_hobbies: 'Your hobbies:',
-      publicinfo_field_email_public: 'E-mail address is public',
-      publicinfo_field_email_public_hint: 'If this is checked, your e-mail address will be displayed on your user page. To protect your address from spambots, your e-mail address will be encrypted.',
-      publicinfo_field_jsfx: 'Disable animation effects on pages',
-      publicinfo_field_jsfx_hint: 'If you aren\'t big on eye candy, this can speed up applets like the ACL manager and confirmation dialogs.',
-      publicinfo_btn_save: 'Save profile',
-      
-      // Avatar management
-      avatar_err_disabled_title: 'Avatar support is disabled.',
-      avatar_err_disabled_body: 'The administrator has not enabled avatar support for this site.',
-      avatar_table_title: 'Avatar settings',
-      avatar_label_current: 'Current avatar:',
-      avatar_image_alt: '%username%\'s avatar',
-      avatar_image_none: 'You don\'t have an avatar currently.',
-      avatar_lbl_change: 'Change your avatar:',
-      avatar_lbl_keep: 'Keep my current avatar',
-      avatar_lbl_remove: 'Delete my avatar',
-      avatar_lbl_set_http: 'Upload a new avatar from the Web',
-      avatar_lbl_set_file: 'Upload a new avatar from my computer',
-      avatar_lbl_set_gravatar: 'Use my Gravatar',
-      avatar_lbl_url: 'URL to image:',
-      avatar_lbl_url_desc: 'This must start with the <tt>http://</tt> prefix and must be a valid URL. The image will be copied from the existing URL to this server - dynamic avatars are not supported.',
-      avatar_lbl_file: 'Upload file:',
-      avatar_lbl_file_desc: 'Your browser needs to support file uploads for this option to work.',
-      avatar_limits: 'The maximum file size is %config.avatar_max_size% bytes, and maximum dimensions are %config.avatar_max_width% &#215; %config.avatar_max_height% pixels; we\'ll try resizing it if necessary. Allowed formats are PNG, GIF, and JPEG.',
-      avatar_delete_success: 'Your avatar has been deleted.',
-      avatar_bad_write: 'Either the remote server had trouble finding the image, or your image exceeded the allowed file size.',
-      avatar_bad_filetype: 'The file you selected is invalid. You must choose a file in PNG, JPEG, or GIF format.',
-      avatar_disallowed_animation: 'You have chosen an animated image, which is not allowed. Please choose a non-animated image.',
-      avatar_corrupt_image: 'The image you selected is corrupt. Please choose another image.',
-      avatar_too_large: 'The image you uploaded exceeds the maximum dimensions (%config.avatar_max_width% &#215; %config.avatar_max_height%px) allowed on this site. Please choose another image.',
-      avatar_move_failed: 'Your image was accepted, but there was a problem moving the image file to the correct location.',
-      avatar_upload_success: 'Your avatar has been updated.',
-      avatar_file_too_large: 'The image you uploaded exceeds the maximum file size allowed for avatars on this site.',
-      avatar_invalid_url: 'The URL you entered to your avatar image is not valid. Please enter another URL and try again.',
-      avatar_gravatar_success: 'Your Gravatar will now be used as your avatar on this site.',
-      avatar_gravatar_rating_g: 'The highest allowed rating for your Gravatar image is <b>G</b>. Images must be suitable for display on all websites with any audience type.',
-      avatar_gravatar_rating_pg: 'The highest allowed rating for your Gravatar image is <b>PG</b>. Rude gestures, lesser swear words, mild violence, and mildly provocatively dressed individuals are permitted.',
-      avatar_gravatar_rating_r: 'The highest allowed rating for your Gravatar image is <b>R</b>. Nudity and violence must be tasteful.',
-      avatar_gravatar_rating_x: 'The highest allowed rating for your Gravatar image is <b>X</b>.',
-      avatar_link_gravatar_info: 'Learn about Gravatar',
-      
-      // Password strength widget
-      pwstrength_score_verystrong: 'Very strong (score: %score%)',
-      pwstrength_score_strong: 'Strong (score: %score%)',
-      pwstrength_score_good: 'Good (score: %score%)',
-      pwstrength_score_fair: 'Fair (score: %score%)',
-      pwstrength_score_weak: 'Weak (score: %score%)',
-    },
-    groupcp: {
-      status_mod: 'You are a moderator of this group.',
-      status_member: 'You are a member of this group.',
-      status_not_member: 'You are not a member of this group.',
-      
-      err_state_system_group: 'Because this is a system group, you can\'t make it open or allow membership requests.',
-      err_user_not_found: 'The username you entered could not be found.',
-      
-      type_hidden: 'Hidden group',
-      type_closed: 'Closed group',
-      type_request: 'Members can request to join',
-      type_open: 'Anyone can join',
-      
-      lbl_current_memberships: 'Current group memberships:',
-      lbl_non_memberships: 'Groups you are outside of:',
-      lbl_group_name: 'Group name:',
-      lbl_status: 'Membership status:',
-      lbl_state: 'Group state:',
-      lbl_make_mod: 'User is a group moderator',
-      lbl_username: 'Username:',
-      lbl_moderator: 'Group moderator:',
-      
-      msg_membership_requested: 'A request has been sent to the moderator(s) of this group to add you.',
-      msg_status_pending: '(Your request to join is awaiting approval)',
-      msg_no_mods: 'This group has no moderators.',
-      msg_no_members: 'This group has no members.',
-      msg_system_group: '(system group)',
-      msg_pending_updated: 'Pending members status updated successfully.',
-      msg_state_updated: 'The group state was updated.',
-      msg_user_already_in_mod_updated: 'The user "%username%" is already in this group, so their moderator status was updated.',
-      msg_user_already_in: 'The user "%username%" is already in this group.',
-      msg_user_added: 'The user "%username%" has been added to this usergroup.',
-      msg_self_added: 'You have been added to this group.',
-      
-      btn_view: 'View information',
-      btn_request_join: 'Request membership',
-      btn_join: 'Join this group',
-      btn_approve_pending: 'Approve membership',
-      btn_reject_pending: 'Reject membership',
-      btn_remove_selected: 'Remove selected users',
-      btn_add_member: 'Add member',
-      
-      grp_administrators: 'Administrators',
-      grp_moderators: 'Moderators',
-      
-      th_select_group: 'Group membership details',
-      th_group_info: 'Group information',
-      th_pending_memberships: 'Pending memberships',
-      th_group_members: 'Group members',
-      th_group_mods: 'Group moderators',
-      th_add_member: 'Add a new member to this group',
-      th_username: 'Username',
-      th_email: 'E-mail',
-      th_reg_time: 'Registered',
-      th_comments: 'Total comments',
-      th_select: 'Select',
-      th_remove: 'Remove?',
-    },
-    privmsgs: {
-      err_need_login: 'You need to be <a href="%login_link%">logged in</a> to view private messages.',
-      err_not_authorized_read: 'You are not authorized to view this message.',
-      err_not_authorized_edit: 'You are not authorized to alter this message.',
-      err_send_submit: 'Your message could not be sent because the following problems were encountered:',
-      err_need_username: 'Please enter the username to which you want to send your message.',
-      err_need_subject: 'Please enter a subject for your message.',
-      err_need_message: 'Please enter a message to send.',
-      err_limit_exceeded_title: 'Recipient limit exceeded',
-      err_limit_exceeded_body: 'You can only send this message to a maximum of %limit% users.',
-      err_folder_not_exist: 'The folder "%folder_name%" does not exist. Return to your <a href="%inbox_url%">inbox</a>.',
-      
-      msg_message_status: 'Message status',
-      msg_message_moved: 'Your message has been moved to the folder "%folder%".',
-      msg_message_deleted: 'The message has been deleted.',
-      msg_message_sent: 'Your message has been sent. You may edit the message if you wish; one copy for each recipient will be in your outbox until each recipient has read it. Return to your <a href="%inbox_link%">inbox</a>.',
-      msg_draft_saved: 'Your message has been saved to your Drafts folder.',
-      msg_no_messages: 'No messages in this folder.',
-      
-      lbl_message_from: 'Private message from %sender%',
-      lbl_subject: 'Subject:',
-      lbl_date: 'Date:',
-      lbl_message: 'Message:',
-      lbl_compose_th: 'Compose new private message',
-      lbl_compose_to: 'To:',
-      lbl_compose_to_max: 'Separate multiple names with a single comma; you may send this message to up to <b>%limit%</b> users.',
-      lbl_edit_th: 'Edit draft',
-      
-      btn_send_reply: 'Send reply',
-      btn_archive: 'Archive message',
-      btn_return_to_inbox: 'Return to inbox',
-      btn_send: 'Send message',
-      btn_savedraft: 'Save as draft',
-      btn_archive_selected: 'Archive selected',
-      btn_delete_selected: 'Delete selected',
-      btn_delete_all: 'Delete all',
-      btn_compose: 'New message',
-      
-      sidebar_th_privmsgs: 'Private messages',
-      folder_inbox: 'Inbox',
-      folder_outbox: 'Outbox',
-      folder_sent: 'Sent items',
-      folder_drafts: 'Drafts',
-      folder_archive: 'Archive',
-      sidebar_th_buddies: 'Buddies',
-      sidebar_friend_list: 'Friend list',
-      sidebar_foe_list: 'Foe list',
-      
-      folder_th_foldername: 'Folder:',
-      folder_th_to: 'To',
-      folder_th_from: 'From',
-      folder_th_subject: 'Subject',
-      folder_th_date: 'Date',
-      folder_th_mark: 'Mark',
-      
-      // Buddy / foe lists
-      th_buddy_list: 'Buddy list for %username%',
-      msg_no_buddies: 'No buddies in your list.',
-      btn_pm_all_buddies: 'Send a PM to all buddies',
-      heading_add_buddy: 'Add a new friend',
-      btn_add: 'Add',
-      lbl_username: 'Username:',
-      btn_buddy_remove: 'Remove',
-      btn_buddy_send_pm: 'Send private message',
-      
-      th_foe_list: 'Foe list for %username%',
-      msg_no_foes: 'No foes in your list.',
-      heading_add_foe: 'Add a new foe',
-      
-      // AJAX interface (up and coming)
-      ajax_err_need_js: 'It looks like your browser doesn\'t have support for Javascript. You\'ll need to have Javascript support in order to use the new Private Message interface. You can also switch to the <a href="%basic_link%">old interface</a>, which doesn\'t require Javascript support.',
-      ajax_err_json: 'The server had a problem processing your request.',
-      
-      ajax_btn_compose: 'Compose message',
-      ajax_btn_archive: 'Archive',
-      ajax_btn_unarchive: 'Restore to inbox',
-      ajax_btn_mark_read: 'Mark as read',
-      ajax_btn_mark_unread: 'Mark as unread',
-      ajax_btn_delete: 'Move to trash',
-      ajax_btn_delete_forever: 'Delete forever',
-      ajax_btn_refresh: 'Refresh',
-      ajax_btn_reply: 'Reply',
-      
-      ajax_folder_inbox: 'Inbox',
-      ajax_folder_starred: 'Starred',
-      ajax_folder_sent: 'Sent messages',
-      ajax_folder_drafts: 'Drafts',
-      ajax_folder_archive: 'Archive',
-      ajax_folder_trash: 'Trash',
-      
-      ajax_msg_loading: 'Loading...',
-      
-      ajax_lbl_sender: '%sender% to %recipient%',
-      ajax_me: 'me',
-      
-      ajax_teaser_inbox: 'No new mail.',
-      ajax_teaser_starred: 'You haven\'t starred any messages yet. Starring a message lets you give it a special status that separates it from the rest of your mail so it\'s easier to find. You can star a message by clicking the small gray star next to a message in your inbox or archive.',
-      ajax_teaser_sent: 'You haven\'t sent any messages yet. When you send a message, a copy of it will appear here.',
-      ajax_teaser_drafts: 'You don\'t have any drafts. When you save a message so you can finish writing it later, it will appear here.',
-      ajax_teaser_archive: 'You haven\'t archived any messages yet. Archive a message when you want to remove it from your inbox but keep a copy of it around.',
-      ajax_teaser_trash: 'No deleted messages. Delete a message by checking it in another folder and clicking Delete. Deleting a message from this folder will remove it permanently.',
-      
-      ajax_no_subject: '[No subject]',
-      
-      ajax_compose_lbl_to: 'To:',
-      ajax_compose_lbl_subject: 'Subject:',
-      ajax_compose_btn_send: 'Send message',
-      ajax_compose_btn_save: 'Save draft',
-      ajax_compose_btn_cancel: 'Discard',
-      ajax_compose_btn_cancel_confirm: 'Discard message',
-      
-      ajax_compose_msg_confirm_discard_title: 'Discard message?',
-      ajax_compose_msg_confirm_discard_body: 'Save this message as a draft if you want to avoid losing it. Discarding this message will not delete any existing drafts.',
-    },
-    userfuncs: {
-      
-      // Special:Contributions
-      contribs_err_no_user: 'You need to select a user to view contributions for.',
-      contribs_heading_edits: 'Page edits',
-      contribs_msg_no_edits: 'This user has not made any edits.',
-      contribs_heading_other: 'Other changes made by this user',
-      contribs_msg_no_other: 'This user has not made any non-editing changes.',
-      
-      // Special:ChangeStyle
-      changetheme_heading_theme: 'Please select a new theme:',
-      changetheme_heading_style: 'Please select a stylesheet:',
-      changetheme_btn_continue: 'Continue',
-      changetheme_btn_allclear: 'Change style',
-      changetheme_success_title: 'Theme changed',
-      changetheme_success_body: 'Your theme preferences have been updated. Redirecting you to the last viewed page...',
-      
-      // Special:ActivateAccount
-      activate_err_badlink_title: 'Account activation error',
-      activate_err_badlink_body: 'This page can only be accessed using links sent to users via e-mail.',
-      activate_err_bad_key: 'The activation key was probably incorrect, or the account is already active.',
-      activate_success_title: 'Activation successful',
-      activate_success_body: 'Your account is now active. Thank you for registering.',
-      
-      // Special:PasswordReset
-      passreset_blurb_line1: 'Don\'t worry, it happens to the best of us.',
-      passreset_blurb_line2: 'To reset your password, just enter your username below, and a new password will be e-mailed to you.',
-      passreset_lbl_username: 'Username:',
-      passreset_btn_mailpasswd: 'Mail new password',
-      passreset_email: "Dear %username%,
-    
+			
+			autofill_heading_suggestions: 'Username suggestions',
+			autofill_msg_no_suggestions: 'No suggestions',
+			
+			// CSRF confirmation form
+			csrf_confirm_title: 'Invalid form confirmation key',
+			csrf_confirm_body: 'Your browser sent an invalid confirmation key for a form. Your session may have expired, or you may have been redirected here from a remote site in an attack known as Cross-Site Request Forgery (CSRF). If you are sure you want to continue with this action, you may click the button below. Otherwise, return to the main page and do not proceed.',
+			csrf_confirm_btn_viewrequest: 'View request and form data',
+			csrf_confirm_btn_continue: 'Continue',
+		},
+		usercp: {
+			// Meta
+			sec_profile: 'Profile/membership',
+			sec_pm: 'Private messages',
+			
+			sec_profile_emailpassword: 'Edit e-mail address and password',
+			sec_profile_signature: 'Edit signature',
+			sec_profile_publicinfo: 'Edit public profile',
+			sec_profile_usergroups: 'Group memberships',
+			sec_profile_avatar: 'Avatar settings',
+			
+			sec_pm_inbox: 'Inbox',
+			sec_pm_outbox: 'Outbox',
+			sec_pm_sent: 'Sent items',
+			sec_pm_drafts: 'Drafts',
+			sec_pm_archive: 'Archive',
+			
+			btn_memberlist: 'list of registered members',
+			
+			// CP home
+			intro_heading_main: '%username%, welcome to your control panel',
+			intro: '<p>Your User Control Panel lets you change settings on the site.</p><p>If you haven\'t created a <a href="%userpage_link%">user page</a> yet, consider doing so - it\'s your free writing space. It\'s also where information you put on this page can be viewed by others. People can also leave <a href="%userpage_link%#do:comments">comments</a> on your user page.</p>',
+			
+			// E-mail / password change form
+			emailpassword_title: 'Change E-mail Address or Password',
+			emailpassword_err_email_no_match: 'The e-mail addresses you entered did not match.',
+			emailpassword_err_list: 'The following errors were encountered while saving your e-mail address:',
+			emailpassword_err_title: 'Error updating e-mail address',
+			emailpassword_err_demo: 'You can\'t change your password in demo mode.',
+			emailpassword_err_password_too_short: 'The new password must be 6 characters or greater in length.',
+			emailpassword_err_password_too_weak: 'Your password did not meet the complexity score requirement for this site. Your password scored %score%, while a score of at least %config.pw_strength_minimum% is needed.',
+			emailpassword_msg_change_disabled: 'You cannot change your password here because either a single sign-on is being used and your password is stored in a different location, or password authentication is disabled for this site.',
+			emailpassword_msg_change_disabled_url: 'To manage or change your login details, use the following link:',
+			emailpassword_msg_profile_success: 'Profile changed',
+			emailpassword_msg_pass_success: 'Password changed',
+			emailpassword_msg_email_success: 'E-mail address changed',
+			emailpassword_msg_need_activ_user: 'Your profile has been changed. Since e-mail activation is required on this site, you will need to re-activate your account to continue. An e-mail has been sent to the new e-mail address with an activation link. You must click that link in order to log in again.',
+			emailpassword_msg_need_activ_admin: 'Your profile has been changed. Since account activation is required on this site, you will need to re-activate your account to continue. An e-mail has been sent to the new e-mail address with an activation link. You must click that link in order to log in again.',
+			emailpassword_msg_password_changed: 'Your profile has been changed successfully. You will now be redirected back to the user control panel.',
+			emailpassword_err_password_no_match: 'The passwords you entered do not match.',
+			emailpassword_grp_chpasswd: 'Change password',
+			emailpassword_field_newpass: 'Type a new password:',
+			emailpassword_field_newpass_confirm: 'Type the password again to confirm:',
+			emailpassword_msg_password_min_score: 'Your password needs to score at least <b>%config.pw_strength_minimum%</b> in order to be accepted.',
+			// The following is NOT an in-joke. ;-)
+			emailpassword_grp_chemail: 'Change e-mail address',
+			emailpassword_field_newemail: 'New e-mail address:',
+			emailpassword_field_newemail_confirm: 'Confirm e-mail address:',
+			
+			// Signature editor
+			signature_title: 'Editing signature',
+			signature_msg_saved: 'Your signature has been saved.',
+			signature_btn_save: 'Save signature',
+			
+			// Additional profile info
+			publicinfo_title: 'Editing public profile',
+			publicinfo_msg_save_success: 'Your profile has been updated.',
+			publicinfo_heading_main: 'Your public profile',
+			publicinfo_note_optional: 'Please note that all of the information you enter here will be <b>publicly viewable.</b> All of the fields on this page are optional and may be left blank if you so desire.',
+			publicinfo_field_realname: 'Real name:',
+			publicinfo_field_language: 'Preferred language:',
+			publicinfo_field_language_hint: 'Select the language special pages and page controls should appear in.',
+			publicinfo_field_changetheme_title: 'Change theme:',
+			publicinfo_field_changetheme_hint: 'If you don\'t like the look of the site, need a visual break, or are just curious, we might have some different themes for you to try out!',
+			publicinfo_field_changetheme: 'Change my theme...',
+			publicinfo_field_dateformat: 'Date format:',
+			publicinfo_field_timeformat: 'Time format:',
+			publicinfo_field_timezone: 'Time zone:',
+			publicinfo_field_timezone_hint: 'Select the time zone you live in and when Daylight Savings Time occurs, if at all.',
+			publicinfo_field_dst: 'Daylight saving time:',
+			publicinfo_field_usertitle_title: 'User title:',
+			publicinfo_field_usertitle_hint: 'This can be some text that will be displayed underneath your username.',
+			publicinfo_field_rank_title: 'Display rank:',
+			publicinfo_field_rank_hint: 'Select the rank you want to be shown with your username.',
+			publicinfo_th_im: 'Instant messenger contact information',
+			publicinfo_field_aim: 'AIM handle:',
+			publicinfo_field_wlm: '<acronym title="Windows&trade; Live Messenger">WLM</acronym> handle:<br /><small>If you don\'t specify the domain (@whatever.com), "@hotmail.com" will be assumed.</small>',
+			publicinfo_field_yim: 'Yahoo! IM handle:',
+			publicinfo_field_xmpp: 'Jabber&trade;/XMPP handle:',
+			publicinfo_th_contact: 'Extra contact information',
+			publicinfo_field_homepage: 'Your homepage:<br /><small>Please remember the http:// prefix.</small>',
+			publicinfo_field_location: 'Your location:',
+			publicinfo_field_job: 'Your job:',
+			publicinfo_field_hobbies: 'Your hobbies:',
+			publicinfo_field_email_public: 'E-mail address is public',
+			publicinfo_field_email_public_hint: 'If this is checked, your e-mail address will be displayed on your user page. To protect your address from spambots, your e-mail address will be encrypted.',
+			publicinfo_field_jsfx: 'Disable animation effects on pages',
+			publicinfo_field_jsfx_hint: 'If you aren\'t big on eye candy, this can speed up applets like the ACL manager and confirmation dialogs.',
+			publicinfo_btn_save: 'Save profile',
+			
+			// Avatar management
+			avatar_err_disabled_title: 'Avatar support is disabled.',
+			avatar_err_disabled_body: 'The administrator has not enabled avatar support for this site.',
+			avatar_table_title: 'Avatar settings',
+			avatar_label_current: 'Current avatar:',
+			avatar_image_alt: '%username%\'s avatar',
+			avatar_image_none: 'You don\'t have an avatar currently.',
+			avatar_lbl_change: 'Change your avatar:',
+			avatar_lbl_keep: 'Keep my current avatar',
+			avatar_lbl_remove: 'Delete my avatar',
+			avatar_lbl_set_http: 'Upload a new avatar from the Web',
+			avatar_lbl_set_file: 'Upload a new avatar from my computer',
+			avatar_lbl_set_gravatar: 'Use my Gravatar',
+			avatar_lbl_url: 'URL to image:',
+			avatar_lbl_url_desc: 'This must start with the <tt>http://</tt> prefix and must be a valid URL. The image will be copied from the existing URL to this server - dynamic avatars are not supported.',
+			avatar_lbl_file: 'Upload file:',
+			avatar_lbl_file_desc: 'Your browser needs to support file uploads for this option to work.',
+			avatar_limits: 'The maximum file size is %config.avatar_max_size% bytes, and maximum dimensions are %config.avatar_max_width% &#215; %config.avatar_max_height% pixels; we\'ll try resizing it if necessary. Allowed formats are PNG, GIF, and JPEG.',
+			avatar_delete_success: 'Your avatar has been deleted.',
+			avatar_bad_write: 'Either the remote server had trouble finding the image, or your image exceeded the allowed file size.',
+			avatar_bad_filetype: 'The file you selected is invalid. You must choose a file in PNG, JPEG, or GIF format.',
+			avatar_disallowed_animation: 'You have chosen an animated image, which is not allowed. Please choose a non-animated image.',
+			avatar_corrupt_image: 'The image you selected is corrupt. Please choose another image.',
+			avatar_too_large: 'The image you uploaded exceeds the maximum dimensions (%config.avatar_max_width% &#215; %config.avatar_max_height%px) allowed on this site. Please choose another image.',
+			avatar_move_failed: 'Your image was accepted, but there was a problem moving the image file to the correct location.',
+			avatar_upload_success: 'Your avatar has been updated.',
+			avatar_file_too_large: 'The image you uploaded exceeds the maximum file size allowed for avatars on this site.',
+			avatar_invalid_url: 'The URL you entered to your avatar image is not valid. Please enter another URL and try again.',
+			avatar_gravatar_success: 'Your Gravatar will now be used as your avatar on this site.',
+			avatar_gravatar_rating_g: 'The highest allowed rating for your Gravatar image is <b>G</b>. Images must be suitable for display on all websites with any audience type.',
+			avatar_gravatar_rating_pg: 'The highest allowed rating for your Gravatar image is <b>PG</b>. Rude gestures, lesser swear words, mild violence, and mildly provocatively dressed individuals are permitted.',
+			avatar_gravatar_rating_r: 'The highest allowed rating for your Gravatar image is <b>R</b>. Nudity and violence must be tasteful.',
+			avatar_gravatar_rating_x: 'The highest allowed rating for your Gravatar image is <b>X</b>.',
+			avatar_link_gravatar_info: 'Learn about Gravatar',
+			
+			// Password strength widget
+			pwstrength_score_verystrong: 'Very strong (score: %score%)',
+			pwstrength_score_strong: 'Strong (score: %score%)',
+			pwstrength_score_good: 'Good (score: %score%)',
+			pwstrength_score_fair: 'Fair (score: %score%)',
+			pwstrength_score_weak: 'Weak (score: %score%)',
+		},
+		groupcp: {
+			status_mod: 'You are a moderator of this group.',
+			status_member: 'You are a member of this group.',
+			status_not_member: 'You are not a member of this group.',
+			
+			err_state_system_group: 'Because this is a system group, you can\'t make it open or allow membership requests.',
+			err_user_not_found: 'The username you entered could not be found.',
+			
+			type_hidden: 'Hidden group',
+			type_closed: 'Closed group',
+			type_request: 'Members can request to join',
+			type_open: 'Anyone can join',
+			
+			lbl_current_memberships: 'Current group memberships:',
+			lbl_non_memberships: 'Groups you are outside of:',
+			lbl_group_name: 'Group name:',
+			lbl_status: 'Membership status:',
+			lbl_state: 'Group state:',
+			lbl_make_mod: 'User is a group moderator',
+			lbl_username: 'Username:',
+			lbl_moderator: 'Group moderator:',
+			
+			msg_membership_requested: 'A request has been sent to the moderator(s) of this group to add you.',
+			msg_status_pending: '(Your request to join is awaiting approval)',
+			msg_no_mods: 'This group has no moderators.',
+			msg_no_members: 'This group has no members.',
+			msg_system_group: '(system group)',
+			msg_pending_updated: 'Pending members status updated successfully.',
+			msg_state_updated: 'The group state was updated.',
+			msg_user_already_in_mod_updated: 'The user "%username%" is already in this group, so their moderator status was updated.',
+			msg_user_already_in: 'The user "%username%" is already in this group.',
+			msg_user_added: 'The user "%username%" has been added to this usergroup.',
+			msg_self_added: 'You have been added to this group.',
+			
+			btn_view: 'View information',
+			btn_request_join: 'Request membership',
+			btn_join: 'Join this group',
+			btn_approve_pending: 'Approve membership',
+			btn_reject_pending: 'Reject membership',
+			btn_remove_selected: 'Remove selected users',
+			btn_add_member: 'Add member',
+			
+			grp_administrators: 'Administrators',
+			grp_moderators: 'Moderators',
+			
+			th_select_group: 'Group membership details',
+			th_group_info: 'Group information',
+			th_pending_memberships: 'Pending memberships',
+			th_group_members: 'Group members',
+			th_group_mods: 'Group moderators',
+			th_add_member: 'Add a new member to this group',
+			th_username: 'Username',
+			th_email: 'E-mail',
+			th_reg_time: 'Registered',
+			th_comments: 'Total comments',
+			th_select: 'Select',
+			th_remove: 'Remove?',
+		},
+		privmsgs: {
+			err_need_login: 'You need to be <a href="%login_link%">logged in</a> to view private messages.',
+			err_not_authorized_read: 'You are not authorized to view this message.',
+			err_not_authorized_edit: 'You are not authorized to alter this message.',
+			err_send_submit: 'Your message could not be sent because the following problems were encountered:',
+			err_need_username: 'Please enter the username to which you want to send your message.',
+			err_need_subject: 'Please enter a subject for your message.',
+			err_need_message: 'Please enter a message to send.',
+			err_limit_exceeded_title: 'Recipient limit exceeded',
+			err_limit_exceeded_body: 'You can only send this message to a maximum of %limit% users.',
+			err_folder_not_exist: 'The folder "%folder_name%" does not exist. Return to your <a href="%inbox_url%">inbox</a>.',
+			
+			msg_message_status: 'Message status',
+			msg_message_moved: 'Your message has been moved to the folder "%folder%".',
+			msg_message_deleted: 'The message has been deleted.',
+			msg_message_sent: 'Your message has been sent. You may edit the message if you wish; one copy for each recipient will be in your outbox until each recipient has read it. Return to your <a href="%inbox_link%">inbox</a>.',
+			msg_draft_saved: 'Your message has been saved to your Drafts folder.',
+			msg_no_messages: 'No messages in this folder.',
+			
+			lbl_message_from: 'Private message from %sender%',
+			lbl_subject: 'Subject:',
+			lbl_date: 'Date:',
+			lbl_message: 'Message:',
+			lbl_compose_th: 'Compose new private message',
+			lbl_compose_to: 'To:',
+			lbl_compose_to_max: 'Separate multiple names with a single comma; you may send this message to up to <b>%limit%</b> users.',
+			lbl_edit_th: 'Edit draft',
+			
+			btn_send_reply: 'Send reply',
+			btn_archive: 'Archive message',
+			btn_return_to_inbox: 'Return to inbox',
+			btn_send: 'Send message',
+			btn_savedraft: 'Save as draft',
+			btn_archive_selected: 'Archive selected',
+			btn_delete_selected: 'Delete selected',
+			btn_delete_all: 'Delete all',
+			btn_compose: 'New message',
+			
+			sidebar_th_privmsgs: 'Private messages',
+			folder_inbox: 'Inbox',
+			folder_outbox: 'Outbox',
+			folder_sent: 'Sent items',
+			folder_drafts: 'Drafts',
+			folder_archive: 'Archive',
+			sidebar_th_buddies: 'Buddies',
+			sidebar_friend_list: 'Friend list',
+			sidebar_foe_list: 'Foe list',
+			
+			folder_th_foldername: 'Folder:',
+			folder_th_to: 'To',
+			folder_th_from: 'From',
+			folder_th_subject: 'Subject',
+			folder_th_date: 'Date',
+			folder_th_mark: 'Mark',
+			
+			// Buddy / foe lists
+			th_buddy_list: 'Buddy list for %username%',
+			msg_no_buddies: 'No buddies in your list.',
+			btn_pm_all_buddies: 'Send a PM to all buddies',
+			heading_add_buddy: 'Add a new friend',
+			btn_add: 'Add',
+			lbl_username: 'Username:',
+			btn_buddy_remove: 'Remove',
+			btn_buddy_send_pm: 'Send private message',
+			
+			th_foe_list: 'Foe list for %username%',
+			msg_no_foes: 'No foes in your list.',
+			heading_add_foe: 'Add a new foe',
+			
+			// AJAX interface (up and coming)
+			ajax_err_need_js: 'It looks like your browser doesn\'t have support for Javascript. You\'ll need to have Javascript support in order to use the new Private Message interface. You can also switch to the <a href="%basic_link%">old interface</a>, which doesn\'t require Javascript support.',
+			ajax_err_json: 'The server had a problem processing your request.',
+			
+			ajax_btn_compose: 'Compose message',
+			ajax_btn_archive: 'Archive',
+			ajax_btn_unarchive: 'Restore to inbox',
+			ajax_btn_mark_read: 'Mark as read',
+			ajax_btn_mark_unread: 'Mark as unread',
+			ajax_btn_delete: 'Move to trash',
+			ajax_btn_delete_forever: 'Delete forever',
+			ajax_btn_refresh: 'Refresh',
+			ajax_btn_reply: 'Reply',
+			
+			ajax_folder_inbox: 'Inbox',
+			ajax_folder_starred: 'Starred',
+			ajax_folder_sent: 'Sent messages',
+			ajax_folder_drafts: 'Drafts',
+			ajax_folder_archive: 'Archive',
+			ajax_folder_trash: 'Trash',
+			
+			ajax_msg_loading: 'Loading...',
+			
+			ajax_lbl_sender: '%sender% to %recipient%',
+			ajax_me: 'me',
+			
+			ajax_teaser_inbox: 'No new mail.',
+			ajax_teaser_starred: 'You haven\'t starred any messages yet. Starring a message lets you give it a special status that separates it from the rest of your mail so it\'s easier to find. You can star a message by clicking the small gray star next to a message in your inbox or archive.',
+			ajax_teaser_sent: 'You haven\'t sent any messages yet. When you send a message, a copy of it will appear here.',
+			ajax_teaser_drafts: 'You don\'t have any drafts. When you save a message so you can finish writing it later, it will appear here.',
+			ajax_teaser_archive: 'You haven\'t archived any messages yet. Archive a message when you want to remove it from your inbox but keep a copy of it around.',
+			ajax_teaser_trash: 'No deleted messages. Delete a message by checking it in another folder and clicking Delete. Deleting a message from this folder will remove it permanently.',
+			
+			ajax_no_subject: '[No subject]',
+			
+			ajax_compose_lbl_to: 'To:',
+			ajax_compose_lbl_subject: 'Subject:',
+			ajax_compose_btn_send: 'Send message',
+			ajax_compose_btn_save: 'Save draft',
+			ajax_compose_btn_cancel: 'Discard',
+			ajax_compose_btn_cancel_confirm: 'Discard message',
+			
+			ajax_compose_msg_confirm_discard_title: 'Discard message?',
+			ajax_compose_msg_confirm_discard_body: 'Save this message as a draft if you want to avoid losing it. Discarding this message will not delete any existing drafts.',
+		},
+		userfuncs: {
+			
+			// Special:Contributions
+			contribs_err_no_user: 'You need to select a user to view contributions for.',
+			contribs_heading_edits: 'Page edits',
+			contribs_msg_no_edits: 'This user has not made any edits.',
+			contribs_heading_other: 'Other changes made by this user',
+			contribs_msg_no_other: 'This user has not made any non-editing changes.',
+			
+			// Special:ChangeStyle
+			changetheme_heading_theme: 'Please select a new theme:',
+			changetheme_heading_style: 'Please select a stylesheet:',
+			changetheme_btn_continue: 'Continue',
+			changetheme_btn_allclear: 'Change style',
+			changetheme_success_title: 'Theme changed',
+			changetheme_success_body: 'Your theme preferences have been updated. Redirecting you to the last viewed page...',
+			
+			// Special:ActivateAccount
+			activate_err_badlink_title: 'Account activation error',
+			activate_err_badlink_body: 'This page can only be accessed using links sent to users via e-mail.',
+			activate_err_bad_key: 'The activation key was probably incorrect, or the account is already active.',
+			activate_success_title: 'Activation successful',
+			activate_success_body: 'Your account is now active. Thank you for registering.',
+			
+			// Special:PasswordReset
+			passreset_blurb_line1: 'Don\'t worry, it happens to the best of us.',
+			passreset_blurb_line2: 'To reset your password, just enter your username below, and a new password will be e-mailed to you.',
+			passreset_lbl_username: 'Username:',
+			passreset_btn_mailpasswd: 'Mail new password',
+			passreset_email: "Dear %username%,
+		
 Someone (hopefully you) on the %site_name% website requested that a new password be created.
 
 The request was sent from the IP address %remote_addr%.
@@ -597,83 +597,83 @@
 
 If you did request this password, then please log in using the password shown below:
 
-  Password: %temp_pass%
-  
+	Password: %temp_pass%
+	
 After you log in using this password, you will be able to reset your real password. You can only log in using this temporary password once.
 
 Sincerely yours,
 The %site_name% administration team
 ",
-      passreset_stage1_success: 'An e-mail has been sent to the e-mail address on file for your username with a new password in it. Please check your e-mail for further instructions.',
-      passreset_stage1_error: 'Error occured, your new password was not sent.',
-      passreset_stage2_th: 'Reset password',
-      passreset_stage2_lbl_password: 'Password:',
-      passreset_stage2_lbl_confirm: 'Confirm:',
-      passreset_stage2_lbl_strength: 'Password strength rating:',
-      passreset_stage2_blurb_strength: 'Your password needs to have a score of at least <b>%config.pw_strength_minimum%</b>.',
-      passreset_stage2_btn_submit: 'Reset password',
-      passreset_stage2_success: 'Your password has been reset. Return to the <a href="%url_mainpage%">main page</a>.',
-      
-      passreset_err_no_match: 'The passwords you entered do not match.',
-      passreset_err_too_short: 'The new password must be 6 characters or greater in length.',
-      passreset_err_failed_score: 'ERROR: Your password did not pass the complexity score requirement. You need %config.pw_strength_minimum% points to pass; your password received a score of %inp_score%. <a href="%url%">Go back</a>',
-      passreset_err_pass_expired: 'Your temporary password has expired. Please <a href="%reset_url%">request another one</a>.',
-      
-      // Special:Memberlist
-      ml_column_username: 'Username',
-      ml_column_userlevel: 'Title',
-      ml_column_email: 'E-mail',
-      ml_column_regtime: 'Registered',
-      ml_level_guest: 'Guest',
-      ml_level_member: 'Member',
-      ml_level_mod: 'Moderator',
-      ml_level_admin: 'Site administrator',
-      ml_level_unknown: 'Unknown (level %level%)',
-      ml_email_nonpublic: 'Non-public',
-      ml_date_daysago: '%days_ago% days ago',
-      ml_date_today: 'Today',
-      ml_date_yesterday: 'Yesterday',
-      ml_btn_adminuser: 'Administer user',
-      ml_tip_userpage: 'Click to view this user\'s userpage',
-      ml_tip_nouserpage: 'This user hasn\'t created a userpage yet, but you can still view profile details by clicking this link.',
-      ml_lbl_finduser: 'Find a member:',
-      ml_btn_go: 'Go',
-      ml_tip_wildcard: 'You may use the following wildcards: * to match multiple characters, ? to match a single character.',
-      ml_err_nousers_find: 'Sorry - no users that matched your query could be found. Please try some different search terms.',
-      ml_err_nousers: 'Sorry - no users with usernames that start with that letter could be found.',
-      ml_msg_matches_zero: 'Search returned no matches',
-      ml_msg_matches_one: 'Search returned 1 match',
-      ml_msg_matches: 'Search returned %matches% matches',
-    },
-    userpage: {
-      page_title: '%username|htmlsafe%\'s user page',
-      heading_basics: 'All about %username%',
-      lbl_joined: 'Member since:',
-      lbl_num_comments: 'Total comments:',
-      lbl_real_name: 'Real name:',
-      btn_administer_user: 'Administer user',
-      heading_comments: '%username%\'s latest comments',
-      comments_lbl_posted: 'Posted',
-      msg_no_comments: 'This user has not posted any comments.',
-      heading_contact: 'Get in touch',
-      lbl_email: 'E-mail address:',
-      lbl_homepage: 'Website:',
-      btn_send_pm: 'Send %username% a <a href="%pm_link%">Private Message!</a>',
-      btn_send_pm_guest: 'You could send %username% a Private Message if you were <a %login_flags%>logged in</a>.',
-      lbl_aim: 'AIM:',
-      lbl_yim: 'Yahoo! IM:',
-      lbl_wlm: 'WLM:',
-      lbl_xmpp: 'XMPP/Jabber&trade;:',
-      heading_real_life: '%username% in real life',
-      lbl_location: 'Location:',
-      lbl_job: 'Job/occupation:',
-      lbl_hobbies: 'Enjoys:',
-      msg_no_contact_info: '%username% hasn\'t posted any real-life contact information.',
-      msg_user_not_exist: 'Additional information: user "%username%" does not exist.',
-      tab_profile: 'Profile',
-      tab_content: 'User page',
-    }
-  }
+			passreset_stage1_success: 'An e-mail has been sent to the e-mail address on file for your username with a new password in it. Please check your e-mail for further instructions.',
+			passreset_stage1_error: 'Error occured, your new password was not sent.',
+			passreset_stage2_th: 'Reset password',
+			passreset_stage2_lbl_password: 'Password:',
+			passreset_stage2_lbl_confirm: 'Confirm:',
+			passreset_stage2_lbl_strength: 'Password strength rating:',
+			passreset_stage2_blurb_strength: 'Your password needs to have a score of at least <b>%config.pw_strength_minimum%</b>.',
+			passreset_stage2_btn_submit: 'Reset password',
+			passreset_stage2_success: 'Your password has been reset. Return to the <a href="%url_mainpage%">main page</a>.',
+			
+			passreset_err_no_match: 'The passwords you entered do not match.',
+			passreset_err_too_short: 'The new password must be 6 characters or greater in length.',
+			passreset_err_failed_score: 'ERROR: Your password did not pass the complexity score requirement. You need %config.pw_strength_minimum% points to pass; your password received a score of %inp_score%. <a href="%url%">Go back</a>',
+			passreset_err_pass_expired: 'Your temporary password has expired. Please <a href="%reset_url%">request another one</a>.',
+			
+			// Special:Memberlist
+			ml_column_username: 'Username',
+			ml_column_userlevel: 'Title',
+			ml_column_email: 'E-mail',
+			ml_column_regtime: 'Registered',
+			ml_level_guest: 'Guest',
+			ml_level_member: 'Member',
+			ml_level_mod: 'Moderator',
+			ml_level_admin: 'Site administrator',
+			ml_level_unknown: 'Unknown (level %level%)',
+			ml_email_nonpublic: 'Non-public',
+			ml_date_daysago: '%days_ago% days ago',
+			ml_date_today: 'Today',
+			ml_date_yesterday: 'Yesterday',
+			ml_btn_adminuser: 'Administer user',
+			ml_tip_userpage: 'Click to view this user\'s userpage',
+			ml_tip_nouserpage: 'This user hasn\'t created a userpage yet, but you can still view profile details by clicking this link.',
+			ml_lbl_finduser: 'Find a member:',
+			ml_btn_go: 'Go',
+			ml_tip_wildcard: 'You may use the following wildcards: * to match multiple characters, ? to match a single character.',
+			ml_err_nousers_find: 'Sorry - no users that matched your query could be found. Please try some different search terms.',
+			ml_err_nousers: 'Sorry - no users with usernames that start with that letter could be found.',
+			ml_msg_matches_zero: 'Search returned no matches',
+			ml_msg_matches_one: 'Search returned 1 match',
+			ml_msg_matches: 'Search returned %matches% matches',
+		},
+		userpage: {
+			page_title: '%username|htmlsafe%\'s user page',
+			heading_basics: 'All about %username%',
+			lbl_joined: 'Member since:',
+			lbl_num_comments: 'Total comments:',
+			lbl_real_name: 'Real name:',
+			btn_administer_user: 'Administer user',
+			heading_comments: '%username%\'s latest comments',
+			comments_lbl_posted: 'Posted',
+			msg_no_comments: 'This user has not posted any comments.',
+			heading_contact: 'Get in touch',
+			lbl_email: 'E-mail address:',
+			lbl_homepage: 'Website:',
+			btn_send_pm: 'Send %username% a <a href="%pm_link%">Private Message!</a>',
+			btn_send_pm_guest: 'You could send %username% a Private Message if you were <a %login_flags%>logged in</a>.',
+			lbl_aim: 'AIM:',
+			lbl_yim: 'Yahoo! IM:',
+			lbl_wlm: 'WLM:',
+			lbl_xmpp: 'XMPP/Jabber&trade;:',
+			heading_real_life: '%username% in real life',
+			lbl_location: 'Location:',
+			lbl_job: 'Job/occupation:',
+			lbl_hobbies: 'Enjoys:',
+			msg_no_contact_info: '%username% hasn\'t posted any real-life contact information.',
+			msg_user_not_exist: 'Additional information: user "%username%" does not exist.',
+			tab_profile: 'Profile',
+			tab_content: 'User page',
+		}
+	}
 };
 
 // All done! :-)
--- a/plugins/PrivateMessages.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/PrivateMessages.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_privatemessages_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_privatemessages_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_privatemessages_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_privatemessages_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -25,831 +25,831 @@
 
 function PrivateMessages_paths_init()
 {
-  register_special_page('PrivateMessages', 'specialpage_private_messages');
+	register_special_page('PrivateMessages', 'specialpage_private_messages');
 }
 
 function page_Special_PrivateMessages()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( !$session->user_logged_in )
-  {
-    die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('privmsgs_err_need_login', array('login_link' => makeUrlNS('Special', 'Login/' . $paths->page))) . '</p>');
-  }
-  $argv = Array();
-  $argv[] = $paths->getParam(0);
-  $argv[] = $paths->getParam(1);
-  $argv[] = $paths->getParam(2);
-  if ( !$argv[0] )
-  {
-    $argv[0] = 'InVaLiD';
-  }
-  switch($argv[0])
-  {
-    default:
-      header('Location: '.makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'));
-      break;
-    case 'View':
-      $id = $argv[1];
-      if ( !ctype_digit($id) )
-      {
-        die_friendly('Message error', '<p>Invalid message ID</p>');
-      }
-      $q = $db->sql_query('SELECT p.message_from, p.message_to, p.subject, p.message_text, p.date, p.folder_name, u.signature FROM '.table_prefix.'privmsgs AS p LEFT JOIN '.table_prefix.'users AS u ON (p.message_from=u.username) WHERE message_id='.$id.'');
-      if ( !$q )
-      {
-        $db->_die('The message data could not be selected.');
-      }
-      $r = $db->fetchrow();
-      $db->free_result();
-      if ( ($r['message_to'] != $session->username && $r['message_from'] != $session->username ) || $r['folder_name']=='drafts' )
-      {
-        die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('privmsgs_err_not_authorized_read') . '</p>');
-      }
-      if ( $r['message_to'] == $session->username )
-      {
-        $q = $db->sql_query('UPDATE '.table_prefix.'privmsgs SET message_read=1 WHERE message_id='.$id.'');
-        $db->free_result();
-        if ( !$q )
-        {
-          $db->_die('Could not mark message as read');
-        }
-      }
-      $template->header();
-      userprefs_show_menu();
-      ?>
-        <br />
-        <div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4">
-          <tr><th colspan="2"><?php echo $lang->get('privmsgs_lbl_message_from', array('sender' => htmlspecialchars($r['message_from']))); ?></th></tr>
-          <tr><td class="row1"><?php echo $lang->get('privmsgs_lbl_subject') ?></td><td class="row1"><?php echo $r['subject']; ?></td></tr>
-          <tr><td class="row2"><?php echo $lang->get('privmsgs_lbl_date') ?></td><td class="row2"><?php echo enano_date(ED_DATE | ED_TIME, $r['date']); ?></td></tr>
-          <tr><td class="row1"><?php echo $lang->get('privmsgs_lbl_message') ?></td><td class="row1"><?php echo RenderMan::render($r['message_text']);
-          if ( $r['signature'] != '' )
-          {
-            echo '<hr style="margin-left: 1em; width: 200px;" />';
-            echo RenderMan::render($r['signature']);
-          }
-          ?></td></tr>
-          <tr><td colspan="2" class="row3"><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Compose/ReplyTo/'.$id); ?>"><?php echo $lang->get('privmsgs_btn_send_reply'); ?></a>  |  <a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Delete/'.$id); ?>">Delete message</a>  |  <?php if($r['folder_name'] != 'archive') { ?><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Move/'.$id.'/Archive'); ?>"><?php echo $lang->get('privmsgs_btn_archive'); ?></a>  |  <?php } ?><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox') ?>"><?php echo $lang->get('privmsgs_btn_return_to_inbox'); ?></a></td></tr>
-        </table></div>
-      <?php
-      $template->footer();              
-      break;
-    case 'Move':
-      $id = $argv[1];
-      if ( !ctype_digit($id) )
-      {
-        die_friendly('Message error', '<p>Invalid message ID</p>');
-      }
-      $q = $db->sql_query('SELECT message_to FROM '.table_prefix.'privmsgs WHERE message_id='.$id.'');
-      if ( !$q )
-      {
-        $db->_die('The message data could not be selected.');
-      }
-      $r = $db->fetchrow();
-      $db->free_result();
-      if ( $r['message_to'] != $session->username )
-      {
-        die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('privmsgs_err_not_authorized_edit') . '</p>');
-      }
-      $fname = $argv[2];
-      if ( !$fname || ( $fname != 'Inbox' && $fname != 'Outbox' && $fname != 'Sent' && $fname != 'Drafts' && $fname != 'Archive' ) )
-      {
-        die_friendly('Invalid request', '<p>The folder name "'.$fname.'" is invalid.</p>');
-      }
-      $q = $db->sql_query('UPDATE '.table_prefix.'privmsgs SET folder_name=\''.strtolower($fname).'\' WHERE message_id='.$id.';');
-      $db->free_result();
-      if ( !$q )
-      {
-        $db->_die('The message was not successfully moved.');
-      }
-      die_friendly($lang->get('privmsgs_msg_message_status'), '<p>' . $lang->get('privmsgs_msg_message_moved', array('folder' => $fname)) . '</p><p><a href="'.makeUrlNS('Special', 'PrivateMessages/Folder/Inbox').'">' . $lang->get('privmsgs_btn_return_to_inbox') . '</a></p>');
-      break;
-    case 'Delete':
-      $id = $argv[1];
-      if ( !ctype_digit($id) )
-      {
-        die_friendly('Message error', '<p>Invalid message ID</p>');
-      }
-      $q = $db->sql_query('SELECT message_to FROM '.table_prefix.'privmsgs WHERE message_id='.$id.'');
-      if ( !$q )
-      {
-        $db->_die('The message data could not be selected.');
-      }
-      $r = $db->fetchrow();
-      if ( $r['message_to'] != $session->username )
-      {
-        die_friendly($lang->get('etc_access_denied_short'), '<p>You are not authorized to delete this message.</p>');
-      }
-      $q = $db->sql_query('DELETE FROM '.table_prefix.'privmsgs WHERE message_id='.$id.';');
-      if ( !$q )
-      {
-        $db->_die('The message was not successfully deleted.');
-      }
-      $db->free_result();
-      die_friendly($lang->get('privmsgs_msg_message_status'), '<p>' . $lang->get('privmsgs_msg_message_deleted') . '</p><p><a href="'.makeUrlNS('Special', 'PrivateMessages/Folder/Inbox').'">' . $lang->get('privmsgs_btn_return_to_inbox') . '</a></p>');
-      break;
-    case 'Compose':
-      if ( $argv[1]=='Send' && isset($_POST['_send']) )
-      {
-        // Check each POST DATA parameter...
-        $errors = array();
-        if(!isset($_POST['to']) || ( isset($_POST['to']) && $_POST['to'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_username');
-        }
-        if(!isset($_POST['subject']) || ( isset($_POST['subject']) && $_POST['subject'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_subject');
-        }
-        if(!isset($_POST['message']) || ( isset($_POST['message']) && $_POST['message'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_message');
-        }
-        if ( count($errors) < 1 )
-        {
-          $namelist = $_POST['to'];
-          $namelist = str_replace(', ', ',', $namelist);
-          $namelist = explode(',', $namelist);
-          foreach($namelist as $n) { $n = $db->escape($n); }
-          $subject = RenderMan::preprocess_text($_POST['subject']);
-          $message = RenderMan::preprocess_text($_POST['message']);
-          $base_query = 'INSERT INTO '.table_prefix.'privmsgs(message_from,message_to,date,subject,message_text,folder_name,message_read) VALUES';
-          foreach($namelist as $n)
-          {
-            $base_query .= '(\''.$session->username.'\', \''.$n.'\', '.time().', \''.$subject.'\', \''.$message.'\', \'inbox\', 0),';
-          }
-          $base_query = substr($base_query, 0, strlen($base_query)-1) . ';';
-          $result = $db->sql_query($base_query);
-          $db->free_result();
-          if ( !$result )
-          {
-            $db->_die('The message could not be sent.');
-          }
-          else
-          {
-            die_friendly($lang->get('privmsgs_msg_message_status'), '<p>' . $lang->get('privmsgs_msg_message_sent', array('inbox_link' => makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'))) . '</p>');
-          }
-          return;
-        }
-      }
-      else if ( $argv[1] == 'Send' && isset($_POST['_savedraft'] ) )
-      {
-        $errors = array();
-        if ( !isset($_POST['to']) || ( isset($_POST['to']) && $_POST['to'] == '') )
-        {
-          $errors[] = $lang->get('privmsgs_err_need_username');
-        }
-        if ( !isset($_POST['subject']) || ( isset($_POST['subject']) && $_POST['subject'] == '') )
-        {
-          $errors[] = $lang->get('privmsgs_err_need_subject');
-        }
-        if ( !isset($_POST['message']) || ( isset($_POST['message']) && $_POST['message'] == '') )
-        {
-          $errors[] = $lang->get('privmsgs_err_need_message');
-        }
-        if ( count($errors) < 1 )
-        {
-          $namelist = $_POST['to'];
-          $namelist = str_replace(', ', ',', $namelist);
-          $namelist = explode(',', $namelist);
-          foreach($namelist as $n)
-          {
-            $n = $db->escape($n);
-          }
-          if ( count($namelist) > MAX_PMS_PER_BATCH && !$session->get_permssions('mod_misc') )
-          {
-            die_friendly($lang->get('privmsgs_err_limit_exceeded_title'), '<p>' . $lang->get('privmsgs_err_limit_exceeded_body', array('limit' => MAX_PMS_PER_BATCH)) . '</p>');
-          }
-          $subject = $db->escape($_POST['subject']);
-          $message = RenderMan::preprocess_text($_POST['message']);
-          $base_query = 'INSERT INTO '.table_prefix.'privmsgs(message_from,message_to,date,subject,message_text,folder_name,message_read) VALUES';
-          foreach($namelist as $n)
-          {
-            $base_query .= '(\''.$session->username.'\', \''.$n.'\', '.time().', \''.$subject.'\', \''.$message.'\', \'drafts\', 0),';
-          }
-          $base_query = substr($base_query, 0, strlen($base_query) - 1) . ';';
-          $result = $db->sql_query($base_query);
-          $db->free_result();
-          if ( !$result )
-          {
-            $db->_die('The message could not be saved.');
-          }
-        }
-      }
-      else if(isset($_POST['_inbox']))
-      {
-        redirect(makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'), '', '', 0);
-      }
-      if($argv[1] == 'ReplyTo' && preg_match('#^([0-9]+)$#', $argv[2]))
-      {
-        $to = '';
-        $text = '';
-        $subj = '';
-        $id = $argv[2];
-        $q = $db->sql_query('SELECT p.message_from, p.message_to, p.subject, p.message_text, p.date, p.folder_name, u.signature FROM '.table_prefix.'privmsgs AS p LEFT JOIN '.table_prefix.'users AS u ON (p.message_from=u.username) WHERE message_id='.$id.';');
-        if ( !$q )
-          $db->_die('The message data could not be selected.');
-        
-        $r = $db->fetchrow();
-        $db->free_result();
-        if ( ($r['message_to'] != $session->username && $r['message_from'] != $session->username ) || $r['folder_name'] == 'drafts' )
-        {
-          die_friendly($lang->get('etc_access_denied_short'), '<p>You are not authorized to view the contents of this message.</p>');
-        }
-        $subj = 'Re: ' . $r['subject'];
-        $text = "\n\n\nOn " . enano_date(ED_DATE | ED_TIME, $r['date']) . ", " . $r['message_from'] . " wrote:\n> " . str_replace("\n", "\n> ", $r['message_text']); // Way less complicated than using a regex ;-)
-        
-        $tbuf = $text;
-        while( preg_match("/\n([\> ]*?)\> \>/", $text) )
-        {
-          $text = preg_replace("/\n([\> ]*?)\> \>/", '\\1>>', $text);
-          if ( $text == $tbuf )
-            break;
-          $tbuf = $text;
-        }
-        
-        $to = $r['message_from'];
-      }
-      else
-      {
-        if ( ( $argv[1]=='to' || $argv[1]=='To' ) && $argv[2] )
-        {
-          $to = htmlspecialchars($argv[2]);
-        }
-        else
-        {
-          $to = '';
-        }
-        $text = '';
-        $subj = '';
-      }
-        $template->header();
-        userprefs_show_menu();
-        if ( isset($errors) && count($errors) > 0 )
-        {
-          echo '<div class="warning-box">
-                  ' . $lang->get('privmsgs_err_send_submit') . '
-                  <ul>
-                    <li>' . implode('</li><li>', $errors) . '</li>
-                  </ul>
-                </div>';
-        }
-        echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/Compose/Send').'" method="post">';
-        
-        if ( isset($_POST['_savedraft']) )
-        {
-          echo '<div class="info-box">' . $lang->get('privmsgs_msg_draft_saved') . '</div>';
-        }
-        ?>
-        <br />
-        <div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4">
-          <tr>
-            <th colspan="2"><?php echo $lang->get('privmsgs_lbl_compose_th'); ?></th>
-          </tr>
-          <tr>
-            <td class="row1">
-              <?php echo $lang->get('privmsgs_lbl_compose_to'); ?><br />
-              <small><?php echo $lang->get('privmsgs_lbl_compose_to_max', array('limit' => MAX_PMS_PER_BATCH)); ?></small>
-            </td>
-            <td class="row1">
-              <?php echo $template->username_field('to', (isset($_POST['_savedraft'])) ? $_POST['to'] : $to ); ?>
-            </td>
-          </tr>
-          <tr>
-            <td class="row2">
-              <?php echo $lang->get('privmsgs_lbl_subject'); ?>
-            </td>
-            <td class="row2">
-              <input name="subject" type="text" size="30" value="<?php if(isset($_POST['_savedraft'])) echo htmlspecialchars($_POST['subject']); else echo $subj; ?>" />
-            </td>
-          </tr>
-          <tr>
-            <td class="row1">
-              <?php echo $lang->get('privmsgs_lbl_message'); ?>
-            </td>
-            <td class="row1" style="min-width: 80%;">
-              <?php
-                if ( isset($_POST['_savedraft']) )
-                {
-                  $content = htmlspecialchars($_POST['message']);
-                }
-                else
-                {
-                  $content =& $text;
-                }
-                echo $template->tinymce_textarea('message', $content, 20, 40);
-              ?>
-            </td>
-          </tr>
-          <tr>
-            <th class="subhead" colspan="2">
-              <input type="submit" name="_send" value="<?php echo $lang->get('privmsgs_btn_send'); ?>" />
-              <input type="submit" name="_savedraft" value="<?php echo $lang->get('privmsgs_btn_savedraft'); ?>" />
-              <input type="submit" name="_inbox" value="<?php echo $lang->get('privmsgs_btn_return_to_inbox'); ?>" />
-            </th>
-          </tr>
-        </table></div>
-        <?php
-        echo '</form>';
-        $template->footer();
-      break;
-    case 'Edit':
-      $id = $argv[1];
-      if ( !ctype_digit($id) )
-      {
-        die_friendly('Message error', '<p>Invalid message ID</p>');
-      }
-      $q = $db->sql_query('SELECT message_from, message_to, subject, message_text, date, folder_name, message_read FROM '.table_prefix.'privmsgs WHERE message_id='.$id.'');
-      if ( !$q )
-      {
-        $db->_die('The message data could not be selected.');
-      }
-      $r = $db->fetchrow();
-      $db->free_result();
-      if ( $r['message_from'] != $session->username || $r['message_read'] == 1 )
-      {
-        die_friendly($lang->get('etc_access_denied_short'), '<p>You are not authorized to edit this message.</p>');
-      }
-      $fname = $argv[2];
-      
-      if(isset($_POST['_send']))
-      {
-        // Check each POST DATA parameter...
-        $errors = array();
-        if(!isset($_POST['to']) || ( isset($_POST['to']) && $_POST['to'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_username');
-        }
-        if(!isset($_POST['subject']) || ( isset($_POST['subject']) && $_POST['subject'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_subject');
-        }
-        if(!isset($_POST['message']) || ( isset($_POST['message']) && $_POST['message'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_message');
-        }
-        if ( count($errors) < 1 )
-        {
-          $namelist = $_POST['to'];
-          $namelist = str_replace(', ', ',', $namelist);
-          $namelist = explode(',', $namelist);
-          foreach ($namelist as $n)
-          {
-            $n = $db->escape($n);
-          }
-          $subject = RenderMan::preprocess_text($_POST['subject']);
-          $message = RenderMan::preprocess_text($_POST['message']);
-          $base_query = 'UPDATE '.table_prefix.'privmsgs SET subject=\''.$subject.'\',message_to=\''.$namelist[0].'\',message_text=\''.$message.'\',folder_name=\'inbox\' WHERE message_id='.$id.';';
-          $result = $db->sql_query($base_query);
-          $db->free_result();
-          if ( !$result )
-          {
-            $db->_die('The message could not be sent.');
-          }
-          else
-          {
-            die_friendly($lang->get('privmsgs_msg_message_status'), '<p>' . $lang->get('privmsgs_msg_message_sent', array('inbox_link' => makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'))) . '</p>');
-          }
-          return;
-        }
-      }
-      else if ( isset($_POST['_savedraft']) )
-      {
-        // Check each POST DATA parameter...
-        $errors = array();
-        if(!isset($_POST['to']) || ( isset($_POST['to']) && $_POST['to'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_username');
-        }
-        if(!isset($_POST['subject']) || ( isset($_POST['subject']) && $_POST['subject'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_subject');
-        }
-        if(!isset($_POST['message']) || ( isset($_POST['message']) && $_POST['message'] == ''))
-        {
-          $errors[] = $lang->get('privmsgs_err_need_message');
-        }
-        if ( count($errors) < 1 )
-        {
-          $namelist = $_POST['to'];
-          $namelist = str_replace(', ', ',', $namelist);
-          $namelist = explode(',', $namelist);
-          foreach ( $namelist as $n )
-          {
-            $n = $db->escape($n);
-          }
-          $subject = $db->escape($_POST['subject']);
-          $message = RenderMan::preprocess_text($_POST['message']);
-          $base_query = 'UPDATE '.table_prefix.'privmsgs SET subject=\''.$subject.'\',message_to=\''.$namelist[0].'\',message_text=\''.$message.'\' WHERE message_id='.$id.';';
-          $result = $db->sql_query($base_query);
-          $db->free_result();
-          if ( !$result )
-          {
-            $db->_die('The message could not be saved.');
-          }
-        }
-      }
-        if ( $argv[1]=='to' && $argv[2] )
-        {
-          $to = htmlspecialchars($argv[2]);
-        }
-        else
-        {
-          $to = '';
-        }
-        $template->header();
-        userprefs_show_menu();
-        echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/Edit/'.$id).'" method="post">';
-        
-        if ( isset($_POST['_savedraft']) )
-        {
-          echo '<div class="info-box">' . $lang->get('privmsgs_msg_draft_saved') . '</div>';
-        }
-        ?>
-        <br />
-        <div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4">
-          <tr><th colspan="2"><?php echo $lang->get('privmsgs_lbl_edit_th'); ?></th></tr>
-          <tr>
-            <td class="row1">
-              <?php echo $lang->get('privmsgs_lbl_compose_to'); ?><br />
-              <small><?php echo $lang->get('privmsgs_lbl_compose_to_max', array('limit' => MAX_PMS_PER_BATCH)); ?></small>
-            </td>
-            <td class="row1">
-              <?php echo $template->username_field('to', (isset($_POST['_savedraft'])) ? $_POST['to'] : $r['message_to'] ); ?>
-            </td>
-          </tr>
-          <tr>
-            <td class="row2">
-              <?php echo $lang->get('privmsgs_lbl_subject'); ?>
-            </td>
-            <td class="row2">
-              <input name="subject" type="text" size="30" value="<?php if(isset($_POST['_savedraft'])) echo htmlspecialchars($_POST['subject']); else echo $r['subject']; ?>" />
-            </td>
-          </tr>
-          <tr>
-            <td class="row1">
-              <?php echo $lang->get('privmsgs_lbl_message'); ?>
-            </td>
-            <td class="row1" style="min-width: 80%;">
-              <?php
-                if ( isset($_POST['_savedraft']) )
-                {
-                  $content = htmlspecialchars($_POST['message']);
-                }
-                else
-                {
-                  $content =& $r['message_text'];
-                }
-                echo $template->tinymce_textarea('message', $content, 20, 40);
-              ?>
-            </td>
-          </tr>
-          
-          <tr>
-            <th class="subhead" colspan="2">
-              <input type="submit" name="_send" value="<?php echo $lang->get('privmsgs_btn_send'); ?>" />
-              <input type="submit" name="_savedraft" value="<?php echo $lang->get('privmsgs_btn_savedraft'); ?>" />
-            </th>
-          </tr>
-        </table></div>
-        <?php
-        echo '</form>';
-        $template->footer();
-      break;
-    case 'Folder':
-      $template->header();
-      userprefs_show_menu();
-      switch($argv[1])
-      {
-        default:
-          echo '<p>' . $lang->get('privmsgs_err_folder_not_exist', array(
-              'folder_name' => htmlspecialchars($argv[1]),
-              'inbox_url' => makeUrlNS('Special', 'PrivateMessages/Folder/Inbox')
-            )) . '</p>';
-          break;
-        case 'Inbox':
-        case 'Outbox':
-        case 'Sent':
-        case 'Drafts':
-        case 'Archive':
-          ?>
-          <table border="0" width="100%" cellspacing="10" cellpadding="0">
-          <tr>
-          <td style="padding: 0px; width: 120px;" valign="top"  >
-          <div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
-          <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_privmsgs'); ?></small></th></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'); ?>"><?php echo $lang->get('privmsgs_folder_inbox'); ?></a></small></td></tr>
-          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'); ?>"><?php echo $lang->get('privmsgs_folder_outbox'); ?></a></small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Sent'); ?>"><?php echo $lang->get('privmsgs_folder_sent'); ?></a></small></td></tr>
-          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'); ?>"><?php echo $lang->get('privmsgs_folder_drafts'); ?></a></small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Archive'); ?>"><?php echo $lang->get('privmsgs_folder_archive'); ?></a></small></td></tr>
-          <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_buddies'); ?></small></th></tr>
-          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>"><?php echo $lang->get('privmsgs_sidebar_friend_list'); ?></a></small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>"><?php echo $lang->get('privmsgs_sidebar_foe_list'); ?></a></small></td></tr>
-          </table></div>
-          </td>
-          <td valign="top">
-          <?php
-          $fname = strtolower($argv[1]);
-          switch($argv[1])
-          {
-            case 'Inbox':
-            case 'Archive':
-            default:
-              $q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject, p.message_read FROM '.table_prefix.'privmsgs AS p WHERE p.folder_name=\''.$fname.'\' AND p.message_to=\''.$session->username.'\' ORDER BY date DESC;');
-              break;
-            case 'Outbox':
-              $q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject, p.message_read FROM '.table_prefix.'privmsgs AS p WHERE p.message_from=\''.$session->username.'\' AND message_read=0 ORDER BY date DESC;');
-              break;
-            case 'Sent':
-              $q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject, p.message_read FROM '.table_prefix.'privmsgs AS p WHERE p.message_from=\''.$session->username.'\' AND message_read=1 ORDER BY date DESC;');
-              break;
-            case 'Drafts':
-              $q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject, p.message_read FROM '.table_prefix.'privmsgs AS p WHERE p.folder_name=\''.$fname.'\' AND p.message_from=\''.$session->username.'\' ORDER BY date DESC;');
-              break;
-          }
-          if ( !$q )
-          {
-            $db->_die('The private message data could not be selected.');
-          }
-          if ( $argv[1] == 'Drafts' || $argv[1] == 'Outbox' )
-          {
-            $act = 'Edit';
-          }
-          else
-          {
-            $act = 'View';
-          }
-          echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/PostHandler').'" method="post">
-                  <div class="tblholder">
-                    <table border="0" width="100%" cellspacing="1" cellpadding="4">
-                      <tr>
-                        <th colspan="4" style="text-align: left;">' . $lang->get('privmsgs_folder_th_foldername') . ' ' . $lang->get('privmsgs_folder_' . strtolower($argv[1])) . '</th>
-                      </tr>
-                    <tr>
-                      <th class="subhead">';
-          if ( $fname == 'drafts' || $fname == 'Outbox' )
-          {
-            echo $lang->get('privmsgs_folder_th_to');
-          }
-          else
-          {
-            echo $lang->get('privmsgs_folder_th_from');
-          }
-          echo '</th>
-                <th class="subhead">' . $lang->get('privmsgs_folder_th_subject') . '</th>
-                <th class="subhead">' . $lang->get('privmsgs_folder_th_date') . '</th>
-                <th class="subhead">' . $lang->get('privmsgs_folder_th_mark') . '</th>
-              </tr>';
-          if($db->numrows() < 1)
-          {
-            echo '<tr><td style="text-align: center;" class="row1" colspan="4">' . $lang->get('privmsgs_msg_no_messages') . '</td></tr>';
-          }
-          else
-          {
-            $cls = 'row2';
-            while ( $r = $db->fetchrow() )
-            {
-              if($cls == 'row2') $cls='row1';
-              else $cls = 'row2';
-              $mto = str_replace(' ', '_', $r['message_to']);
-              $mfr = str_replace(' ', '_', $r['message_from']);
-              echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', ( $fname == 'drafts') ? $mto : $mfr).'">';
-              if ( $fname == 'drafts' || $fname == 'outbox' )
-              {
-                echo $r['message_to'];
-              }
-              else
-              {
-                echo $r['message_from'];
-              }
-              
-              echo '</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/'.$act.'/'.$r['message_id']).'">';
-              
-              if ( $r['message_read'] == 0 )
-              {
-                echo '<b>';
-              }
-              echo $r['subject'];
-              if ( $r['message_read'] == 0 )
-              {
-                echo '</b>';
-              }
-              echo '</a></td><td class="'.$cls.'">'.enano_date(ED_DATE | ED_TIME, $r['date']).'</td><td class="'.$cls.'" style="text-align: center;"><input name="marked_'.$r['message_id'].'" type="checkbox" /></td></tr>';
-            }
-            $db->free_result();
-          }
-          echo '<tr>
-                  <th style="text-align: right;" colspan="4">
-                    <input type="hidden" name="folder" value="'.$fname.'" />
-                    <input type="submit" name="archive" value="' . $lang->get('privmsgs_btn_archive_selected') . '" />
-                    <input type="submit" name="delete" value="' . $lang->get('privmsgs_btn_delete_selected') . '" />
-                    <input type="submit" name="deleteall" value="' . $lang->get('privmsgs_btn_delete_all') . '" />
-                  </th>
-                </tr>';
-          echo '</table></div></form>
-          <br />
-          <a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/').'">' . $lang->get('privmsgs_btn_compose') . '</a>
-          </td></tr></table>';
-          break;
-      }
-      $template->footer();
-      break;
-    case 'PostHandler':
-      $fname = $db->escape(strtolower($_POST['folder']));
-      if($fname=='drafts' || $fname=='outbox')
-      {
-        $q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject FROM '.table_prefix.'privmsgs AS p WHERE p.folder_name=\''.$fname.'\' AND p.message_from=\''.$session->username.'\' ORDER BY date DESC;');  
-      } else {
-        $q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject FROM '.table_prefix.'privmsgs AS p WHERE p.folder_name=\''.$fname.'\' AND p.message_to=\''.$session->username.'\' ORDER BY date DESC;');
-      }
-      if(!$q) $db->_die('The private message data could not be selected.');
-          
-      if(isset($_POST['archive'])) {
-        while($row = $db->fetchrow($q))
-        {
-          if(isset($_POST['marked_'.$row['message_id']]))
-          {
-            $e = $db->sql_query('UPDATE '.table_prefix.'privmsgs SET folder_name=\'archive\' WHERE message_id='.$row['message_id'].';');
-            if(!$e) $db->_die('Message '.$row['message_id'].' was not successfully moved.');
-            $db->free_result();
-          }
-        }
-      } elseif(isset($_POST['delete'])) {
-        while($row = $db->fetchrow($q))
-        {
-          if(isset($_POST['marked_'.$row['message_id']]))
-          {
-            $e = $db->sql_query('DELETE FROM '.table_prefix.'privmsgs WHERE message_id='.$row['message_id'].';');
-            if(!$e) $db->_die('Message '.$row['message_id'].' was not successfully moved.');
-            $db->free_result();
-          }
-        }
-      } elseif(isset($_POST['deleteall'])) {
-        while($row = $db->fetchrow($q))
-        {
-          $e = $db->sql_query('DELETE FROM '.table_prefix.'privmsgs WHERE message_id='.$row['message_id'].';');
-          if(!$e) $db->_die('Message '.$row['message_id'].' was not successfully moved.');
-          $db->free_result();
-        }
-      } else {
-        die_friendly('Invalid request', 'This section can only be accessed from within another Private Message section.');
-      }
-      $db->free_result($q);
-      header('Location: '.makeUrlNS('Special', 'PrivateMessages/Folder/'. substr(strtoupper($_POST['folder']), 0, 1) . substr(strtolower($_POST['folder']), 1, strlen($_POST['folder'])) ));
-      break;
-    case 'FriendList':
-      if($argv[1] == 'Add')
-      {
-        if(isset($_POST['_go']))
-          $buddyname = $_POST['buddyname'];
-        elseif($argv[2])
-          $buddyname = $argv[2];
-        else
-          die_friendly('Error adding buddy', '<p>No name specified</p>');
-        $q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($buddyname).'\'');
-        if(!$q) $db->_die('The buddy\'s user ID could not be selected.');
-        if($db->numrows() < 1) echo '<h3>Error adding buddy</h3><p>The username you entered is not in use by any registered user.</p>';
-        {
-          $r = $db->fetchrow();
-          $db->free_result();
-          $q = $db->sql_query('INSERT INTO '.table_prefix.'buddies(user_id,buddy_user_id,is_friend) VALUES('.$session->user_id.', '.$r['user_id'].', 1);');
-          if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be added: '.$db->get_error().'</p>';
-          $db->free_result();
-        }
-      } elseif($argv[1] == 'Remove' && preg_match('#^([0-9]+)$#', $argv[2])) {
-        // Using WHERE user_id prevents users from deleting others' buddies
-        $q = $db->sql_query('DELETE FROM '.table_prefix.'buddies WHERE user_id='.$session->user_id.' AND buddy_id='.$argv[2].';');
-        $db->free_result();
-        if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be deleted: '.$db->get_error().'</p>';
-        if(mysql_affected_rows() < 1) echo '<h3>Warning:</h3><p>No rows were affected. Either the selected buddy ID does not exist or you tried to delete someone else\'s buddy.</p>';
-      }
-      $template->header();
-      userprefs_show_menu();
-      ?>
-      <table border="0" width="100%" cellspacing="10" cellpadding="0">
-          <tr>
-          <td style="padding: 0px; width: 120px;" valign="top"  >
-          <div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
-          <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_privmsgs'); ?></small></th></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'); ?>"><?php echo $lang->get('privmsgs_folder_inbox'); ?></a></small></td></tr>
-          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'); ?>"><?php echo $lang->get('privmsgs_folder_outbox'); ?></a></small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Sent'); ?>"><?php echo $lang->get('privmsgs_folder_sent'); ?></a></small></td></tr>
-          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'); ?>"><?php echo $lang->get('privmsgs_folder_drafts'); ?></a></small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Archive'); ?>"><?php echo $lang->get('privmsgs_folder_archive'); ?></a></small></td></tr>
-          <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_buddies'); ?></small></th></tr>
-          <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>"><?php echo $lang->get('privmsgs_sidebar_friend_list'); ?></a></small></td></tr>
-          <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>"><?php echo $lang->get('privmsgs_sidebar_foe_list'); ?></a></small></td></tr>
-          </table></div>
-          </td>
-          <td valign="top">
-        <?php
-        $q = $db->sql_query('SELECT u.username,b.buddy_id FROM '.table_prefix.'buddies AS b LEFT JOIN '.table_prefix.'users AS u ON ( u.user_id=b.buddy_user_id ) WHERE b.user_id='.$session->user_id.' AND is_friend=1;');
-        if(!$q) $db->_die('The buddy list could not be selected.');
-        else 
-        {
-          $allbuds = '';
-          echo '<br /><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr><th colspan="3">' . $lang->get('privmsgs_th_buddy_list', array('username' => htmlspecialchars($session->username))) . '</th></tr>';
-          if($db->numrows() < 1) echo '<tr><td class="row3">' . $lang->get('privmsgs_msg_no_buddies') . '</td></tr>';
-          $cls = 'row2';
-          while ( $row = $db->fetchrow() )
-          {
-            if($cls=='row2') $cls = 'row1';
-            else $cls = 'row2';
-            echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', str_replace(' ', '_', $row['username'])).'" '. ( isPage($paths->nslist['User'].str_replace(' ', '_', $row['username'])) ? '' : 'class="wikilink-nonexistent" ' ) .'>'.$row['username'].'</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.str_replace(' ', '_', $row['username'])).'">' . $lang->get('privmsgs_btn_buddy_send_pm') . '</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Remove/'.$row['buddy_id']).'">' . $lang->get('privmsgs_btn_buddy_remove') . '</a></td></tr>';
-            $allbuds .= str_replace(' ', '_', $row['username']).',';
-          }
-          $db->free_result();
-          $allbuds = substr($allbuds, 0, strlen($allbuds)-1);
-          if($cls=='row2') $cls = 'row1';
-          else $cls = 'row2';
-          echo '<tr><td colspan="3" class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.$allbuds).'">' . $lang->get('privmsgs_btn_pm_all_buddies') . '</a></td></tr>';
-          echo '</table></div>';
-        }
-        echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Add').'" method="post" onsubmit="if(!submitAuthorized) return false;">
-              <h3>' . $lang->get('privmsgs_heading_add_buddy') . '</h3>';
-        echo '<p>' . $lang->get('privmsgs_lbl_username') . ' '.$template->username_field('buddyname').'  <input type="submit" name="_go" value="' . $lang->get('privmsgs_btn_add') . '" /></p>';
-        echo '</form>';
-        ?>
-        </td>
-        </tr>
-        </table>
-        <?php
-      $template->footer();
-      break;
-    case 'FoeList':
-      if($argv[1] == 'Add' && isset($_POST['_go']))
-      {
-        $q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($_POST['buddyname']).'\'');
-        if(!$q) $db->_die('The buddy\'s user ID could not be selected.');
-        if($db->numrows() < 1) echo '<h3>Error adding buddy</h3><p>The username you entered is not in use by any registered user.</p>';
-        {
-          $r = $db->fetchrow();
-          $q = $db->sql_query('INSERT INTO '.table_prefix.'buddies(user_id,buddy_user_id,is_friend) VALUES('.$session->user_id.', '.$r['user_id'].', 0);');
-          if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be added: '.$db->get_error().'</p>';
-        }
-        $db->free_result();
-      } elseif($argv[1] == 'Remove' && preg_match('#^([0-9]+)$#', $argv[2])) {
-        // Using WHERE user_id prevents users from deleting others' buddies
-        $q = $db->sql_query('DELETE FROM '.table_prefix.'buddies WHERE user_id='.$session->user_id.' AND buddy_id='.$argv[2].';');
-        $db->free_result();
-        if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be deleted: '.$db->get_error().'</p>';
-        if(mysql_affected_rows() < 1) echo '<h3>Warning:</h3><p>No rows were affected. Either the selected buddy ID does not exist or you tried to delete someone else\'s buddy.</p>';
-      }
-      $template->header();
-      userprefs_show_menu();
-      ?>
-        <table border="0" width="100%" cellspacing="10" cellpadding="0">
-        <tr>
-        <td style="padding: 0px; width: 120px;" valign="top"  >
-        <div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
-        <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_privmsgs'); ?></small></th></tr>
-        <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'); ?>"><?php echo $lang->get('privmsgs_folder_inbox'); ?></a></small></td></tr>
-        <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'); ?>"><?php echo $lang->get('privmsgs_folder_outbox'); ?></a></small></td></tr>
-        <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Sent'); ?>"><?php echo $lang->get('privmsgs_folder_sent'); ?></a></small></td></tr>
-        <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'); ?>"><?php echo $lang->get('privmsgs_folder_drafts'); ?></a></small></td></tr>
-        <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Archive'); ?>"><?php echo $lang->get('privmsgs_folder_archive'); ?></a></small></td></tr>
-        <tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_buddies'); ?></small></th></tr>
-        <tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>"><?php echo $lang->get('privmsgs_sidebar_friend_list'); ?></a></small></td></tr>
-        <tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>"><?php echo $lang->get('privmsgs_sidebar_foe_list'); ?></a></small></td></tr>
-        </table></div>
-        </td>
-        <td valign="top">
-        <?php
-        $q = $db->sql_query('SELECT u.username,b.buddy_id FROM '.table_prefix.'buddies AS b LEFT JOIN '.table_prefix.'users AS u ON ( u.user_id=b.buddy_user_id ) WHERE b.user_id='.$session->user_id.' AND is_friend=0;');
-        if(!$q) $db->_die('The buddy list could not be selected.');
-        else 
-        {
-          $allbuds = '';
-          echo '<br /><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr><th colspan="3">' . $lang->get('privmsgs_th_foe_list', array('username' => htmlspecialchars($session->username))) . '</th></tr>';
-          if($db->numrows() < 1) echo '<tr><td class="row3">' . $lang->get('privmsgs_msg_no_foes') . '</td></tr>';
-          $cls = 'row2';
-          while ( $row = $db->fetchrow() )
-          {
-            if($cls=='row2') $cls = 'row1';
-            else $cls = 'row2';
-            echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', str_replace(' ', '_', $row['username'])).'" '. ( isPage($paths->nslist['User'].str_replace(' ', '_', $row['username'])) ? '' : 'class="wikilink-nonexistent" ' ) .'>'.$row['username'].'</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.str_replace(' ', '_', $row['username'])).'">' . $lang->get('privmsgs_btn_buddy_send_pm') . '</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/FoeList/Remove/'.$row['buddy_id']).'">' . $lang->get('privmsgs_btn_buddy_remove') . '</a></td></tr>';
-            $allbuds .= str_replace(' ', '_', $row['username']).',';
-          }
-          $db->free_result();
-          $allbuds = substr($allbuds, 0, strlen($allbuds)-1);
-          if($cls=='row2') $cls = 'row1';
-          else $cls = 'row2';
-          echo '</table></div>';
-        }
-        echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/FoeList/Add').'" method="post" onsubmit="if(!submitAuthorized) return false;">
-              <h3>' . $lang->get('privmsgs_heading_add_foe') . '</h3>';
-        echo '<p>' . $lang->get('privmsgs_lbl_username') . ' '.$template->username_field('buddyname').'  <input type="submit" name="_go" value="' . $lang->get('privmsgs_btn_add') . '" /></p>';
-        echo '</form>';
-        ?>
-        </td>
-        </tr>
-        </table>
-        <?php
-      $template->footer();
-      break;
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( !$session->user_logged_in )
+	{
+		die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('privmsgs_err_need_login', array('login_link' => makeUrlNS('Special', 'Login/' . $paths->page))) . '</p>');
+	}
+	$argv = Array();
+	$argv[] = $paths->getParam(0);
+	$argv[] = $paths->getParam(1);
+	$argv[] = $paths->getParam(2);
+	if ( !$argv[0] )
+	{
+		$argv[0] = 'InVaLiD';
+	}
+	switch($argv[0])
+	{
+		default:
+			header('Location: '.makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'));
+			break;
+		case 'View':
+			$id = $argv[1];
+			if ( !ctype_digit($id) )
+			{
+				die_friendly('Message error', '<p>Invalid message ID</p>');
+			}
+			$q = $db->sql_query('SELECT p.message_from, p.message_to, p.subject, p.message_text, p.date, p.folder_name, u.signature FROM '.table_prefix.'privmsgs AS p LEFT JOIN '.table_prefix.'users AS u ON (p.message_from=u.username) WHERE message_id='.$id.'');
+			if ( !$q )
+			{
+				$db->_die('The message data could not be selected.');
+			}
+			$r = $db->fetchrow();
+			$db->free_result();
+			if ( ($r['message_to'] != $session->username && $r['message_from'] != $session->username ) || $r['folder_name']=='drafts' )
+			{
+				die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('privmsgs_err_not_authorized_read') . '</p>');
+			}
+			if ( $r['message_to'] == $session->username )
+			{
+				$q = $db->sql_query('UPDATE '.table_prefix.'privmsgs SET message_read=1 WHERE message_id='.$id.'');
+				$db->free_result();
+				if ( !$q )
+				{
+					$db->_die('Could not mark message as read');
+				}
+			}
+			$template->header();
+			userprefs_show_menu();
+			?>
+				<br />
+				<div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4">
+					<tr><th colspan="2"><?php echo $lang->get('privmsgs_lbl_message_from', array('sender' => htmlspecialchars($r['message_from']))); ?></th></tr>
+					<tr><td class="row1"><?php echo $lang->get('privmsgs_lbl_subject') ?></td><td class="row1"><?php echo $r['subject']; ?></td></tr>
+					<tr><td class="row2"><?php echo $lang->get('privmsgs_lbl_date') ?></td><td class="row2"><?php echo enano_date(ED_DATE | ED_TIME, $r['date']); ?></td></tr>
+					<tr><td class="row1"><?php echo $lang->get('privmsgs_lbl_message') ?></td><td class="row1"><?php echo RenderMan::render($r['message_text']);
+					if ( $r['signature'] != '' )
+					{
+						echo '<hr style="margin-left: 1em; width: 200px;" />';
+						echo RenderMan::render($r['signature']);
+					}
+					?></td></tr>
+					<tr><td colspan="2" class="row3"><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Compose/ReplyTo/'.$id); ?>"><?php echo $lang->get('privmsgs_btn_send_reply'); ?></a>  |  <a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Delete/'.$id); ?>">Delete message</a>  |  <?php if($r['folder_name'] != 'archive') { ?><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Move/'.$id.'/Archive'); ?>"><?php echo $lang->get('privmsgs_btn_archive'); ?></a>  |  <?php } ?><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox') ?>"><?php echo $lang->get('privmsgs_btn_return_to_inbox'); ?></a></td></tr>
+				</table></div>
+			<?php
+			$template->footer();              
+			break;
+		case 'Move':
+			$id = $argv[1];
+			if ( !ctype_digit($id) )
+			{
+				die_friendly('Message error', '<p>Invalid message ID</p>');
+			}
+			$q = $db->sql_query('SELECT message_to FROM '.table_prefix.'privmsgs WHERE message_id='.$id.'');
+			if ( !$q )
+			{
+				$db->_die('The message data could not be selected.');
+			}
+			$r = $db->fetchrow();
+			$db->free_result();
+			if ( $r['message_to'] != $session->username )
+			{
+				die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('privmsgs_err_not_authorized_edit') . '</p>');
+			}
+			$fname = $argv[2];
+			if ( !$fname || ( $fname != 'Inbox' && $fname != 'Outbox' && $fname != 'Sent' && $fname != 'Drafts' && $fname != 'Archive' ) )
+			{
+				die_friendly('Invalid request', '<p>The folder name "'.$fname.'" is invalid.</p>');
+			}
+			$q = $db->sql_query('UPDATE '.table_prefix.'privmsgs SET folder_name=\''.strtolower($fname).'\' WHERE message_id='.$id.';');
+			$db->free_result();
+			if ( !$q )
+			{
+				$db->_die('The message was not successfully moved.');
+			}
+			die_friendly($lang->get('privmsgs_msg_message_status'), '<p>' . $lang->get('privmsgs_msg_message_moved', array('folder' => $fname)) . '</p><p><a href="'.makeUrlNS('Special', 'PrivateMessages/Folder/Inbox').'">' . $lang->get('privmsgs_btn_return_to_inbox') . '</a></p>');
+			break;
+		case 'Delete':
+			$id = $argv[1];
+			if ( !ctype_digit($id) )
+			{
+				die_friendly('Message error', '<p>Invalid message ID</p>');
+			}
+			$q = $db->sql_query('SELECT message_to FROM '.table_prefix.'privmsgs WHERE message_id='.$id.'');
+			if ( !$q )
+			{
+				$db->_die('The message data could not be selected.');
+			}
+			$r = $db->fetchrow();
+			if ( $r['message_to'] != $session->username )
+			{
+				die_friendly($lang->get('etc_access_denied_short'), '<p>You are not authorized to delete this message.</p>');
+			}
+			$q = $db->sql_query('DELETE FROM '.table_prefix.'privmsgs WHERE message_id='.$id.';');
+			if ( !$q )
+			{
+				$db->_die('The message was not successfully deleted.');
+			}
+			$db->free_result();
+			die_friendly($lang->get('privmsgs_msg_message_status'), '<p>' . $lang->get('privmsgs_msg_message_deleted') . '</p><p><a href="'.makeUrlNS('Special', 'PrivateMessages/Folder/Inbox').'">' . $lang->get('privmsgs_btn_return_to_inbox') . '</a></p>');
+			break;
+		case 'Compose':
+			if ( $argv[1]=='Send' && isset($_POST['_send']) )
+			{
+				// Check each POST DATA parameter...
+				$errors = array();
+				if(!isset($_POST['to']) || ( isset($_POST['to']) && $_POST['to'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_username');
+				}
+				if(!isset($_POST['subject']) || ( isset($_POST['subject']) && $_POST['subject'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_subject');
+				}
+				if(!isset($_POST['message']) || ( isset($_POST['message']) && $_POST['message'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_message');
+				}
+				if ( count($errors) < 1 )
+				{
+					$namelist = $_POST['to'];
+					$namelist = str_replace(', ', ',', $namelist);
+					$namelist = explode(',', $namelist);
+					foreach($namelist as $n) { $n = $db->escape($n); }
+					$subject = RenderMan::preprocess_text($_POST['subject']);
+					$message = RenderMan::preprocess_text($_POST['message']);
+					$base_query = 'INSERT INTO '.table_prefix.'privmsgs(message_from,message_to,date,subject,message_text,folder_name,message_read) VALUES';
+					foreach($namelist as $n)
+					{
+						$base_query .= '(\''.$session->username.'\', \''.$n.'\', '.time().', \''.$subject.'\', \''.$message.'\', \'inbox\', 0),';
+					}
+					$base_query = substr($base_query, 0, strlen($base_query)-1) . ';';
+					$result = $db->sql_query($base_query);
+					$db->free_result();
+					if ( !$result )
+					{
+						$db->_die('The message could not be sent.');
+					}
+					else
+					{
+						die_friendly($lang->get('privmsgs_msg_message_status'), '<p>' . $lang->get('privmsgs_msg_message_sent', array('inbox_link' => makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'))) . '</p>');
+					}
+					return;
+				}
+			}
+			else if ( $argv[1] == 'Send' && isset($_POST['_savedraft'] ) )
+			{
+				$errors = array();
+				if ( !isset($_POST['to']) || ( isset($_POST['to']) && $_POST['to'] == '') )
+				{
+					$errors[] = $lang->get('privmsgs_err_need_username');
+				}
+				if ( !isset($_POST['subject']) || ( isset($_POST['subject']) && $_POST['subject'] == '') )
+				{
+					$errors[] = $lang->get('privmsgs_err_need_subject');
+				}
+				if ( !isset($_POST['message']) || ( isset($_POST['message']) && $_POST['message'] == '') )
+				{
+					$errors[] = $lang->get('privmsgs_err_need_message');
+				}
+				if ( count($errors) < 1 )
+				{
+					$namelist = $_POST['to'];
+					$namelist = str_replace(', ', ',', $namelist);
+					$namelist = explode(',', $namelist);
+					foreach($namelist as $n)
+					{
+						$n = $db->escape($n);
+					}
+					if ( count($namelist) > MAX_PMS_PER_BATCH && !$session->get_permssions('mod_misc') )
+					{
+						die_friendly($lang->get('privmsgs_err_limit_exceeded_title'), '<p>' . $lang->get('privmsgs_err_limit_exceeded_body', array('limit' => MAX_PMS_PER_BATCH)) . '</p>');
+					}
+					$subject = $db->escape($_POST['subject']);
+					$message = RenderMan::preprocess_text($_POST['message']);
+					$base_query = 'INSERT INTO '.table_prefix.'privmsgs(message_from,message_to,date,subject,message_text,folder_name,message_read) VALUES';
+					foreach($namelist as $n)
+					{
+						$base_query .= '(\''.$session->username.'\', \''.$n.'\', '.time().', \''.$subject.'\', \''.$message.'\', \'drafts\', 0),';
+					}
+					$base_query = substr($base_query, 0, strlen($base_query) - 1) . ';';
+					$result = $db->sql_query($base_query);
+					$db->free_result();
+					if ( !$result )
+					{
+						$db->_die('The message could not be saved.');
+					}
+				}
+			}
+			else if(isset($_POST['_inbox']))
+			{
+				redirect(makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'), '', '', 0);
+			}
+			if($argv[1] == 'ReplyTo' && preg_match('#^([0-9]+)$#', $argv[2]))
+			{
+				$to = '';
+				$text = '';
+				$subj = '';
+				$id = $argv[2];
+				$q = $db->sql_query('SELECT p.message_from, p.message_to, p.subject, p.message_text, p.date, p.folder_name, u.signature FROM '.table_prefix.'privmsgs AS p LEFT JOIN '.table_prefix.'users AS u ON (p.message_from=u.username) WHERE message_id='.$id.';');
+				if ( !$q )
+					$db->_die('The message data could not be selected.');
+				
+				$r = $db->fetchrow();
+				$db->free_result();
+				if ( ($r['message_to'] != $session->username && $r['message_from'] != $session->username ) || $r['folder_name'] == 'drafts' )
+				{
+					die_friendly($lang->get('etc_access_denied_short'), '<p>You are not authorized to view the contents of this message.</p>');
+				}
+				$subj = 'Re: ' . $r['subject'];
+				$text = "\n\n\nOn " . enano_date(ED_DATE | ED_TIME, $r['date']) . ", " . $r['message_from'] . " wrote:\n> " . str_replace("\n", "\n> ", $r['message_text']); // Way less complicated than using a regex ;-)
+				
+				$tbuf = $text;
+				while( preg_match("/\n([\> ]*?)\> \>/", $text) )
+				{
+					$text = preg_replace("/\n([\> ]*?)\> \>/", '\\1>>', $text);
+					if ( $text == $tbuf )
+						break;
+					$tbuf = $text;
+				}
+				
+				$to = $r['message_from'];
+			}
+			else
+			{
+				if ( ( $argv[1]=='to' || $argv[1]=='To' ) && $argv[2] )
+				{
+					$to = htmlspecialchars($argv[2]);
+				}
+				else
+				{
+					$to = '';
+				}
+				$text = '';
+				$subj = '';
+			}
+				$template->header();
+				userprefs_show_menu();
+				if ( isset($errors) && count($errors) > 0 )
+				{
+					echo '<div class="warning-box">
+									' . $lang->get('privmsgs_err_send_submit') . '
+									<ul>
+										<li>' . implode('</li><li>', $errors) . '</li>
+									</ul>
+								</div>';
+				}
+				echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/Compose/Send').'" method="post">';
+				
+				if ( isset($_POST['_savedraft']) )
+				{
+					echo '<div class="info-box">' . $lang->get('privmsgs_msg_draft_saved') . '</div>';
+				}
+				?>
+				<br />
+				<div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4">
+					<tr>
+						<th colspan="2"><?php echo $lang->get('privmsgs_lbl_compose_th'); ?></th>
+					</tr>
+					<tr>
+						<td class="row1">
+							<?php echo $lang->get('privmsgs_lbl_compose_to'); ?><br />
+							<small><?php echo $lang->get('privmsgs_lbl_compose_to_max', array('limit' => MAX_PMS_PER_BATCH)); ?></small>
+						</td>
+						<td class="row1">
+							<?php echo $template->username_field('to', (isset($_POST['_savedraft'])) ? $_POST['to'] : $to ); ?>
+						</td>
+					</tr>
+					<tr>
+						<td class="row2">
+							<?php echo $lang->get('privmsgs_lbl_subject'); ?>
+						</td>
+						<td class="row2">
+							<input name="subject" type="text" size="30" value="<?php if(isset($_POST['_savedraft'])) echo htmlspecialchars($_POST['subject']); else echo $subj; ?>" />
+						</td>
+					</tr>
+					<tr>
+						<td class="row1">
+							<?php echo $lang->get('privmsgs_lbl_message'); ?>
+						</td>
+						<td class="row1" style="min-width: 80%;">
+							<?php
+								if ( isset($_POST['_savedraft']) )
+								{
+									$content = htmlspecialchars($_POST['message']);
+								}
+								else
+								{
+									$content =& $text;
+								}
+								echo $template->tinymce_textarea('message', $content, 20, 40);
+							?>
+						</td>
+					</tr>
+					<tr>
+						<th class="subhead" colspan="2">
+							<input type="submit" name="_send" value="<?php echo $lang->get('privmsgs_btn_send'); ?>" />
+							<input type="submit" name="_savedraft" value="<?php echo $lang->get('privmsgs_btn_savedraft'); ?>" />
+							<input type="submit" name="_inbox" value="<?php echo $lang->get('privmsgs_btn_return_to_inbox'); ?>" />
+						</th>
+					</tr>
+				</table></div>
+				<?php
+				echo '</form>';
+				$template->footer();
+			break;
+		case 'Edit':
+			$id = $argv[1];
+			if ( !ctype_digit($id) )
+			{
+				die_friendly('Message error', '<p>Invalid message ID</p>');
+			}
+			$q = $db->sql_query('SELECT message_from, message_to, subject, message_text, date, folder_name, message_read FROM '.table_prefix.'privmsgs WHERE message_id='.$id.'');
+			if ( !$q )
+			{
+				$db->_die('The message data could not be selected.');
+			}
+			$r = $db->fetchrow();
+			$db->free_result();
+			if ( $r['message_from'] != $session->username || $r['message_read'] == 1 )
+			{
+				die_friendly($lang->get('etc_access_denied_short'), '<p>You are not authorized to edit this message.</p>');
+			}
+			$fname = $argv[2];
+			
+			if(isset($_POST['_send']))
+			{
+				// Check each POST DATA parameter...
+				$errors = array();
+				if(!isset($_POST['to']) || ( isset($_POST['to']) && $_POST['to'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_username');
+				}
+				if(!isset($_POST['subject']) || ( isset($_POST['subject']) && $_POST['subject'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_subject');
+				}
+				if(!isset($_POST['message']) || ( isset($_POST['message']) && $_POST['message'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_message');
+				}
+				if ( count($errors) < 1 )
+				{
+					$namelist = $_POST['to'];
+					$namelist = str_replace(', ', ',', $namelist);
+					$namelist = explode(',', $namelist);
+					foreach ($namelist as $n)
+					{
+						$n = $db->escape($n);
+					}
+					$subject = RenderMan::preprocess_text($_POST['subject']);
+					$message = RenderMan::preprocess_text($_POST['message']);
+					$base_query = 'UPDATE '.table_prefix.'privmsgs SET subject=\''.$subject.'\',message_to=\''.$namelist[0].'\',message_text=\''.$message.'\',folder_name=\'inbox\' WHERE message_id='.$id.';';
+					$result = $db->sql_query($base_query);
+					$db->free_result();
+					if ( !$result )
+					{
+						$db->_die('The message could not be sent.');
+					}
+					else
+					{
+						die_friendly($lang->get('privmsgs_msg_message_status'), '<p>' . $lang->get('privmsgs_msg_message_sent', array('inbox_link' => makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'))) . '</p>');
+					}
+					return;
+				}
+			}
+			else if ( isset($_POST['_savedraft']) )
+			{
+				// Check each POST DATA parameter...
+				$errors = array();
+				if(!isset($_POST['to']) || ( isset($_POST['to']) && $_POST['to'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_username');
+				}
+				if(!isset($_POST['subject']) || ( isset($_POST['subject']) && $_POST['subject'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_subject');
+				}
+				if(!isset($_POST['message']) || ( isset($_POST['message']) && $_POST['message'] == ''))
+				{
+					$errors[] = $lang->get('privmsgs_err_need_message');
+				}
+				if ( count($errors) < 1 )
+				{
+					$namelist = $_POST['to'];
+					$namelist = str_replace(', ', ',', $namelist);
+					$namelist = explode(',', $namelist);
+					foreach ( $namelist as $n )
+					{
+						$n = $db->escape($n);
+					}
+					$subject = $db->escape($_POST['subject']);
+					$message = RenderMan::preprocess_text($_POST['message']);
+					$base_query = 'UPDATE '.table_prefix.'privmsgs SET subject=\''.$subject.'\',message_to=\''.$namelist[0].'\',message_text=\''.$message.'\' WHERE message_id='.$id.';';
+					$result = $db->sql_query($base_query);
+					$db->free_result();
+					if ( !$result )
+					{
+						$db->_die('The message could not be saved.');
+					}
+				}
+			}
+				if ( $argv[1]=='to' && $argv[2] )
+				{
+					$to = htmlspecialchars($argv[2]);
+				}
+				else
+				{
+					$to = '';
+				}
+				$template->header();
+				userprefs_show_menu();
+				echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/Edit/'.$id).'" method="post">';
+				
+				if ( isset($_POST['_savedraft']) )
+				{
+					echo '<div class="info-box">' . $lang->get('privmsgs_msg_draft_saved') . '</div>';
+				}
+				?>
+				<br />
+				<div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4">
+					<tr><th colspan="2"><?php echo $lang->get('privmsgs_lbl_edit_th'); ?></th></tr>
+					<tr>
+						<td class="row1">
+							<?php echo $lang->get('privmsgs_lbl_compose_to'); ?><br />
+							<small><?php echo $lang->get('privmsgs_lbl_compose_to_max', array('limit' => MAX_PMS_PER_BATCH)); ?></small>
+						</td>
+						<td class="row1">
+							<?php echo $template->username_field('to', (isset($_POST['_savedraft'])) ? $_POST['to'] : $r['message_to'] ); ?>
+						</td>
+					</tr>
+					<tr>
+						<td class="row2">
+							<?php echo $lang->get('privmsgs_lbl_subject'); ?>
+						</td>
+						<td class="row2">
+							<input name="subject" type="text" size="30" value="<?php if(isset($_POST['_savedraft'])) echo htmlspecialchars($_POST['subject']); else echo $r['subject']; ?>" />
+						</td>
+					</tr>
+					<tr>
+						<td class="row1">
+							<?php echo $lang->get('privmsgs_lbl_message'); ?>
+						</td>
+						<td class="row1" style="min-width: 80%;">
+							<?php
+								if ( isset($_POST['_savedraft']) )
+								{
+									$content = htmlspecialchars($_POST['message']);
+								}
+								else
+								{
+									$content =& $r['message_text'];
+								}
+								echo $template->tinymce_textarea('message', $content, 20, 40);
+							?>
+						</td>
+					</tr>
+					
+					<tr>
+						<th class="subhead" colspan="2">
+							<input type="submit" name="_send" value="<?php echo $lang->get('privmsgs_btn_send'); ?>" />
+							<input type="submit" name="_savedraft" value="<?php echo $lang->get('privmsgs_btn_savedraft'); ?>" />
+						</th>
+					</tr>
+				</table></div>
+				<?php
+				echo '</form>';
+				$template->footer();
+			break;
+		case 'Folder':
+			$template->header();
+			userprefs_show_menu();
+			switch($argv[1])
+			{
+				default:
+					echo '<p>' . $lang->get('privmsgs_err_folder_not_exist', array(
+							'folder_name' => htmlspecialchars($argv[1]),
+							'inbox_url' => makeUrlNS('Special', 'PrivateMessages/Folder/Inbox')
+						)) . '</p>';
+					break;
+				case 'Inbox':
+				case 'Outbox':
+				case 'Sent':
+				case 'Drafts':
+				case 'Archive':
+					?>
+					<table border="0" width="100%" cellspacing="10" cellpadding="0">
+					<tr>
+					<td style="padding: 0px; width: 120px;" valign="top"  >
+					<div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
+					<tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_privmsgs'); ?></small></th></tr>
+					<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'); ?>"><?php echo $lang->get('privmsgs_folder_inbox'); ?></a></small></td></tr>
+					<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'); ?>"><?php echo $lang->get('privmsgs_folder_outbox'); ?></a></small></td></tr>
+					<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Sent'); ?>"><?php echo $lang->get('privmsgs_folder_sent'); ?></a></small></td></tr>
+					<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'); ?>"><?php echo $lang->get('privmsgs_folder_drafts'); ?></a></small></td></tr>
+					<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Archive'); ?>"><?php echo $lang->get('privmsgs_folder_archive'); ?></a></small></td></tr>
+					<tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_buddies'); ?></small></th></tr>
+					<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>"><?php echo $lang->get('privmsgs_sidebar_friend_list'); ?></a></small></td></tr>
+					<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>"><?php echo $lang->get('privmsgs_sidebar_foe_list'); ?></a></small></td></tr>
+					</table></div>
+					</td>
+					<td valign="top">
+					<?php
+					$fname = strtolower($argv[1]);
+					switch($argv[1])
+					{
+						case 'Inbox':
+						case 'Archive':
+						default:
+							$q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject, p.message_read FROM '.table_prefix.'privmsgs AS p WHERE p.folder_name=\''.$fname.'\' AND p.message_to=\''.$session->username.'\' ORDER BY date DESC;');
+							break;
+						case 'Outbox':
+							$q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject, p.message_read FROM '.table_prefix.'privmsgs AS p WHERE p.message_from=\''.$session->username.'\' AND message_read=0 ORDER BY date DESC;');
+							break;
+						case 'Sent':
+							$q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject, p.message_read FROM '.table_prefix.'privmsgs AS p WHERE p.message_from=\''.$session->username.'\' AND message_read=1 ORDER BY date DESC;');
+							break;
+						case 'Drafts':
+							$q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject, p.message_read FROM '.table_prefix.'privmsgs AS p WHERE p.folder_name=\''.$fname.'\' AND p.message_from=\''.$session->username.'\' ORDER BY date DESC;');
+							break;
+					}
+					if ( !$q )
+					{
+						$db->_die('The private message data could not be selected.');
+					}
+					if ( $argv[1] == 'Drafts' || $argv[1] == 'Outbox' )
+					{
+						$act = 'Edit';
+					}
+					else
+					{
+						$act = 'View';
+					}
+					echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/PostHandler').'" method="post">
+									<div class="tblholder">
+										<table border="0" width="100%" cellspacing="1" cellpadding="4">
+											<tr>
+												<th colspan="4" style="text-align: left;">' . $lang->get('privmsgs_folder_th_foldername') . ' ' . $lang->get('privmsgs_folder_' . strtolower($argv[1])) . '</th>
+											</tr>
+										<tr>
+											<th class="subhead">';
+					if ( $fname == 'drafts' || $fname == 'Outbox' )
+					{
+						echo $lang->get('privmsgs_folder_th_to');
+					}
+					else
+					{
+						echo $lang->get('privmsgs_folder_th_from');
+					}
+					echo '</th>
+								<th class="subhead">' . $lang->get('privmsgs_folder_th_subject') . '</th>
+								<th class="subhead">' . $lang->get('privmsgs_folder_th_date') . '</th>
+								<th class="subhead">' . $lang->get('privmsgs_folder_th_mark') . '</th>
+							</tr>';
+					if($db->numrows() < 1)
+					{
+						echo '<tr><td style="text-align: center;" class="row1" colspan="4">' . $lang->get('privmsgs_msg_no_messages') . '</td></tr>';
+					}
+					else
+					{
+						$cls = 'row2';
+						while ( $r = $db->fetchrow() )
+						{
+							if($cls == 'row2') $cls='row1';
+							else $cls = 'row2';
+							$mto = str_replace(' ', '_', $r['message_to']);
+							$mfr = str_replace(' ', '_', $r['message_from']);
+							echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', ( $fname == 'drafts') ? $mto : $mfr).'">';
+							if ( $fname == 'drafts' || $fname == 'outbox' )
+							{
+								echo $r['message_to'];
+							}
+							else
+							{
+								echo $r['message_from'];
+							}
+							
+							echo '</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/'.$act.'/'.$r['message_id']).'">';
+							
+							if ( $r['message_read'] == 0 )
+							{
+								echo '<b>';
+							}
+							echo $r['subject'];
+							if ( $r['message_read'] == 0 )
+							{
+								echo '</b>';
+							}
+							echo '</a></td><td class="'.$cls.'">'.enano_date(ED_DATE | ED_TIME, $r['date']).'</td><td class="'.$cls.'" style="text-align: center;"><input name="marked_'.$r['message_id'].'" type="checkbox" /></td></tr>';
+						}
+						$db->free_result();
+					}
+					echo '<tr>
+									<th style="text-align: right;" colspan="4">
+										<input type="hidden" name="folder" value="'.$fname.'" />
+										<input type="submit" name="archive" value="' . $lang->get('privmsgs_btn_archive_selected') . '" />
+										<input type="submit" name="delete" value="' . $lang->get('privmsgs_btn_delete_selected') . '" />
+										<input type="submit" name="deleteall" value="' . $lang->get('privmsgs_btn_delete_all') . '" />
+									</th>
+								</tr>';
+					echo '</table></div></form>
+					<br />
+					<a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/').'">' . $lang->get('privmsgs_btn_compose') . '</a>
+					</td></tr></table>';
+					break;
+			}
+			$template->footer();
+			break;
+		case 'PostHandler':
+			$fname = $db->escape(strtolower($_POST['folder']));
+			if($fname=='drafts' || $fname=='outbox')
+			{
+				$q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject FROM '.table_prefix.'privmsgs AS p WHERE p.folder_name=\''.$fname.'\' AND p.message_from=\''.$session->username.'\' ORDER BY date DESC;');  
+			} else {
+				$q = $db->sql_query('SELECT p.message_id, p.message_from, p.message_to, p.date, p.subject FROM '.table_prefix.'privmsgs AS p WHERE p.folder_name=\''.$fname.'\' AND p.message_to=\''.$session->username.'\' ORDER BY date DESC;');
+			}
+			if(!$q) $db->_die('The private message data could not be selected.');
+					
+			if(isset($_POST['archive'])) {
+				while($row = $db->fetchrow($q))
+				{
+					if(isset($_POST['marked_'.$row['message_id']]))
+					{
+						$e = $db->sql_query('UPDATE '.table_prefix.'privmsgs SET folder_name=\'archive\' WHERE message_id='.$row['message_id'].';');
+						if(!$e) $db->_die('Message '.$row['message_id'].' was not successfully moved.');
+						$db->free_result();
+					}
+				}
+			} elseif(isset($_POST['delete'])) {
+				while($row = $db->fetchrow($q))
+				{
+					if(isset($_POST['marked_'.$row['message_id']]))
+					{
+						$e = $db->sql_query('DELETE FROM '.table_prefix.'privmsgs WHERE message_id='.$row['message_id'].';');
+						if(!$e) $db->_die('Message '.$row['message_id'].' was not successfully moved.');
+						$db->free_result();
+					}
+				}
+			} elseif(isset($_POST['deleteall'])) {
+				while($row = $db->fetchrow($q))
+				{
+					$e = $db->sql_query('DELETE FROM '.table_prefix.'privmsgs WHERE message_id='.$row['message_id'].';');
+					if(!$e) $db->_die('Message '.$row['message_id'].' was not successfully moved.');
+					$db->free_result();
+				}
+			} else {
+				die_friendly('Invalid request', 'This section can only be accessed from within another Private Message section.');
+			}
+			$db->free_result($q);
+			header('Location: '.makeUrlNS('Special', 'PrivateMessages/Folder/'. substr(strtoupper($_POST['folder']), 0, 1) . substr(strtolower($_POST['folder']), 1, strlen($_POST['folder'])) ));
+			break;
+		case 'FriendList':
+			if($argv[1] == 'Add')
+			{
+				if(isset($_POST['_go']))
+					$buddyname = $_POST['buddyname'];
+				elseif($argv[2])
+					$buddyname = $argv[2];
+				else
+					die_friendly('Error adding buddy', '<p>No name specified</p>');
+				$q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($buddyname).'\'');
+				if(!$q) $db->_die('The buddy\'s user ID could not be selected.');
+				if($db->numrows() < 1) echo '<h3>Error adding buddy</h3><p>The username you entered is not in use by any registered user.</p>';
+				{
+					$r = $db->fetchrow();
+					$db->free_result();
+					$q = $db->sql_query('INSERT INTO '.table_prefix.'buddies(user_id,buddy_user_id,is_friend) VALUES('.$session->user_id.', '.$r['user_id'].', 1);');
+					if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be added: '.$db->get_error().'</p>';
+					$db->free_result();
+				}
+			} elseif($argv[1] == 'Remove' && preg_match('#^([0-9]+)$#', $argv[2])) {
+				// Using WHERE user_id prevents users from deleting others' buddies
+				$q = $db->sql_query('DELETE FROM '.table_prefix.'buddies WHERE user_id='.$session->user_id.' AND buddy_id='.$argv[2].';');
+				$db->free_result();
+				if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be deleted: '.$db->get_error().'</p>';
+				if(mysql_affected_rows() < 1) echo '<h3>Warning:</h3><p>No rows were affected. Either the selected buddy ID does not exist or you tried to delete someone else\'s buddy.</p>';
+			}
+			$template->header();
+			userprefs_show_menu();
+			?>
+			<table border="0" width="100%" cellspacing="10" cellpadding="0">
+					<tr>
+					<td style="padding: 0px; width: 120px;" valign="top"  >
+					<div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
+					<tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_privmsgs'); ?></small></th></tr>
+					<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'); ?>"><?php echo $lang->get('privmsgs_folder_inbox'); ?></a></small></td></tr>
+					<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'); ?>"><?php echo $lang->get('privmsgs_folder_outbox'); ?></a></small></td></tr>
+					<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Sent'); ?>"><?php echo $lang->get('privmsgs_folder_sent'); ?></a></small></td></tr>
+					<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'); ?>"><?php echo $lang->get('privmsgs_folder_drafts'); ?></a></small></td></tr>
+					<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Archive'); ?>"><?php echo $lang->get('privmsgs_folder_archive'); ?></a></small></td></tr>
+					<tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_buddies'); ?></small></th></tr>
+					<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>"><?php echo $lang->get('privmsgs_sidebar_friend_list'); ?></a></small></td></tr>
+					<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>"><?php echo $lang->get('privmsgs_sidebar_foe_list'); ?></a></small></td></tr>
+					</table></div>
+					</td>
+					<td valign="top">
+				<?php
+				$q = $db->sql_query('SELECT u.username,b.buddy_id FROM '.table_prefix.'buddies AS b LEFT JOIN '.table_prefix.'users AS u ON ( u.user_id=b.buddy_user_id ) WHERE b.user_id='.$session->user_id.' AND is_friend=1;');
+				if(!$q) $db->_die('The buddy list could not be selected.');
+				else 
+				{
+					$allbuds = '';
+					echo '<br /><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr><th colspan="3">' . $lang->get('privmsgs_th_buddy_list', array('username' => htmlspecialchars($session->username))) . '</th></tr>';
+					if($db->numrows() < 1) echo '<tr><td class="row3">' . $lang->get('privmsgs_msg_no_buddies') . '</td></tr>';
+					$cls = 'row2';
+					while ( $row = $db->fetchrow() )
+					{
+						if($cls=='row2') $cls = 'row1';
+						else $cls = 'row2';
+						echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', str_replace(' ', '_', $row['username'])).'" '. ( isPage($paths->nslist['User'].str_replace(' ', '_', $row['username'])) ? '' : 'class="wikilink-nonexistent" ' ) .'>'.$row['username'].'</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.str_replace(' ', '_', $row['username'])).'">' . $lang->get('privmsgs_btn_buddy_send_pm') . '</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Remove/'.$row['buddy_id']).'">' . $lang->get('privmsgs_btn_buddy_remove') . '</a></td></tr>';
+						$allbuds .= str_replace(' ', '_', $row['username']).',';
+					}
+					$db->free_result();
+					$allbuds = substr($allbuds, 0, strlen($allbuds)-1);
+					if($cls=='row2') $cls = 'row1';
+					else $cls = 'row2';
+					echo '<tr><td colspan="3" class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.$allbuds).'">' . $lang->get('privmsgs_btn_pm_all_buddies') . '</a></td></tr>';
+					echo '</table></div>';
+				}
+				echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/FriendList/Add').'" method="post" onsubmit="if(!submitAuthorized) return false;">
+							<h3>' . $lang->get('privmsgs_heading_add_buddy') . '</h3>';
+				echo '<p>' . $lang->get('privmsgs_lbl_username') . ' '.$template->username_field('buddyname').'  <input type="submit" name="_go" value="' . $lang->get('privmsgs_btn_add') . '" /></p>';
+				echo '</form>';
+				?>
+				</td>
+				</tr>
+				</table>
+				<?php
+			$template->footer();
+			break;
+		case 'FoeList':
+			if($argv[1] == 'Add' && isset($_POST['_go']))
+			{
+				$q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($_POST['buddyname']).'\'');
+				if(!$q) $db->_die('The buddy\'s user ID could not be selected.');
+				if($db->numrows() < 1) echo '<h3>Error adding buddy</h3><p>The username you entered is not in use by any registered user.</p>';
+				{
+					$r = $db->fetchrow();
+					$q = $db->sql_query('INSERT INTO '.table_prefix.'buddies(user_id,buddy_user_id,is_friend) VALUES('.$session->user_id.', '.$r['user_id'].', 0);');
+					if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be added: '.$db->get_error().'</p>';
+				}
+				$db->free_result();
+			} elseif($argv[1] == 'Remove' && preg_match('#^([0-9]+)$#', $argv[2])) {
+				// Using WHERE user_id prevents users from deleting others' buddies
+				$q = $db->sql_query('DELETE FROM '.table_prefix.'buddies WHERE user_id='.$session->user_id.' AND buddy_id='.$argv[2].';');
+				$db->free_result();
+				if(!$q) echo '<h3>Warning:</h3><p>Buddy could not be deleted: '.$db->get_error().'</p>';
+				if(mysql_affected_rows() < 1) echo '<h3>Warning:</h3><p>No rows were affected. Either the selected buddy ID does not exist or you tried to delete someone else\'s buddy.</p>';
+			}
+			$template->header();
+			userprefs_show_menu();
+			?>
+				<table border="0" width="100%" cellspacing="10" cellpadding="0">
+				<tr>
+				<td style="padding: 0px; width: 120px;" valign="top"  >
+				<div class="tblholder" style="width: 120px;"><table border="0" width="120" cellspacing="1" cellpadding="4">
+				<tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_privmsgs'); ?></small></th></tr>
+				<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'); ?>"><?php echo $lang->get('privmsgs_folder_inbox'); ?></a></small></td></tr>
+				<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'); ?>"><?php echo $lang->get('privmsgs_folder_outbox'); ?></a></small></td></tr>
+				<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Sent'); ?>"><?php echo $lang->get('privmsgs_folder_sent'); ?></a></small></td></tr>
+				<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'); ?>"><?php echo $lang->get('privmsgs_folder_drafts'); ?></a></small></td></tr>
+				<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/Folder/Archive'); ?>"><?php echo $lang->get('privmsgs_folder_archive'); ?></a></small></td></tr>
+				<tr><th><small><?php echo $lang->get('privmsgs_sidebar_th_buddies'); ?></small></th></tr>
+				<tr><td class="row2"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FriendList'); ?>"><?php echo $lang->get('privmsgs_sidebar_friend_list'); ?></a></small></td></tr>
+				<tr><td class="row1"><small><a href="<?php echo makeUrlNS('Special', 'PrivateMessages/FoeList'); ?>"><?php echo $lang->get('privmsgs_sidebar_foe_list'); ?></a></small></td></tr>
+				</table></div>
+				</td>
+				<td valign="top">
+				<?php
+				$q = $db->sql_query('SELECT u.username,b.buddy_id FROM '.table_prefix.'buddies AS b LEFT JOIN '.table_prefix.'users AS u ON ( u.user_id=b.buddy_user_id ) WHERE b.user_id='.$session->user_id.' AND is_friend=0;');
+				if(!$q) $db->_die('The buddy list could not be selected.');
+				else 
+				{
+					$allbuds = '';
+					echo '<br /><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr><th colspan="3">' . $lang->get('privmsgs_th_foe_list', array('username' => htmlspecialchars($session->username))) . '</th></tr>';
+					if($db->numrows() < 1) echo '<tr><td class="row3">' . $lang->get('privmsgs_msg_no_foes') . '</td></tr>';
+					$cls = 'row2';
+					while ( $row = $db->fetchrow() )
+					{
+						if($cls=='row2') $cls = 'row1';
+						else $cls = 'row2';
+						echo '<tr><td class="'.$cls.'"><a href="'.makeUrlNS('User', str_replace(' ', '_', $row['username'])).'" '. ( isPage($paths->nslist['User'].str_replace(' ', '_', $row['username'])) ? '' : 'class="wikilink-nonexistent" ' ) .'>'.$row['username'].'</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/Compose/to/'.str_replace(' ', '_', $row['username'])).'">' . $lang->get('privmsgs_btn_buddy_send_pm') . '</a></td><td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'PrivateMessages/FoeList/Remove/'.$row['buddy_id']).'">' . $lang->get('privmsgs_btn_buddy_remove') . '</a></td></tr>';
+						$allbuds .= str_replace(' ', '_', $row['username']).',';
+					}
+					$db->free_result();
+					$allbuds = substr($allbuds, 0, strlen($allbuds)-1);
+					if($cls=='row2') $cls = 'row1';
+					else $cls = 'row2';
+					echo '</table></div>';
+				}
+				echo '<form action="'.makeUrlNS('Special', 'PrivateMessages/FoeList/Add').'" method="post" onsubmit="if(!submitAuthorized) return false;">
+							<h3>' . $lang->get('privmsgs_heading_add_foe') . '</h3>';
+				echo '<p>' . $lang->get('privmsgs_lbl_username') . ' '.$template->username_field('buddyname').'  <input type="submit" name="_go" value="' . $lang->get('privmsgs_btn_add') . '" /></p>';
+				echo '</form>';
+				?>
+				</td>
+				</tr>
+				</table>
+				<?php
+			$template->footer();
+			break;
+	}
 }
 
 ?>
\ No newline at end of file
--- a/plugins/SpecialAdmin.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialAdmin.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_specialadmin_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_specialadmin_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_specialadmin_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_specialadmin_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -27,1420 +27,1420 @@
 
 function SpecialAdmin_paths_init()
 {
-  global $paths;
-  
-  register_special_page('Administration', 'specialpage_administration');
-  register_special_page('EditSidebar', 'specialpage_manage_sidebar');
+	global $paths;
+	
+	register_special_page('Administration', 'specialpage_administration');
+	register_special_page('EditSidebar', 'specialpage_manage_sidebar');
 }
 
 $plugins->attachHook('base_classes_initted', 'SpecialAdmin_include();');
 
 function SpecialAdmin_include()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  // Admin pages that were too enormous to be in this file were split off into the plugins/admin/ directory in 1.0.1.
-  // Only load these files if we're looking to load the admin panel
-  list($pid, $ns) = RenderMan::strToPageID($paths->get_pageid_from_url());
-  if ( $ns == 'Admin' || ( $pid == 'Administration' && $ns == 'Special' ) )
-  {
-    require(ENANO_ROOT . '/plugins/admin/Home.php');
-    require(ENANO_ROOT . '/plugins/admin/PageManager.php');
-    require(ENANO_ROOT . '/plugins/admin/PageEditor.php');
-    require(ENANO_ROOT . '/plugins/admin/PageGroups.php');
-    require(ENANO_ROOT . '/plugins/admin/GroupManager.php');
-    require(ENANO_ROOT . '/plugins/admin/SecurityLog.php');
-    require(ENANO_ROOT . '/plugins/admin/UserManager.php');
-    require(ENANO_ROOT . '/plugins/admin/UserRanks.php');
-    require(ENANO_ROOT . '/plugins/admin/LangManager.php');
-    require(ENANO_ROOT . '/plugins/admin/ThemeManager.php');
-    require(ENANO_ROOT . '/plugins/admin/PluginManager.php');
-    require(ENANO_ROOT . '/plugins/admin/CacheManager.php');
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	// Admin pages that were too enormous to be in this file were split off into the plugins/admin/ directory in 1.0.1.
+	// Only load these files if we're looking to load the admin panel
+	list($pid, $ns) = RenderMan::strToPageID($paths->get_pageid_from_url());
+	if ( $ns == 'Admin' || ( $pid == 'Administration' && $ns == 'Special' ) )
+	{
+		require(ENANO_ROOT . '/plugins/admin/Home.php');
+		require(ENANO_ROOT . '/plugins/admin/PageManager.php');
+		require(ENANO_ROOT . '/plugins/admin/PageEditor.php');
+		require(ENANO_ROOT . '/plugins/admin/PageGroups.php');
+		require(ENANO_ROOT . '/plugins/admin/GroupManager.php');
+		require(ENANO_ROOT . '/plugins/admin/SecurityLog.php');
+		require(ENANO_ROOT . '/plugins/admin/UserManager.php');
+		require(ENANO_ROOT . '/plugins/admin/UserRanks.php');
+		require(ENANO_ROOT . '/plugins/admin/LangManager.php');
+		require(ENANO_ROOT . '/plugins/admin/ThemeManager.php');
+		require(ENANO_ROOT . '/plugins/admin/PluginManager.php');
+		require(ENANO_ROOT . '/plugins/admin/CacheManager.php');
+	}
 }
 
 // For convenience and nothing more.
 function acp_start_form()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', ( isset($_GET['sqldbg']) ? 'sqldbg&' : '' ) . ( isset($_GET['nocompress']) ? 'nocompress&' : '' ) . 'module='.$paths->cpage['module']).'" method="post" enctype="multipart/form-data">';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', ( isset($_GET['sqldbg']) ? 'sqldbg&' : '' ) . ( isset($_GET['nocompress']) ? 'nocompress&' : '' ) . 'module='.$paths->cpage['module']).'" method="post" enctype="multipart/form-data">';
 }
 
 // function names are IMPORTANT!!! The name pattern is: page_<namespace ID>_<page URLname, without namespace>
 
 function page_Admin_GeneralConfig()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $cache;
-  
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  // FIXME: is this a bad place for this? I couldn't think of anything much better. Not helped by the fact that I hate misc scripts.
-  if ( isset($_POST['act']) && $_POST['act'] === 'gzip_check' )
-  {
-    global $is_https;
-    header('Content-type: application/json');
-    require(ENANO_ROOT . '/includes/http.php');
-    try
-    {
-      if ( !isset($_SERVER['SERVER_ADDR']) )
-        throw new Exception('No SERVER_ADDR support - can\'t test server environment');
-      
-      $server_addr = $_SERVER['SERVER_ADDR'];
-      // cheap ipv6 test
-      if ( strstr($server_addr, ":") )
-        $server_addr = "[$server_addr]";
-      
-      $req = new Request_HTTP($server_addr, makeUrlNS('System', 'GzipTest', 'disable_builtin_gzip'), 'GET', intval($_SERVER['SERVER_PORT']), $is_https);
-      $req->add_header('Accept-Encoding', 'gzip,deflate');
-      $headers = $req->get_response_headers_array();
-      $send = array(
-          'server_does_it' => ( isset($headers['Content-encoding']) && in_array($headers['Content-encoding'], array('gzip', 'deflate')) ),
-          'php_supports_gzip' => function_exists('gzdeflate')
-        );
-    }
-    catch ( Exception $e )
-    {
-      $send = array(
-        'mode' => 'error',
-        'error' => "HTTP request exception: <pre>$e</pre>"
-        );
-    }
-    echo enano_json_encode($send);
-    return;
-  }
-  
-  if(isset($_POST['submit']) && !defined('ENANO_DEMO_MODE') )
-  {
-    
-    // Global site options
-    setConfig('site_name', $_POST['site_name']);
-    setConfig('site_desc', $_POST['site_desc']);
-    setConfig('main_page', sanitize_page_id($_POST['main_page']));
-    setConfig('copyright_notice', $_POST['copyright']);
-    setConfig('contact_email', $_POST['contact_email']);
-    
-    setConfig('main_page_alt_enable', ( isset($_POST['main_page_alt_enable']) && $_POST['main_page_alt_enable'] === '1' ? '1' : '0' ));
-    if ( !empty($_POST['main_page_alt']) )
-    {
-      setConfig('main_page_alt', sanitize_page_id($_POST['main_page_alt']));
-    }
-    
-    // Wiki mode
-    if(isset($_POST['wikimode']))                setConfig('wiki_mode', '1');
-    else                                         setConfig('wiki_mode', '0');
-    if(isset($_POST['wiki_mode_require_login'])) setConfig('wiki_mode_require_login', '1');
-    else                                         setConfig('wiki_mode_require_login', '0');
-    if(isset($_POST['editmsg']))                 setConfig('wiki_edit_notice', '1');
-    else                                         setConfig('wiki_edit_notice', '0');
-    setConfig('wiki_edit_notice_text', $_POST['editmsg_text']);
-    $cache->purge('wiki_edit_notice');
-    if(isset($_POST['guest_edit_require_captcha'])) setConfig('guest_edit_require_captcha', '1');
-    else                                         setConfig('guest_edit_require_captcha', '0');
-    
-    // Stats
-    if(isset($_POST['log_hits']))                setConfig('log_hits', '1');
-    else                                         setConfig('log_hits', '0');
-    
-    // Disablement
-    if(isset($_POST['site_disabled'])) {         setConfig('site_disabled', '1'); setConfig('site_disabled_notice', $_POST['site_disabled_notice']); }
-    else                                         setConfig('site_disabled', '0');
-    
-    // Account activation
-    setConfig('account_activation', $_POST['account_activation']);
-    
-    // W3C compliance buttons
-    if(isset($_POST['w3c-vh32']))     setConfig("w3c_vh32", "1");
-    else                              setConfig("w3c_vh32", "0");
-    if(isset($_POST['w3c-vh40']))     setConfig("w3c_vh40", "1");
-    else                              setConfig("w3c_vh40", "0");
-    if(isset($_POST['w3c-vh401']))    setConfig("w3c_vh401", "1");
-    else                              setConfig("w3c_vh401", "0");
-    if(isset($_POST['w3c-vxhtml10'])) setConfig("w3c_vxhtml10", "1");
-    else                              setConfig("w3c_vxhtml10", "0");
-    if(isset($_POST['w3c-vxhtml11'])) setConfig("w3c_vxhtml11", "1");
-    else                              setConfig("w3c_vxhtml11", "0");
-    if(isset($_POST['w3c-vcss']))     setConfig("w3c_vcss", "1");
-    else                              setConfig("w3c_vcss", "0");
-    
-    // SourceForge.net logo
-    if(isset($_POST['showsf'])) setConfig('sflogo_enabled', '1');
-    else                        setConfig('sflogo_enabled', '0');
-    setConfig('sflogo_groupid', $_POST['sfgroup']);
-    setConfig('sflogo_type', $_POST['sflogo']);
-    
-    // Comment options
-    if(isset($_POST['comment-approval'])) setConfig('approve_comments', '1');
-    else                                  setConfig('approve_comments', '0');
-    if(isset($_POST['enable-comments']))  setConfig('enable_comments', '1');
-    else                                  setConfig('enable_comments', '0');
-    setConfig('comments_need_login', $_POST['comments_need_login']);
-    if ( in_array($_POST['comment_spam_policy'], array('moderate', 'reject', 'accept')) )
-    {
-      setConfig('comment_spam_policy', $_POST['comment_spam_policy']);
-    }
-    
-    // Powered by link
-    if ( isset($_POST['enano_powered_link']) ) setConfig('powered_btn', '1');
-    else                                       setConfig('powered_btn', '0');    
-    
-    if(isset($_POST['dbdbutton']))        setConfig('dbd_button', '1');
-    else                                  setConfig('dbd_button', '0');
-    
-    if($_POST['emailmethod'] == 'phpmail') setConfig('smtp_enabled', '0');
-    else                                   setConfig('smtp_enabled', '1');
-    
-    setConfig('smtp_server', $_POST['smtp_host']);
-    setConfig('smtp_user', $_POST['smtp_user']);
-    if($_POST['smtp_pass'] != 'XXXXXXXXXXXX') setConfig('smtp_password', $_POST['smtp_pass']);
-    
-    // Password strength
-    if ( isset($_POST['pw_strength_enable']) ) setConfig('pw_strength_enable', '1');
-    else                                       setConfig('pw_strength_enable', '0');
-    
-    $strength = intval($_POST['pw_strength_minimum']);
-    if ( $strength >= -10 && $strength <= 30 )
-    {
-      $strength = strval($strength);
-      setConfig('pw_strength_minimum', $strength);
-    }
-    
-    // Default theme
-    $default_theme = ( isset($template->named_theme_list[@$_POST['default_theme']]) ) ? $_POST['default_theme'] : $template->theme_list[0]['theme_id'];
-    setConfig('theme_default', $default_theme);
-    
-    // Breadcrumb mode
-    if ( in_array($_POST['breadcrumb_mode'], array('subpages', 'always', 'never')) )
-    {
-      setConfig('breadcrumb_mode', $_POST['breadcrumb_mode']);
-    }
-    
-    // CDN path
-    if ( preg_match('/^http:\/\//', $_POST['cdn_path']) || $_POST['cdn_path'] === '' )
-    {
-      // trim off a trailing slash
-      setConfig('cdn_path', preg_replace('#/$#', '', $_POST['cdn_path']));
-    }
-    
-    setConfig('register_tou', RenderMan::preprocess_text($_POST['register_tou'], true, false));
-    
-    // Account lockout policy
-    if ( ctype_digit($_POST['lockout_threshold']) )
-      setConfig('lockout_threshold', $_POST['lockout_threshold']);
-    
-    if ( ctype_digit($_POST['lockout_duration']) )
-      setConfig('lockout_duration', $_POST['lockout_duration']);
-    
-    if ( in_array($_POST['lockout_policy'], array('disable', 'captcha', 'lockout')) )
-      setConfig('lockout_policy', $_POST['lockout_policy']);
-    
-    // Session time
-    foreach ( array('session_short_time', 'session_remember_time') as $k )
-    {
-      if ( strval(intval($_POST[$k])) === $_POST[$k] && intval($_POST[$k]) >= 0 )
-      {
-        setConfig($k, $_POST[$k]);
-      }
-    }
-    
-    // Avatar settings
-    setConfig('avatar_enable', ( isset($_POST['avatar_enable']) ? '1' : '0' ));
-    // for these next three values, set the config value if it's a valid integer; this is
-    // done by using strval(intval($foo)) === $foo, which flattens $foo to an integer and
-    // then converts it back to a string. This effectively verifies that var $foo is both
-    // set and that it's a valid string representing an integer.
-    setConfig('avatar_max_size', ( strval(intval($_POST['avatar_max_size'])) === $_POST['avatar_max_size'] ? $_POST['avatar_max_size'] : '10240' ));
-    setConfig('avatar_max_width', ( strval(intval($_POST['avatar_max_width'])) === $_POST['avatar_max_width'] ? $_POST['avatar_max_width'] : '96' ));
-    setConfig('avatar_max_height', ( strval(intval($_POST['avatar_max_height'])) === $_POST['avatar_max_height'] ? $_POST['avatar_max_height'] : '96' ));
-    setConfig('avatar_enable_anim', ( isset($_POST['avatar_enable_anim']) ? '1' : '0' ));
-    setConfig('avatar_upload_file', ( isset($_POST['avatar_upload_file']) ? '1' : '0' ));
-    setConfig('avatar_upload_http', ( isset($_POST['avatar_upload_http']) ? '1' : '0' ));
-    setConfig('avatar_upload_gravatar', ( isset($_POST['avatar_upload_gravatar']) ? '1' : '0' ));
-    if ( in_array($_POST['gravatar_rating'], array('g', 'pg', 'r', 'x')) )
-    {
-      setConfig('gravatar_rating', $_POST['gravatar_rating']);
-    }
-    
-    setConfig('avatar_directory', 'files/avatars');
-    
-    setConfig('userpage_grant_acl', ( isset($_POST['userpage_grant_acl']) ? '1' : '0' ));
-    setConfig('gzip_output', ( isset($_POST['gzip_output']) ? '1' : '0' ));
-    
-    // Allow plugins to save their changes
-    $code = $plugins->setHook('acp_general_save');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-    
-    echo '<div class="info-box">' . $lang->get('acpgc_msg_save_success') . '</div><br />';
-    
-  }
-  else if ( isset($_POST['submit']) && defined('ENANO_DEMO_MODE') )
-  {
-    echo '<div class="error-box">Saving the general site configuration is blocked in the administration demo.</div>';
-  }
-  echo('<form name="main" action="'.htmlspecialchars(makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module'])).'" method="post" onsubmit="if(!submitAuthorized) return false;">');
-  ?>
-  <div class="tblholder">
-    <table border="0" width="100%" cellspacing="1" cellpadding="4">
-      
-    <!-- Global options -->
-    
-      <tr><th colspan="2"><?php echo $lang->get('acpgc_heading_main'); ?></th></tr>
-      
-      <tr>
-        <th colspan="2" class="subhead"><?php echo $lang->get('acpgc_heading_submain'); ?></th>
-      </tr>
-      
-      <!-- site name -->
-      
-      <tr>
-        <td class="row1" style="width: 50%;">
-          <?php echo $lang->get('acpgc_field_site_name'); ?>
-        </td>
-        <td class="row1" style="width: 50%;">
-          <input type="text" name="site_name" size="30" value="<?php echo htmlspecialchars(getConfig('site_name')); ?>" />
-        </td>
-      </tr>
-      
-      <!-- site tagline -->
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_site_desc'); ?>
-        </td>
-        <td class="row2">
-          <input type="text" name="site_desc" size="30" value="<?php echo htmlspecialchars(getConfig('site_desc')); ?>" />
-        </td>
-      </tr>
-      
-      <!-- main page -->
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_main_page'); ?></td>
-        <td class="row1">
-          <?php echo $template->pagename_field('main_page', sanitize_page_id(getConfig('main_page', 'Main_Page'))); ?><br />
-            <label><input type="radio" name="main_page_alt_enable" value="0" onclick="$('#main_page_alt_tr').hide();" <?php if ( getConfig('main_page_alt_enable', '0') == '0' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_main_page_option_same'); ?></label><br />
-            <label><input type="radio" name="main_page_alt_enable" value="1" onclick="$('#main_page_alt_tr').show();" <?php if ( getConfig('main_page_alt_enable', '0') == '1' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_main_page_option_members'); ?></label>
-        </td>
-      </tr>
-      <tr id="main_page_alt_tr"<?php if ( getConfig('main_page_alt_enable', '0') == '0' ) echo ' style="display: none;"'; ?>>
-        <td class="row3">
-          <?php echo $lang->get('acpgc_field_main_page_members'); ?>
-        </td>
-        <td class="row3">
-          <?php echo $template->pagename_field('main_page_alt', sanitize_page_id(getConfig('main_page_alt', /* default alt to current main page */ getConfig('main_page', 'Main_Page')))); ?>
-        </td>
-      </tr>
-      
-      <!-- copyright notice -->
-      <tr>
-        <td class="row2">
-            <?php echo $lang->get('acpgc_field_copyright'); ?>
-        </td>
-        <td class="row2">
-          <input type="text" name="copyright" size="30" value="<?php echo htmlspecialchars(getConfig('copyright_notice')); ?>" />
-        </td>
-      </tr>
-      <tr>
-        <td class="row1" colspan="2">
-          <?php echo $lang->get('acpgc_field_copyright_hint'); ?>
-        </td>
-      </tr>
-      
-      <!-- contact e-mail -->
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_contactemail'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_contactemail_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <input name="contact_email" type="text" size="40" value="<?php echo htmlspecialchars(getConfig('contact_email')); ?>" />
-        </td>
-      </tr>
-      
-    <!-- Wiki mode -->
-      
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_wikimode'); ?></th></tr>
-      
-      <tr>
-        <td class="row3" rowspan="2">
-          <?php echo $lang->get('acpgc_field_wikimode_intro'); ?><br /><br />
-          <?php echo $lang->get('acpgc_field_wikimode_info_sanitize'); ?><br /><br />
-          <?php echo $lang->get('acpgc_field_wikimode_info_history'); ?>
-        </td>
-        <td class="row1">
-          <input type="checkbox" name="wikimode" id="wikimode" <?php if(getConfig('wiki_mode')=='1') echo('CHECKED '); ?> /><label for="wikimode"><?php echo $lang->get('acpgc_field_wikimode'); ?></label>
-        </td>
-      </tr>
-      
-      <tr><td class="row2"><label><input type="checkbox" name="wiki_mode_require_login"<?php if(getConfig('wiki_mode_require_login')=='1') echo('CHECKED '); ?>/> Only for logged in users</label></td></tr>
-      
-      <tr>
-        <td class="row3" rowspan="2">
-          <b><?php echo $lang->get('acpgc_field_editnotice_title'); ?></b><br />
-          <?php echo $lang->get('acpgc_field_editnotice_info'); ?>
-        </td>
-        <td class="row1">
-          <input onclick="if(this.checked) document.getElementById('editmsg_text').style.display='block'; else document.getElementById('editmsg_text').style.display='none';" type="checkbox" name="editmsg" id="editmsg" <?php if(getConfig('wiki_edit_notice', '0')=='1') echo('CHECKED '); ?>/>
-          <label for="editmsg"><?php echo $lang->get('acpgc_field_editnotice'); ?></label>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row2">
-          <textarea <?php if(getConfig('wiki_edit_notice', '0')!='1') echo('style="display:none" '); ?>rows="5" cols="30" name="editmsg_text" id="editmsg_text"><?php echo getConfig('wiki_edit_notice_text'); ?></textarea>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <b><?php echo $lang->get('acpgc_field_edit_require_captcha_title'); ?></b><br />
-          <?php echo $lang->get('acpgc_field_edit_require_captcha_hint'); ?>
-        </td>
-        <td class="row1">
-          <label>
-            <input type="checkbox" name="guest_edit_require_captcha" <?php if ( getConfig('guest_edit_require_captcha') == '1' ) echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_edit_require_captcha'); ?>
-          </label>
-        </td>
-      </tr>
-      
-    <!-- Site statistics -->
-    
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_stats'); ?></th></tr>
-      
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_stats_intro'); ?><br /><br />
-          <?php echo $lang->get('acpgc_stats_hint_privacy'); ?>
-        </td>
-        <td class="row1">
-          <label>
-            <input type="checkbox" name="log_hits" <?php if(getConfig('log_hits') == '1') echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_stats_enable'); ?>
-          </label><br />
-          <small><?php echo $lang->get('acpgc_field_stats_hint'); ?></small>
-        </td>
-      </tr>
-      
-    <!-- Comment options -->
-      
-      <tr>
-        <th class="subhead" colspan="2">
-          <?php echo $lang->get('acpgc_heading_comments'); ?>
-        </th>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <label for="enable-comments">
-            <b><?php echo $lang->get('acpgc_field_enable_comments'); ?></b>
-          </label>
-        </td>
-        <td class="row1">
-          <input name="enable-comments"  id="enable-comments"  type="checkbox" <?php if(getConfig('enable_comments', '1')=='1')  echo('CHECKED '); ?>/>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row2">
-          <label for="comment-approval">
-            <?php echo $lang->get('acpgc_field_approve_comments'); ?>
-          </label>
-        </td>
-        <td class="row2">
-          <input name="comment-approval" id="comment-approval" type="checkbox" <?php if(getConfig('approve_comments', '0')=='1') echo('CHECKED '); ?>/>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_comment_allow_guests'); ?>
-        </td>
-        <td class="row1">
-          <label>
-            <input name="comments_need_login" type="radio" value="0" <?php if(getConfig('comments_need_login')=='0') echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_comment_allow_guests_yes'); ?>
-          </label>
-          <label>
-            <input name="comments_need_login" type="radio" value="1" <?php if(getConfig('comments_need_login')=='1') echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_comment_allow_guests_captcha'); ?>
-          </label>
-          <label>
-            <input name="comments_need_login" type="radio" value="2" <?php if(getConfig('comments_need_login')=='2') echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_comment_allow_guests_no'); ?>
-          </label>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_comment_spam_policy'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_comment_spam_policy_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <label>
-            <input name="comment_spam_policy" type="radio" value="moderate" <?php if ( getConfig('comment_spam_policy', 'moderate') == 'moderate' ) echo 'checked="checked"'; ?>/>
-            <?php echo $lang->get('acpgc_field_comment_spam_policy_moderate'); ?>
-          </label><br /> 
-          <label>
-            <input name="comment_spam_policy" type="radio" value="reject" <?php if ( getConfig('comment_spam_policy', 'moderate') == 'reject' ) echo 'checked="checked"'; ?>/>
-            <?php echo $lang->get('acpgc_field_comment_spam_policy_reject'); ?>
-          </label><br />
-          <label>
-            <input name="comment_spam_policy" type="radio" value="accept" <?php if ( getConfig('comment_spam_policy', 'moderate') == 'accept' ) echo 'checked="checked"'; ?>/>
-            <?php echo $lang->get('acpgc_field_comment_spam_policy_accept'); ?>
-          </label>
-        </td>
-      </tr>
-            
-    <!-- Site disablement -->
-    
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_disablesite'); ?></th></tr>
-      
-      <tr>
-        <td class="row3" rowspan="2">
-          <?php echo $lang->get('acpgc_field_disablesite_hint'); ?>
-        </td>
-        <td class="row1">
-          <label>
-            <input onclick="if(this.checked) document.getElementById('site_disabled_notice').style.display='block'; else document.getElementById('site_disabled_notice').style.display='none';" type="checkbox" name="site_disabled" <?php if(getConfig('site_disabled') == '1') echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_disablesite'); ?>
-          </label>
-        </td>
-      </tr>
-      <tr>
-        <td class="row2">
-          <div id="site_disabled_notice"<?php if(getConfig('site_disabled')!='1') echo(' style="display:none"'); ?>>
-            <?php echo $lang->get('acpgc_field_disablesite_message'); ?><br />
-            <textarea name="site_disabled_notice" rows="7" cols="30"><?php echo getConfig('site_disabled_notice'); ?></textarea>
-          </div>
-        </td>
-      </tr>
-      
-    <!-- Default theme -->
-    
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_default_theme'); ?></th></tr>
-      
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_default_theme'); ?>
-        </td>
-        <td class="row2">
-          <select name="default_theme">
-          <?php
-              foreach ( $template->named_theme_list as $theme_id => $theme_data )
-              {
-                if ( !isset($theme_data['theme_name']) )
-                  // probably a system theme
-                  continue;
-                  
-                $theme_name = htmlspecialchars($theme_data['theme_name']);
-                $selected = ( $theme_id === getConfig('theme_default') ) ? ' selected="selected"' : '';
-                echo "  <option value=\"$theme_id\"$selected>$theme_name</option>\n          ";
-              }
-            ?>
-          </select>
-        </td>
-      </tr>
-      
-    <!-- Breadcrumbs -->
-    
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_breadcrumb_mode'); ?>
-        </td>
-        <td class="row1">
-          <select name="breadcrumb_mode">
-          <?php
-            foreach ( array('subpages', 'always', 'never') as $mode )
-            {
-              $str = $lang->get("acpgc_field_breadcrumb_mode_$mode");
-              $sel = ( getConfig('breadcrumb_mode') == $mode ) ? ' selected="selected"' : '';
-              echo "  <option value=\"$mode\"$sel>$str</option>\n          ";
-            }
-          ?>
-          </select>
-        </td>
-      </tr>
-    
-    <!-- CDN settings -->
-    
-      <tr>
-        <td class="row2">
-          <p>
-            <?php echo $lang->get('acpgc_field_cdn_path'); ?><br />
-            <small><?php echo $lang->get('acpgc_field_cdn_path_hint'); ?></small>
-          </p>
-          <p>
-            <small><?php echo $lang->get('acpgc_field_cdn_path_example'); ?></small>
-          </p>
-        </td>
-        <td class="row2">
-          <input type="text" name="cdn_path" value="<?php echo htmlspecialchars(getConfig('cdn_path', '')); ?>" style="width: 98%;" />
-        </td>
-      </tr>
-      
-    <!-- Gzip -->
-    
-      <tr>
-        <td class="row1">
-          <b><?php echo $lang->get('acpgc_field_gzip'); ?></b><br />
-          <small><?php echo $lang->get('acpgc_field_gzip_hint'); ?></small><br />
-          <br />
-          <a href="#" onclick="ajaxGzipCheck(); return false;"><?php echo $lang->get('acpgc_field_gzip_btn_check'); ?></a>
-        </td>
-        <td class="row1">
-          <div id="gzip_check_result"></div>
-          <label>
-            <input type="checkbox" name="gzip_output" <?php if ( getConfig('gzip_output', false) == 1 ) echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_gzip_lbl'); ?>
-          </label>
-        </td>
-      </tr>
-      
-    <!-- Allow plugins to add code -->
-      <?php
-      $code = $plugins->setHook('acp_general_basic');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      ?>
-      
-    </table>
-    </div>
-        
-    <div class="tblholder">
-    <table border="0" width="100%" cellspacing="1" cellpadding="4">
-    
-    <tr>
-      <th colspan="2"><?php echo $lang->get('acpgc_heading_users'); ?></th>
-    </tr>
-    
-    <!-- Account activation -->
-      
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_activate'); ?></th></tr>
-      
-      <tr>
-        <td class="row3" colspan="2">
-          <?php echo $lang->get('acpgc_activate_intro_line1'); ?><br /><br />
-          <?php echo $lang->get('acpgc_activate_intro_line2'); ?><br /><br />
-          <b><?php echo $lang->get('acpgc_activate_intro_sfnet_warning'); ?></b>
-        </td>
-      </tr>
-      
-      <tr>
-      <td class="row1" style="width: 50%;"><?php echo $lang->get('acpgc_field_activate'); ?></td><td class="row1">
-          <?php
-          echo '<label><input'; if(getConfig('account_activation') == 'disable') echo ' checked="checked"'; echo ' type="radio" name="account_activation" value="disable" /> ' . $lang->get('acpgc_field_activate_disable') . '</label><br />';
-          echo '<label><input'; if(getConfig('account_activation') != 'user' && getConfig('account_activation') != 'admin' && getConfig('account_activation') != 'disable') echo ' checked="checked"'; echo ' type="radio" name="account_activation" value="none" /> ' . $lang->get('acpgc_field_activate_none') . '</label>';
-          echo '<label><input'; if(getConfig('account_activation') == 'user') echo ' checked="checked"'; echo ' type="radio" name="account_activation" value="user" /> ' . $lang->get('acpgc_field_activate_user') . '</label>';
-          echo '<label><input'; if(getConfig('account_activation') == 'admin') echo ' checked="checked"'; echo ' type="radio" name="account_activation" value="admin" /> ' . $lang->get('acpgc_field_activate_admin') . '</label>';
-          ?>
-        </td>
-      </tr>
-      
-    <!-- Terms of Use -->
-    
-      <tr>
-        <th class="subhead" colspan="2">
-          <?php echo $lang->get('acpgc_heading_tou'); ?>
-        </th>
-      </tr>
-      
-      <tr>
-        <td class="row2">
-          <b><?php echo $lang->get('acpgc_field_tou'); ?></b><br />
-          <small><?php echo $lang->get('acpgc_field_tou_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <?php
-            $terms = getConfig('register_tou');
-            echo $template->tinymce_textarea('register_tou', $terms, 10, 40);
-          ?>
-        </td>
-      </tr>
-      
-    <!-- Account lockout -->
-    
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_lockout'); ?></th></tr>
-      
-      <tr><td class="row3" colspan="2"><?php echo $lang->get('acpgc_lockout_intro'); ?></td></tr>
-      
-      <tr>
-        <td class="row2"><?php echo $lang->get('acpgc_field_lockout_threshold'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_lockout_threshold_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <input type="text" name="lockout_threshold" value="<?php echo ( $_ = getConfig('lockout_threshold') ) ? $_ : '5' ?>" />
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row1"><?php echo $lang->get('acpgc_field_lockout_duration'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_lockout_duration_hint'); ?></small>
-        </td>
-        <td class="row1">
-          <input type="text" name="lockout_duration" value="<?php echo ( $_ = getConfig('lockout_duration') ) ? $_ : '15' ?>" />
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row2"><?php echo $lang->get('acpgc_field_lockout_policy'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_lockout_policy_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <label><input type="radio" name="lockout_policy" value="disable" <?php if ( getConfig('lockout_policy') == 'disable' ) echo 'checked="checked"'; ?> /> <?php echo $lang->get('acpgc_field_lockout_policy_nothing'); ?></label><br />
-          <label><input type="radio" name="lockout_policy" value="captcha" <?php if ( getConfig('lockout_policy') == 'captcha' ) echo 'checked="checked"'; ?> /> <?php echo $lang->get('acpgc_field_lockout_policy_captcha'); ?></label><br />
-          <label><input type="radio" name="lockout_policy" value="lockout" <?php if ( getConfig('lockout_policy') == 'lockout' || !getConfig('lockout_policy') ) echo 'checked="checked"'; ?> /> <?php echo $lang->get('acpgc_field_lockout_policy_lockout'); ?></label>
-        </td>
-      </tr>
-      
-    <!-- Password strength -->
-      
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_passstrength'); ?></th></tr>
-      
-      <tr>
-        <td class="row2">
-          <b><?php echo $lang->get('acpgc_field_passstrength_title'); ?></b><br />
-          <small><?php echo $lang->get('acpgc_field_passstrength_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <label><input type="checkbox" name="pw_strength_enable" <?php if ( getConfig('pw_strength_enable') == '1' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_passstrength'); ?></label>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <b><?php echo $lang->get('acpgc_field_passminimum_title'); ?></b><br />
-          <small><?php echo $lang->get('acpgc_field_passminimum_hint'); ?></small>
-        </td>
-        <td class="row1">
-          <input type="text" name="pw_strength_minimum" value="<?php echo strval(getConfig('pw_strength_minimum', -10)); ?>" />
-        </td>
-      </tr>
-      
-    <!-- E-mail options -->
-    
-      <tr>
-        <th class="subhead" colspan="2">
-          <?php echo $lang->get('acpgc_heading_email'); ?>
-        </th>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_email_method'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_email_method_hint'); ?></small>
-        </td>
-        <td class="row1">
-          <label>
-            <input <?php if(getConfig('smtp_enabled') != '1') echo 'checked="checked"'; ?> type="radio" name="emailmethod" value="phpmail" />
-            <?php echo $lang->get('acpgc_field_email_method_builtin'); ?>
-          </label>
-          
-          <br />
-          
-          <label>
-            <input <?php if(getConfig('smtp_enabled') == '1') echo 'checked="checked"'; ?> type="radio" name="emailmethod" value="smtp" />
-            <?php echo $lang->get('acpgc_field_email_method_smtp'); ?>
-          </label>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_email_smtp_hostname'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_email_smtp_hostname_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <input value="<?php echo getConfig('smtp_server'); ?>" name="smtp_host" type="text" size="30" />
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_email_smtp_auth'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_email_smtp_hostname_hint'); ?></small>
-        </td>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_email_smtp_username'); ?> <input value="<?php echo getConfig('smtp_user'); ?>" name="smtp_user" type="text" size="30" /><br />
-          <?php echo $lang->get('acpgc_field_email_smtp_password'); ?> <input value="<?php if(getConfig('smtp_password') != false) echo 'XXXXXXXXXXXX'; ?>" name="smtp_pass" type="password" size="30" />
-        </td>
-      </tr>
-      
-    <!-- Session length -->
-    
-      <tr>
-        <th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_sessions'); ?></th>
-      </tr>
-      
-      <tr>
-        <td class="row3" colspan="2"><?php echo $lang->get('acpgc_hint_sessions_noelev'); ?></td>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_short_time'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_short_time_hint'); ?></small>
-        </td>
-        <td class="row1">
-          <input type="text" name="session_short_time" value="<?php echo getConfig('session_short_time', '720'); ?>" size="4" />
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_long_time'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_long_time_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <input type="text" name="session_remember_time" value="<?php echo getConfig('session_remember_time', '30'); ?>" size="4" />
-        </td>
-      </tr>
-        
-    <!-- Avatar support -->
-    
-      <tr>
-        <th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_avatars'); ?></th>
-      </tr>
-      
-      <tr>
-        <td class="row3" colspan="2">
-          <?php echo $lang->get('acpgc_avatars_intro'); ?>
-        </th>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_avatar_enable'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_avatar_enable_hint'); ?></small>
-        </td>
-        <td class="row1">
-          <label><input type="checkbox" name="avatar_enable" <?php if ( getConfig('avatar_enable') == '1' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_avatar_enable_label'); ?></label>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_avatar_max_filesize'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_avatar_max_filesize_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <input type="text" name="avatar_max_size" size="7" <?php if ( ($x = getConfig('avatar_max_size')) !== false ) echo "value=\"$x\" "; else echo "value=\"10240\" "; ?>/> <?php echo $lang->get('etc_unit_bytes'); ?>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_avatar_max_dimensions'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_avatar_max_dimensions_hint'); ?></small>
-        </td>
-        <td class="row1">
-          <input type="text" name="avatar_max_width" size="7" <?php if ( $x = getConfig('avatar_max_width') ) echo "value=\"$x\" "; else echo "value=\"150\" "; ?>/> &#215;
-          <input type="text" name="avatar_max_height" size="7" <?php if ( $x = getConfig('avatar_max_height') ) echo "value=\"$x\" "; else echo "value=\"150\" "; ?>/> <?php echo $lang->get('etc_unit_pixels'); ?>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row2">
-          <?php echo $lang->get('acpgc_field_avatar_allow_anim_title'); ?><br />
-          <small><?php echo $lang->get('acpgc_field_avatar_allow_anim_hint'); ?></small>
-        </td>
-        <td class="row2">
-          <label><input type="checkbox" name="avatar_enable_anim" <?php if ( getConfig('avatar_enable_anim') == '1' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_avatar_allow_anim'); ?></label>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpgc_field_avatar_upload_methods'); ?><br />
-          <small></small>
-        </td>
-        <td class="row1">
-          <label>
-            <input type="checkbox" name="avatar_upload_file" <?php if ( getConfig('avatar_upload_file', 1) == 1 ) echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_avatar_upload_file'); ?>
-          </label>
-          
-          <br />
-          
-          <label>
-            <input type="checkbox" name="avatar_upload_http" <?php if ( getConfig('avatar_upload_http', 1) == 1 ) echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_avatar_upload_http'); ?>
-          </label>
-          
-          <br />
-          
-          <label>
-          <input type="checkbox" name="avatar_upload_gravatar" <?php if ( getConfig('avatar_upload_gravatar', 1) == 1 ) echo 'checked="checked" '; ?>onclick="document.getElementById('acp_gravatar_rating').style.display = ( this.checked ) ? 'block' : 'none';" />
-            <?php echo $lang->get('acpgc_field_avatar_upload_gravatar'); ?>
-          </label>
-          
-          <br />
-          
-          <fieldset id="acp_gravatar_rating" style="margin-top: 10px; <?php if ( getConfig('avatar_upload_gravatar', 1) == 0 ) echo ' display: none;'; ?>">
-          
-            <?php /* The four ratings are g, pg, r, and x - loop through each and output a localized string and a radiobutton */ ?>
-            <legend><?php echo $lang->get('acpgc_field_avatar_gravatar_rating'); ?></legend>
-            
-            <?php foreach ( array('g', 'pg', 'r', 'x') as $rating ): ?>
-            
-            <label>
-            
-              <input type="radio" name="gravatar_rating" value="<?php echo $rating; ?>"<?php
-                // Check the button if this is the current selection *or* if we're on "G" and the current configuration value is unset
-                if ( getConfig('gravatar_rating', 'g') == $rating )
-                  echo ' checked="checked"';
-                ?> />
-                
-              <?php /* The localized string */ ?>
-              <?php echo $lang->get("acpgc_field_avatar_gravatar_rating_$rating"); ?>
-              
-            </label>
-            
-            <br />
-            
-            <?php endforeach; ?>
-          </fieldset>
-        </td>
-      </tr>
-      
-    <!-- Misc. options -->
-    
-      <tr>
-        <th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_usermisc'); ?></th>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <b><?php echo $lang->get('acpgc_field_userpage_acl_title'); ?></b><br />
-          <small>
-            <?php echo $lang->get('acpgc_field_userpage_acl_hint'); ?>
-          </small>
-        </td>
-        <td class="row1">
-          <label>
-            <input type="checkbox" name="userpage_grant_acl" <?php if ( getConfig('userpage_grant_acl', '1') == '1' ) echo 'checked="checked" '; ?>/>
-            <?php echo $lang->get('acpgc_field_userpage_acl'); ?>
-          </label>
-        </td>
-      </tr>
-      
-    <!-- Allow plugins to add code -->
-      <?php
-      $code = $plugins->setHook('acp_general_users');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      ?>
-        
-    </table>
-    </div>
-    
-    <div class="tblholder">
-    <table border="0" width="100%" cellspacing="1" cellpadding="4">
-    
-    <tr>
-      <th colspan="2"><?php echo $lang->get('acpgc_heading_sidebar'); ?></th>
-    </tr>
-    
-    <!-- enanocms.org link -->
-    
-    <tr>
-      <th colspan="2" class="subhead"><?php echo $lang->get('acpgc_heading_promoteenano'); ?></th>
-    </tr>                      
-    <tr>
-      <td class="row3" style="width: 50%;">
-        <b><?php echo $lang->get('acpgc_field_enano_link_title'); ?></b><br />
-        <small><?php echo $lang->get('acpgc_field_enano_link_hint'); ?></small>
-      </td>
-      <td class="row1">
-        <label>
-          <input name="enano_powered_link" type="checkbox" <?php if(getConfig('powered_btn', '1') == '1') echo 'checked="checked"'; ?> />&nbsp;&nbsp;<?php echo $lang->get('acpgc_field_enano_link'); ?>
-        </label>
-      </td>
-    </tr>
-      
-    <!-- SourceForge.net logo -->
-      
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_sfnet_logo'); ?></th></tr>
-      
-      <tr>
-        <td colspan="2" class="row3">
-          <?php echo $lang->get('acpgc_sfnet_intro'); ?>
-        </td>
-      </tr>
-      
-      <?php
-      if ( getConfig("sflogo_enabled") == '1' )
-        $c='checked="checked" ';
-      else
-        $c='';
-        
-      if ( getConfig("sflogo_groupid") )
-        $g = getConfig("sflogo_groupid");
-      else
-        $g = '';
-        
-      if ( getConfig("sflogo_type") )
-        $t = getConfig("sflogo_type");
-      else
-        $t = '1';
-      ?>
-      
-      <tr>
-        <td class="row1"><?php echo $lang->get('acpgc_field_sfnet_display'); ?></td>
-        <td class="row1"><input type=checkbox name="showsf" id="showsf" <?php echo $c; ?> /></td>
-      </tr>
-      
-      <tr>
-        <td class="row2"><?php echo $lang->get('acpgc_field_sfnet_group_id'); ?></td>
-        <td class="row2"><input value="<?php echo $g; ?>" type=text size=15 name=sfgroup /></td>
-      </tr>
-      
-      <tr>
-        <td class="row1"><?php echo $lang->get('acpgc_field_sfnet_logo_style'); ?></td>
-        <td class="row1">
-          <select name="sflogo">
-            <option <?php if($t=='1') echo('selected="selected" '); ?>value=1><?php echo $lang->get('acpgc_field_sfnet_logo_style_1'); ?></option>
-            <option <?php if($t=='2') echo('selected="selected" '); ?>value=2><?php echo $lang->get('acpgc_field_sfnet_logo_style_2'); ?></option>
-            <option <?php if($t=='3') echo('selected="selected" '); ?>value=3><?php echo $lang->get('acpgc_field_sfnet_logo_style_3'); ?></option>
-            <option <?php if($t=='4') echo('selected="selected" '); ?>value=4><?php echo $lang->get('acpgc_field_sfnet_logo_style_4'); ?></option>
-            <option <?php if($t=='5') echo('selected="selected" '); ?>value=5><?php echo $lang->get('acpgc_field_sfnet_logo_style_5'); ?></option>
-            <option <?php if($t=='6') echo('selected="selected" '); ?>value=6><?php echo $lang->get('acpgc_field_sfnet_logo_style_6'); ?></option>
-            <option <?php if($t=='7') echo('selected="selected" '); ?>value=7><?php echo $lang->get('acpgc_field_sfnet_logo_style_7'); ?></option>
-          </select>
-        </td>
-      </tr>
-      
-    <!-- W3C validator buttons -->
-      
-      <tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_w3clogos'); ?></th></tr>
-      <tr><td colspan="2" class="row3"><?php echo $lang->get('acpgc_w3clogos_intro'); ?></th></tr>
-      
-      <tr><td class="row1"><label for="w3c-vh32"><?php     echo $lang->get('acpgc_w3clogos_btn_html32');  ?></label></td><td class="row1"><input type="checkbox" <?php if(getConfig('w3c_vh32')=='1')     echo('checked="checked" '); ?> id="w3c-vh32"     name="w3c-vh32"     /></td></tr>
-      <tr><td class="row2"><label for="w3c-vh40"><?php     echo $lang->get('acpgc_w3clogos_btn_html40');  ?></label></td><td class="row2"><input type="checkbox" <?php if(getConfig('w3c_vh40')=='1')     echo('checked="checked" '); ?> id="w3c-vh40"     name="w3c-vh40"     /></td></tr>
-      <tr><td class="row1"><label for="w3c-vh401"><?php    echo $lang->get('acpgc_w3clogos_btn_html401'); ?></label></td><td class="row1"><input type="checkbox" <?php if(getConfig('w3c_vh401')=='1')    echo('checked="checked" '); ?> id="w3c-vh401"    name="w3c-vh401"    /></td></tr>
-      <tr><td class="row2"><label for="w3c-vxhtml10"><?php echo $lang->get('acpgc_w3clogos_btn_xhtml10'); ?></label></td><td class="row2"><input type="checkbox" <?php if(getConfig('w3c_vxhtml10')=='1') echo('checked="checked" '); ?> id="w3c-vxhtml10" name="w3c-vxhtml10" /></td></tr>
-      <tr><td class="row1"><label for="w3c-vxhtml11"><?php echo $lang->get('acpgc_w3clogos_btn_xhtml11'); ?></label></td><td class="row1"><input type="checkbox" <?php if(getConfig('w3c_vxhtml11')=='1') echo('checked="checked" '); ?> id="w3c-vxhtml11" name="w3c-vxhtml11" /></td></tr>
-      <tr><td class="row2"><label for="w3c-vcss"><?php     echo $lang->get('acpgc_w3clogos_btn_css');     ?></label></td><td class="row2"><input type="checkbox" <?php if(getConfig('w3c_vcss')=='1')     echo('checked="checked" '); ?> id="w3c-vcss"     name="w3c-vcss"     /></td></tr>
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $cache;
+	
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	// FIXME: is this a bad place for this? I couldn't think of anything much better. Not helped by the fact that I hate misc scripts.
+	if ( isset($_POST['act']) && $_POST['act'] === 'gzip_check' )
+	{
+		global $is_https;
+		header('Content-type: application/json');
+		require(ENANO_ROOT . '/includes/http.php');
+		try
+		{
+			if ( !isset($_SERVER['SERVER_ADDR']) )
+				throw new Exception('No SERVER_ADDR support - can\'t test server environment');
+			
+			$server_addr = $_SERVER['SERVER_ADDR'];
+			// cheap ipv6 test
+			if ( strstr($server_addr, ":") )
+				$server_addr = "[$server_addr]";
+			
+			$req = new Request_HTTP($server_addr, makeUrlNS('System', 'GzipTest', 'disable_builtin_gzip'), 'GET', intval($_SERVER['SERVER_PORT']), $is_https);
+			$req->add_header('Accept-Encoding', 'gzip,deflate');
+			$headers = $req->get_response_headers_array();
+			$send = array(
+					'server_does_it' => ( isset($headers['Content-encoding']) && in_array($headers['Content-encoding'], array('gzip', 'deflate')) ),
+					'php_supports_gzip' => function_exists('gzdeflate')
+				);
+		}
+		catch ( Exception $e )
+		{
+			$send = array(
+				'mode' => 'error',
+				'error' => "HTTP request exception: <pre>$e</pre>"
+				);
+		}
+		echo enano_json_encode($send);
+		return;
+	}
+	
+	if(isset($_POST['submit']) && !defined('ENANO_DEMO_MODE') )
+	{
+		
+		// Global site options
+		setConfig('site_name', $_POST['site_name']);
+		setConfig('site_desc', $_POST['site_desc']);
+		setConfig('main_page', sanitize_page_id($_POST['main_page']));
+		setConfig('copyright_notice', $_POST['copyright']);
+		setConfig('contact_email', $_POST['contact_email']);
+		
+		setConfig('main_page_alt_enable', ( isset($_POST['main_page_alt_enable']) && $_POST['main_page_alt_enable'] === '1' ? '1' : '0' ));
+		if ( !empty($_POST['main_page_alt']) )
+		{
+			setConfig('main_page_alt', sanitize_page_id($_POST['main_page_alt']));
+		}
+		
+		// Wiki mode
+		if(isset($_POST['wikimode']))                setConfig('wiki_mode', '1');
+		else                                         setConfig('wiki_mode', '0');
+		if(isset($_POST['wiki_mode_require_login'])) setConfig('wiki_mode_require_login', '1');
+		else                                         setConfig('wiki_mode_require_login', '0');
+		if(isset($_POST['editmsg']))                 setConfig('wiki_edit_notice', '1');
+		else                                         setConfig('wiki_edit_notice', '0');
+		setConfig('wiki_edit_notice_text', $_POST['editmsg_text']);
+		$cache->purge('wiki_edit_notice');
+		if(isset($_POST['guest_edit_require_captcha'])) setConfig('guest_edit_require_captcha', '1');
+		else                                         setConfig('guest_edit_require_captcha', '0');
+		
+		// Stats
+		if(isset($_POST['log_hits']))                setConfig('log_hits', '1');
+		else                                         setConfig('log_hits', '0');
+		
+		// Disablement
+		if(isset($_POST['site_disabled'])) {         setConfig('site_disabled', '1'); setConfig('site_disabled_notice', $_POST['site_disabled_notice']); }
+		else                                         setConfig('site_disabled', '0');
+		
+		// Account activation
+		setConfig('account_activation', $_POST['account_activation']);
+		
+		// W3C compliance buttons
+		if(isset($_POST['w3c-vh32']))     setConfig("w3c_vh32", "1");
+		else                              setConfig("w3c_vh32", "0");
+		if(isset($_POST['w3c-vh40']))     setConfig("w3c_vh40", "1");
+		else                              setConfig("w3c_vh40", "0");
+		if(isset($_POST['w3c-vh401']))    setConfig("w3c_vh401", "1");
+		else                              setConfig("w3c_vh401", "0");
+		if(isset($_POST['w3c-vxhtml10'])) setConfig("w3c_vxhtml10", "1");
+		else                              setConfig("w3c_vxhtml10", "0");
+		if(isset($_POST['w3c-vxhtml11'])) setConfig("w3c_vxhtml11", "1");
+		else                              setConfig("w3c_vxhtml11", "0");
+		if(isset($_POST['w3c-vcss']))     setConfig("w3c_vcss", "1");
+		else                              setConfig("w3c_vcss", "0");
+		
+		// SourceForge.net logo
+		if(isset($_POST['showsf'])) setConfig('sflogo_enabled', '1');
+		else                        setConfig('sflogo_enabled', '0');
+		setConfig('sflogo_groupid', $_POST['sfgroup']);
+		setConfig('sflogo_type', $_POST['sflogo']);
+		
+		// Comment options
+		if(isset($_POST['comment-approval'])) setConfig('approve_comments', '1');
+		else                                  setConfig('approve_comments', '0');
+		if(isset($_POST['enable-comments']))  setConfig('enable_comments', '1');
+		else                                  setConfig('enable_comments', '0');
+		setConfig('comments_need_login', $_POST['comments_need_login']);
+		if ( in_array($_POST['comment_spam_policy'], array('moderate', 'reject', 'accept')) )
+		{
+			setConfig('comment_spam_policy', $_POST['comment_spam_policy']);
+		}
+		
+		// Powered by link
+		if ( isset($_POST['enano_powered_link']) ) setConfig('powered_btn', '1');
+		else                                       setConfig('powered_btn', '0');    
+		
+		if(isset($_POST['dbdbutton']))        setConfig('dbd_button', '1');
+		else                                  setConfig('dbd_button', '0');
+		
+		if($_POST['emailmethod'] == 'phpmail') setConfig('smtp_enabled', '0');
+		else                                   setConfig('smtp_enabled', '1');
+		
+		setConfig('smtp_server', $_POST['smtp_host']);
+		setConfig('smtp_user', $_POST['smtp_user']);
+		if($_POST['smtp_pass'] != 'XXXXXXXXXXXX') setConfig('smtp_password', $_POST['smtp_pass']);
+		
+		// Password strength
+		if ( isset($_POST['pw_strength_enable']) ) setConfig('pw_strength_enable', '1');
+		else                                       setConfig('pw_strength_enable', '0');
+		
+		$strength = intval($_POST['pw_strength_minimum']);
+		if ( $strength >= -10 && $strength <= 30 )
+		{
+			$strength = strval($strength);
+			setConfig('pw_strength_minimum', $strength);
+		}
+		
+		// Default theme
+		$default_theme = ( isset($template->named_theme_list[@$_POST['default_theme']]) ) ? $_POST['default_theme'] : $template->theme_list[0]['theme_id'];
+		setConfig('theme_default', $default_theme);
+		
+		// Breadcrumb mode
+		if ( in_array($_POST['breadcrumb_mode'], array('subpages', 'always', 'never')) )
+		{
+			setConfig('breadcrumb_mode', $_POST['breadcrumb_mode']);
+		}
+		
+		// CDN path
+		if ( preg_match('/^http:\/\//', $_POST['cdn_path']) || $_POST['cdn_path'] === '' )
+		{
+			// trim off a trailing slash
+			setConfig('cdn_path', preg_replace('#/$#', '', $_POST['cdn_path']));
+		}
+		
+		setConfig('register_tou', RenderMan::preprocess_text($_POST['register_tou'], true, false));
+		
+		// Account lockout policy
+		if ( ctype_digit($_POST['lockout_threshold']) )
+			setConfig('lockout_threshold', $_POST['lockout_threshold']);
+		
+		if ( ctype_digit($_POST['lockout_duration']) )
+			setConfig('lockout_duration', $_POST['lockout_duration']);
+		
+		if ( in_array($_POST['lockout_policy'], array('disable', 'captcha', 'lockout')) )
+			setConfig('lockout_policy', $_POST['lockout_policy']);
+		
+		// Session time
+		foreach ( array('session_short_time', 'session_remember_time') as $k )
+		{
+			if ( strval(intval($_POST[$k])) === $_POST[$k] && intval($_POST[$k]) >= 0 )
+			{
+				setConfig($k, $_POST[$k]);
+			}
+		}
+		
+		// Avatar settings
+		setConfig('avatar_enable', ( isset($_POST['avatar_enable']) ? '1' : '0' ));
+		// for these next three values, set the config value if it's a valid integer; this is
+		// done by using strval(intval($foo)) === $foo, which flattens $foo to an integer and
+		// then converts it back to a string. This effectively verifies that var $foo is both
+		// set and that it's a valid string representing an integer.
+		setConfig('avatar_max_size', ( strval(intval($_POST['avatar_max_size'])) === $_POST['avatar_max_size'] ? $_POST['avatar_max_size'] : '10240' ));
+		setConfig('avatar_max_width', ( strval(intval($_POST['avatar_max_width'])) === $_POST['avatar_max_width'] ? $_POST['avatar_max_width'] : '96' ));
+		setConfig('avatar_max_height', ( strval(intval($_POST['avatar_max_height'])) === $_POST['avatar_max_height'] ? $_POST['avatar_max_height'] : '96' ));
+		setConfig('avatar_enable_anim', ( isset($_POST['avatar_enable_anim']) ? '1' : '0' ));
+		setConfig('avatar_upload_file', ( isset($_POST['avatar_upload_file']) ? '1' : '0' ));
+		setConfig('avatar_upload_http', ( isset($_POST['avatar_upload_http']) ? '1' : '0' ));
+		setConfig('avatar_upload_gravatar', ( isset($_POST['avatar_upload_gravatar']) ? '1' : '0' ));
+		if ( in_array($_POST['gravatar_rating'], array('g', 'pg', 'r', 'x')) )
+		{
+			setConfig('gravatar_rating', $_POST['gravatar_rating']);
+		}
+		
+		setConfig('avatar_directory', 'files/avatars');
+		
+		setConfig('userpage_grant_acl', ( isset($_POST['userpage_grant_acl']) ? '1' : '0' ));
+		setConfig('gzip_output', ( isset($_POST['gzip_output']) ? '1' : '0' ));
+		
+		// Allow plugins to save their changes
+		$code = $plugins->setHook('acp_general_save');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+		
+		echo '<div class="info-box">' . $lang->get('acpgc_msg_save_success') . '</div><br />';
+		
+	}
+	else if ( isset($_POST['submit']) && defined('ENANO_DEMO_MODE') )
+	{
+		echo '<div class="error-box">Saving the general site configuration is blocked in the administration demo.</div>';
+	}
+	echo('<form name="main" action="'.htmlspecialchars(makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module'])).'" method="post" onsubmit="if(!submitAuthorized) return false;">');
+	?>
+	<div class="tblholder">
+		<table border="0" width="100%" cellspacing="1" cellpadding="4">
+			
+		<!-- Global options -->
+		
+			<tr><th colspan="2"><?php echo $lang->get('acpgc_heading_main'); ?></th></tr>
+			
+			<tr>
+				<th colspan="2" class="subhead"><?php echo $lang->get('acpgc_heading_submain'); ?></th>
+			</tr>
+			
+			<!-- site name -->
+			
+			<tr>
+				<td class="row1" style="width: 50%;">
+					<?php echo $lang->get('acpgc_field_site_name'); ?>
+				</td>
+				<td class="row1" style="width: 50%;">
+					<input type="text" name="site_name" size="30" value="<?php echo htmlspecialchars(getConfig('site_name')); ?>" />
+				</td>
+			</tr>
+			
+			<!-- site tagline -->
+			<tr>
+				<td class="row2">
+					<?php echo $lang->get('acpgc_field_site_desc'); ?>
+				</td>
+				<td class="row2">
+					<input type="text" name="site_desc" size="30" value="<?php echo htmlspecialchars(getConfig('site_desc')); ?>" />
+				</td>
+			</tr>
+			
+			<!-- main page -->
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_main_page'); ?></td>
+				<td class="row1">
+					<?php echo $template->pagename_field('main_page', sanitize_page_id(getConfig('main_page', 'Main_Page'))); ?><br />
+						<label><input type="radio" name="main_page_alt_enable" value="0" onclick="$('#main_page_alt_tr').hide();" <?php if ( getConfig('main_page_alt_enable', '0') == '0' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_main_page_option_same'); ?></label><br />
+						<label><input type="radio" name="main_page_alt_enable" value="1" onclick="$('#main_page_alt_tr').show();" <?php if ( getConfig('main_page_alt_enable', '0') == '1' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_main_page_option_members'); ?></label>
+				</td>
+			</tr>
+			<tr id="main_page_alt_tr"<?php if ( getConfig('main_page_alt_enable', '0') == '0' ) echo ' style="display: none;"'; ?>>
+				<td class="row3">
+					<?php echo $lang->get('acpgc_field_main_page_members'); ?>
+				</td>
+				<td class="row3">
+					<?php echo $template->pagename_field('main_page_alt', sanitize_page_id(getConfig('main_page_alt', /* default alt to current main page */ getConfig('main_page', 'Main_Page')))); ?>
+				</td>
+			</tr>
+			
+			<!-- copyright notice -->
+			<tr>
+				<td class="row2">
+						<?php echo $lang->get('acpgc_field_copyright'); ?>
+				</td>
+				<td class="row2">
+					<input type="text" name="copyright" size="30" value="<?php echo htmlspecialchars(getConfig('copyright_notice')); ?>" />
+				</td>
+			</tr>
+			<tr>
+				<td class="row1" colspan="2">
+					<?php echo $lang->get('acpgc_field_copyright_hint'); ?>
+				</td>
+			</tr>
+			
+			<!-- contact e-mail -->
+			<tr>
+				<td class="row2">
+					<?php echo $lang->get('acpgc_field_contactemail'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_contactemail_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<input name="contact_email" type="text" size="40" value="<?php echo htmlspecialchars(getConfig('contact_email')); ?>" />
+				</td>
+			</tr>
+			
+		<!-- Wiki mode -->
+			
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_wikimode'); ?></th></tr>
+			
+			<tr>
+				<td class="row3" rowspan="2">
+					<?php echo $lang->get('acpgc_field_wikimode_intro'); ?><br /><br />
+					<?php echo $lang->get('acpgc_field_wikimode_info_sanitize'); ?><br /><br />
+					<?php echo $lang->get('acpgc_field_wikimode_info_history'); ?>
+				</td>
+				<td class="row1">
+					<input type="checkbox" name="wikimode" id="wikimode" <?php if(getConfig('wiki_mode')=='1') echo('CHECKED '); ?> /><label for="wikimode"><?php echo $lang->get('acpgc_field_wikimode'); ?></label>
+				</td>
+			</tr>
+			
+			<tr><td class="row2"><label><input type="checkbox" name="wiki_mode_require_login"<?php if(getConfig('wiki_mode_require_login')=='1') echo('CHECKED '); ?>/> Only for logged in users</label></td></tr>
+			
+			<tr>
+				<td class="row3" rowspan="2">
+					<b><?php echo $lang->get('acpgc_field_editnotice_title'); ?></b><br />
+					<?php echo $lang->get('acpgc_field_editnotice_info'); ?>
+				</td>
+				<td class="row1">
+					<input onclick="if(this.checked) document.getElementById('editmsg_text').style.display='block'; else document.getElementById('editmsg_text').style.display='none';" type="checkbox" name="editmsg" id="editmsg" <?php if(getConfig('wiki_edit_notice', '0')=='1') echo('CHECKED '); ?>/>
+					<label for="editmsg"><?php echo $lang->get('acpgc_field_editnotice'); ?></label>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row2">
+					<textarea <?php if(getConfig('wiki_edit_notice', '0')!='1') echo('style="display:none" '); ?>rows="5" cols="30" name="editmsg_text" id="editmsg_text"><?php echo getConfig('wiki_edit_notice_text'); ?></textarea>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<b><?php echo $lang->get('acpgc_field_edit_require_captcha_title'); ?></b><br />
+					<?php echo $lang->get('acpgc_field_edit_require_captcha_hint'); ?>
+				</td>
+				<td class="row1">
+					<label>
+						<input type="checkbox" name="guest_edit_require_captcha" <?php if ( getConfig('guest_edit_require_captcha') == '1' ) echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_edit_require_captcha'); ?>
+					</label>
+				</td>
+			</tr>
+			
+		<!-- Site statistics -->
+		
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_stats'); ?></th></tr>
+			
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_stats_intro'); ?><br /><br />
+					<?php echo $lang->get('acpgc_stats_hint_privacy'); ?>
+				</td>
+				<td class="row1">
+					<label>
+						<input type="checkbox" name="log_hits" <?php if(getConfig('log_hits') == '1') echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_stats_enable'); ?>
+					</label><br />
+					<small><?php echo $lang->get('acpgc_field_stats_hint'); ?></small>
+				</td>
+			</tr>
+			
+		<!-- Comment options -->
+			
+			<tr>
+				<th class="subhead" colspan="2">
+					<?php echo $lang->get('acpgc_heading_comments'); ?>
+				</th>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<label for="enable-comments">
+						<b><?php echo $lang->get('acpgc_field_enable_comments'); ?></b>
+					</label>
+				</td>
+				<td class="row1">
+					<input name="enable-comments"  id="enable-comments"  type="checkbox" <?php if(getConfig('enable_comments', '1')=='1')  echo('CHECKED '); ?>/>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row2">
+					<label for="comment-approval">
+						<?php echo $lang->get('acpgc_field_approve_comments'); ?>
+					</label>
+				</td>
+				<td class="row2">
+					<input name="comment-approval" id="comment-approval" type="checkbox" <?php if(getConfig('approve_comments', '0')=='1') echo('CHECKED '); ?>/>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_comment_allow_guests'); ?>
+				</td>
+				<td class="row1">
+					<label>
+						<input name="comments_need_login" type="radio" value="0" <?php if(getConfig('comments_need_login')=='0') echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_comment_allow_guests_yes'); ?>
+					</label>
+					<label>
+						<input name="comments_need_login" type="radio" value="1" <?php if(getConfig('comments_need_login')=='1') echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_comment_allow_guests_captcha'); ?>
+					</label>
+					<label>
+						<input name="comments_need_login" type="radio" value="2" <?php if(getConfig('comments_need_login')=='2') echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_comment_allow_guests_no'); ?>
+					</label>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row2">
+					<?php echo $lang->get('acpgc_field_comment_spam_policy'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_comment_spam_policy_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<label>
+						<input name="comment_spam_policy" type="radio" value="moderate" <?php if ( getConfig('comment_spam_policy', 'moderate') == 'moderate' ) echo 'checked="checked"'; ?>/>
+						<?php echo $lang->get('acpgc_field_comment_spam_policy_moderate'); ?>
+					</label><br /> 
+					<label>
+						<input name="comment_spam_policy" type="radio" value="reject" <?php if ( getConfig('comment_spam_policy', 'moderate') == 'reject' ) echo 'checked="checked"'; ?>/>
+						<?php echo $lang->get('acpgc_field_comment_spam_policy_reject'); ?>
+					</label><br />
+					<label>
+						<input name="comment_spam_policy" type="radio" value="accept" <?php if ( getConfig('comment_spam_policy', 'moderate') == 'accept' ) echo 'checked="checked"'; ?>/>
+						<?php echo $lang->get('acpgc_field_comment_spam_policy_accept'); ?>
+					</label>
+				</td>
+			</tr>
+						
+		<!-- Site disablement -->
+		
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_disablesite'); ?></th></tr>
+			
+			<tr>
+				<td class="row3" rowspan="2">
+					<?php echo $lang->get('acpgc_field_disablesite_hint'); ?>
+				</td>
+				<td class="row1">
+					<label>
+						<input onclick="if(this.checked) document.getElementById('site_disabled_notice').style.display='block'; else document.getElementById('site_disabled_notice').style.display='none';" type="checkbox" name="site_disabled" <?php if(getConfig('site_disabled') == '1') echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_disablesite'); ?>
+					</label>
+				</td>
+			</tr>
+			<tr>
+				<td class="row2">
+					<div id="site_disabled_notice"<?php if(getConfig('site_disabled')!='1') echo(' style="display:none"'); ?>>
+						<?php echo $lang->get('acpgc_field_disablesite_message'); ?><br />
+						<textarea name="site_disabled_notice" rows="7" cols="30"><?php echo getConfig('site_disabled_notice'); ?></textarea>
+					</div>
+				</td>
+			</tr>
+			
+		<!-- Default theme -->
+		
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_default_theme'); ?></th></tr>
+			
+			<tr>
+				<td class="row2">
+					<?php echo $lang->get('acpgc_field_default_theme'); ?>
+				</td>
+				<td class="row2">
+					<select name="default_theme">
+					<?php
+							foreach ( $template->named_theme_list as $theme_id => $theme_data )
+							{
+								if ( !isset($theme_data['theme_name']) )
+									// probably a system theme
+									continue;
+									
+								$theme_name = htmlspecialchars($theme_data['theme_name']);
+								$selected = ( $theme_id === getConfig('theme_default') ) ? ' selected="selected"' : '';
+								echo "  <option value=\"$theme_id\"$selected>$theme_name</option>\n          ";
+							}
+						?>
+					</select>
+				</td>
+			</tr>
+			
+		<!-- Breadcrumbs -->
+		
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_breadcrumb_mode'); ?>
+				</td>
+				<td class="row1">
+					<select name="breadcrumb_mode">
+					<?php
+						foreach ( array('subpages', 'always', 'never') as $mode )
+						{
+							$str = $lang->get("acpgc_field_breadcrumb_mode_$mode");
+							$sel = ( getConfig('breadcrumb_mode') == $mode ) ? ' selected="selected"' : '';
+							echo "  <option value=\"$mode\"$sel>$str</option>\n          ";
+						}
+					?>
+					</select>
+				</td>
+			</tr>
+		
+		<!-- CDN settings -->
+		
+			<tr>
+				<td class="row2">
+					<p>
+						<?php echo $lang->get('acpgc_field_cdn_path'); ?><br />
+						<small><?php echo $lang->get('acpgc_field_cdn_path_hint'); ?></small>
+					</p>
+					<p>
+						<small><?php echo $lang->get('acpgc_field_cdn_path_example'); ?></small>
+					</p>
+				</td>
+				<td class="row2">
+					<input type="text" name="cdn_path" value="<?php echo htmlspecialchars(getConfig('cdn_path', '')); ?>" style="width: 98%;" />
+				</td>
+			</tr>
+			
+		<!-- Gzip -->
+		
+			<tr>
+				<td class="row1">
+					<b><?php echo $lang->get('acpgc_field_gzip'); ?></b><br />
+					<small><?php echo $lang->get('acpgc_field_gzip_hint'); ?></small><br />
+					<br />
+					<a href="#" onclick="ajaxGzipCheck(); return false;"><?php echo $lang->get('acpgc_field_gzip_btn_check'); ?></a>
+				</td>
+				<td class="row1">
+					<div id="gzip_check_result"></div>
+					<label>
+						<input type="checkbox" name="gzip_output" <?php if ( getConfig('gzip_output', false) == 1 ) echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_gzip_lbl'); ?>
+					</label>
+				</td>
+			</tr>
+			
+		<!-- Allow plugins to add code -->
+			<?php
+			$code = $plugins->setHook('acp_general_basic');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			?>
+			
+		</table>
+		</div>
+				
+		<div class="tblholder">
+		<table border="0" width="100%" cellspacing="1" cellpadding="4">
+		
+		<tr>
+			<th colspan="2"><?php echo $lang->get('acpgc_heading_users'); ?></th>
+		</tr>
+		
+		<!-- Account activation -->
+			
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_activate'); ?></th></tr>
+			
+			<tr>
+				<td class="row3" colspan="2">
+					<?php echo $lang->get('acpgc_activate_intro_line1'); ?><br /><br />
+					<?php echo $lang->get('acpgc_activate_intro_line2'); ?><br /><br />
+					<b><?php echo $lang->get('acpgc_activate_intro_sfnet_warning'); ?></b>
+				</td>
+			</tr>
+			
+			<tr>
+			<td class="row1" style="width: 50%;"><?php echo $lang->get('acpgc_field_activate'); ?></td><td class="row1">
+					<?php
+					echo '<label><input'; if(getConfig('account_activation') == 'disable') echo ' checked="checked"'; echo ' type="radio" name="account_activation" value="disable" /> ' . $lang->get('acpgc_field_activate_disable') . '</label><br />';
+					echo '<label><input'; if(getConfig('account_activation') != 'user' && getConfig('account_activation') != 'admin' && getConfig('account_activation') != 'disable') echo ' checked="checked"'; echo ' type="radio" name="account_activation" value="none" /> ' . $lang->get('acpgc_field_activate_none') . '</label>';
+					echo '<label><input'; if(getConfig('account_activation') == 'user') echo ' checked="checked"'; echo ' type="radio" name="account_activation" value="user" /> ' . $lang->get('acpgc_field_activate_user') . '</label>';
+					echo '<label><input'; if(getConfig('account_activation') == 'admin') echo ' checked="checked"'; echo ' type="radio" name="account_activation" value="admin" /> ' . $lang->get('acpgc_field_activate_admin') . '</label>';
+					?>
+				</td>
+			</tr>
+			
+		<!-- Terms of Use -->
+		
+			<tr>
+				<th class="subhead" colspan="2">
+					<?php echo $lang->get('acpgc_heading_tou'); ?>
+				</th>
+			</tr>
+			
+			<tr>
+				<td class="row2">
+					<b><?php echo $lang->get('acpgc_field_tou'); ?></b><br />
+					<small><?php echo $lang->get('acpgc_field_tou_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<?php
+						$terms = getConfig('register_tou');
+						echo $template->tinymce_textarea('register_tou', $terms, 10, 40);
+					?>
+				</td>
+			</tr>
+			
+		<!-- Account lockout -->
+		
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_lockout'); ?></th></tr>
+			
+			<tr><td class="row3" colspan="2"><?php echo $lang->get('acpgc_lockout_intro'); ?></td></tr>
+			
+			<tr>
+				<td class="row2"><?php echo $lang->get('acpgc_field_lockout_threshold'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_lockout_threshold_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<input type="text" name="lockout_threshold" value="<?php echo ( $_ = getConfig('lockout_threshold') ) ? $_ : '5' ?>" />
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row1"><?php echo $lang->get('acpgc_field_lockout_duration'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_lockout_duration_hint'); ?></small>
+				</td>
+				<td class="row1">
+					<input type="text" name="lockout_duration" value="<?php echo ( $_ = getConfig('lockout_duration') ) ? $_ : '15' ?>" />
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row2"><?php echo $lang->get('acpgc_field_lockout_policy'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_lockout_policy_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<label><input type="radio" name="lockout_policy" value="disable" <?php if ( getConfig('lockout_policy') == 'disable' ) echo 'checked="checked"'; ?> /> <?php echo $lang->get('acpgc_field_lockout_policy_nothing'); ?></label><br />
+					<label><input type="radio" name="lockout_policy" value="captcha" <?php if ( getConfig('lockout_policy') == 'captcha' ) echo 'checked="checked"'; ?> /> <?php echo $lang->get('acpgc_field_lockout_policy_captcha'); ?></label><br />
+					<label><input type="radio" name="lockout_policy" value="lockout" <?php if ( getConfig('lockout_policy') == 'lockout' || !getConfig('lockout_policy') ) echo 'checked="checked"'; ?> /> <?php echo $lang->get('acpgc_field_lockout_policy_lockout'); ?></label>
+				</td>
+			</tr>
+			
+		<!-- Password strength -->
+			
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_passstrength'); ?></th></tr>
+			
+			<tr>
+				<td class="row2">
+					<b><?php echo $lang->get('acpgc_field_passstrength_title'); ?></b><br />
+					<small><?php echo $lang->get('acpgc_field_passstrength_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<label><input type="checkbox" name="pw_strength_enable" <?php if ( getConfig('pw_strength_enable') == '1' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_passstrength'); ?></label>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<b><?php echo $lang->get('acpgc_field_passminimum_title'); ?></b><br />
+					<small><?php echo $lang->get('acpgc_field_passminimum_hint'); ?></small>
+				</td>
+				<td class="row1">
+					<input type="text" name="pw_strength_minimum" value="<?php echo strval(getConfig('pw_strength_minimum', -10)); ?>" />
+				</td>
+			</tr>
+			
+		<!-- E-mail options -->
+		
+			<tr>
+				<th class="subhead" colspan="2">
+					<?php echo $lang->get('acpgc_heading_email'); ?>
+				</th>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_email_method'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_email_method_hint'); ?></small>
+				</td>
+				<td class="row1">
+					<label>
+						<input <?php if(getConfig('smtp_enabled') != '1') echo 'checked="checked"'; ?> type="radio" name="emailmethod" value="phpmail" />
+						<?php echo $lang->get('acpgc_field_email_method_builtin'); ?>
+					</label>
+					
+					<br />
+					
+					<label>
+						<input <?php if(getConfig('smtp_enabled') == '1') echo 'checked="checked"'; ?> type="radio" name="emailmethod" value="smtp" />
+						<?php echo $lang->get('acpgc_field_email_method_smtp'); ?>
+					</label>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row2">
+					<?php echo $lang->get('acpgc_field_email_smtp_hostname'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_email_smtp_hostname_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<input value="<?php echo getConfig('smtp_server'); ?>" name="smtp_host" type="text" size="30" />
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_email_smtp_auth'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_email_smtp_hostname_hint'); ?></small>
+				</td>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_email_smtp_username'); ?> <input value="<?php echo getConfig('smtp_user'); ?>" name="smtp_user" type="text" size="30" /><br />
+					<?php echo $lang->get('acpgc_field_email_smtp_password'); ?> <input value="<?php if(getConfig('smtp_password') != false) echo 'XXXXXXXXXXXX'; ?>" name="smtp_pass" type="password" size="30" />
+				</td>
+			</tr>
+			
+		<!-- Session length -->
+		
+			<tr>
+				<th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_sessions'); ?></th>
+			</tr>
+			
+			<tr>
+				<td class="row3" colspan="2"><?php echo $lang->get('acpgc_hint_sessions_noelev'); ?></td>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_short_time'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_short_time_hint'); ?></small>
+				</td>
+				<td class="row1">
+					<input type="text" name="session_short_time" value="<?php echo getConfig('session_short_time', '720'); ?>" size="4" />
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row2">
+					<?php echo $lang->get('acpgc_field_long_time'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_long_time_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<input type="text" name="session_remember_time" value="<?php echo getConfig('session_remember_time', '30'); ?>" size="4" />
+				</td>
+			</tr>
+				
+		<!-- Avatar support -->
+		
+			<tr>
+				<th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_avatars'); ?></th>
+			</tr>
+			
+			<tr>
+				<td class="row3" colspan="2">
+					<?php echo $lang->get('acpgc_avatars_intro'); ?>
+				</th>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_avatar_enable'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_avatar_enable_hint'); ?></small>
+				</td>
+				<td class="row1">
+					<label><input type="checkbox" name="avatar_enable" <?php if ( getConfig('avatar_enable') == '1' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_avatar_enable_label'); ?></label>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row2">
+					<?php echo $lang->get('acpgc_field_avatar_max_filesize'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_avatar_max_filesize_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<input type="text" name="avatar_max_size" size="7" <?php if ( ($x = getConfig('avatar_max_size')) !== false ) echo "value=\"$x\" "; else echo "value=\"10240\" "; ?>/> <?php echo $lang->get('etc_unit_bytes'); ?>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_avatar_max_dimensions'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_avatar_max_dimensions_hint'); ?></small>
+				</td>
+				<td class="row1">
+					<input type="text" name="avatar_max_width" size="7" <?php if ( $x = getConfig('avatar_max_width') ) echo "value=\"$x\" "; else echo "value=\"150\" "; ?>/> &#215;
+					<input type="text" name="avatar_max_height" size="7" <?php if ( $x = getConfig('avatar_max_height') ) echo "value=\"$x\" "; else echo "value=\"150\" "; ?>/> <?php echo $lang->get('etc_unit_pixels'); ?>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row2">
+					<?php echo $lang->get('acpgc_field_avatar_allow_anim_title'); ?><br />
+					<small><?php echo $lang->get('acpgc_field_avatar_allow_anim_hint'); ?></small>
+				</td>
+				<td class="row2">
+					<label><input type="checkbox" name="avatar_enable_anim" <?php if ( getConfig('avatar_enable_anim') == '1' ) echo 'checked="checked" '; ?>/> <?php echo $lang->get('acpgc_field_avatar_allow_anim'); ?></label>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpgc_field_avatar_upload_methods'); ?><br />
+					<small></small>
+				</td>
+				<td class="row1">
+					<label>
+						<input type="checkbox" name="avatar_upload_file" <?php if ( getConfig('avatar_upload_file', 1) == 1 ) echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_avatar_upload_file'); ?>
+					</label>
+					
+					<br />
+					
+					<label>
+						<input type="checkbox" name="avatar_upload_http" <?php if ( getConfig('avatar_upload_http', 1) == 1 ) echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_avatar_upload_http'); ?>
+					</label>
+					
+					<br />
+					
+					<label>
+					<input type="checkbox" name="avatar_upload_gravatar" <?php if ( getConfig('avatar_upload_gravatar', 1) == 1 ) echo 'checked="checked" '; ?>onclick="document.getElementById('acp_gravatar_rating').style.display = ( this.checked ) ? 'block' : 'none';" />
+						<?php echo $lang->get('acpgc_field_avatar_upload_gravatar'); ?>
+					</label>
+					
+					<br />
+					
+					<fieldset id="acp_gravatar_rating" style="margin-top: 10px; <?php if ( getConfig('avatar_upload_gravatar', 1) == 0 ) echo ' display: none;'; ?>">
+					
+						<?php /* The four ratings are g, pg, r, and x - loop through each and output a localized string and a radiobutton */ ?>
+						<legend><?php echo $lang->get('acpgc_field_avatar_gravatar_rating'); ?></legend>
+						
+						<?php foreach ( array('g', 'pg', 'r', 'x') as $rating ): ?>
+						
+						<label>
+						
+							<input type="radio" name="gravatar_rating" value="<?php echo $rating; ?>"<?php
+								// Check the button if this is the current selection *or* if we're on "G" and the current configuration value is unset
+								if ( getConfig('gravatar_rating', 'g') == $rating )
+									echo ' checked="checked"';
+								?> />
+								
+							<?php /* The localized string */ ?>
+							<?php echo $lang->get("acpgc_field_avatar_gravatar_rating_$rating"); ?>
+							
+						</label>
+						
+						<br />
+						
+						<?php endforeach; ?>
+					</fieldset>
+				</td>
+			</tr>
+			
+		<!-- Misc. options -->
+		
+			<tr>
+				<th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_usermisc'); ?></th>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<b><?php echo $lang->get('acpgc_field_userpage_acl_title'); ?></b><br />
+					<small>
+						<?php echo $lang->get('acpgc_field_userpage_acl_hint'); ?>
+					</small>
+				</td>
+				<td class="row1">
+					<label>
+						<input type="checkbox" name="userpage_grant_acl" <?php if ( getConfig('userpage_grant_acl', '1') == '1' ) echo 'checked="checked" '; ?>/>
+						<?php echo $lang->get('acpgc_field_userpage_acl'); ?>
+					</label>
+				</td>
+			</tr>
+			
+		<!-- Allow plugins to add code -->
+			<?php
+			$code = $plugins->setHook('acp_general_users');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			?>
+				
+		</table>
+		</div>
+		
+		<div class="tblholder">
+		<table border="0" width="100%" cellspacing="1" cellpadding="4">
+		
+		<tr>
+			<th colspan="2"><?php echo $lang->get('acpgc_heading_sidebar'); ?></th>
+		</tr>
+		
+		<!-- enanocms.org link -->
+		
+		<tr>
+			<th colspan="2" class="subhead"><?php echo $lang->get('acpgc_heading_promoteenano'); ?></th>
+		</tr>                      
+		<tr>
+			<td class="row3" style="width: 50%;">
+				<b><?php echo $lang->get('acpgc_field_enano_link_title'); ?></b><br />
+				<small><?php echo $lang->get('acpgc_field_enano_link_hint'); ?></small>
+			</td>
+			<td class="row1">
+				<label>
+					<input name="enano_powered_link" type="checkbox" <?php if(getConfig('powered_btn', '1') == '1') echo 'checked="checked"'; ?> />&nbsp;&nbsp;<?php echo $lang->get('acpgc_field_enano_link'); ?>
+				</label>
+			</td>
+		</tr>
+			
+		<!-- SourceForge.net logo -->
+			
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_sfnet_logo'); ?></th></tr>
+			
+			<tr>
+				<td colspan="2" class="row3">
+					<?php echo $lang->get('acpgc_sfnet_intro'); ?>
+				</td>
+			</tr>
+			
+			<?php
+			if ( getConfig("sflogo_enabled") == '1' )
+				$c='checked="checked" ';
+			else
+				$c='';
+				
+			if ( getConfig("sflogo_groupid") )
+				$g = getConfig("sflogo_groupid");
+			else
+				$g = '';
+				
+			if ( getConfig("sflogo_type") )
+				$t = getConfig("sflogo_type");
+			else
+				$t = '1';
+			?>
+			
+			<tr>
+				<td class="row1"><?php echo $lang->get('acpgc_field_sfnet_display'); ?></td>
+				<td class="row1"><input type=checkbox name="showsf" id="showsf" <?php echo $c; ?> /></td>
+			</tr>
+			
+			<tr>
+				<td class="row2"><?php echo $lang->get('acpgc_field_sfnet_group_id'); ?></td>
+				<td class="row2"><input value="<?php echo $g; ?>" type=text size=15 name=sfgroup /></td>
+			</tr>
+			
+			<tr>
+				<td class="row1"><?php echo $lang->get('acpgc_field_sfnet_logo_style'); ?></td>
+				<td class="row1">
+					<select name="sflogo">
+						<option <?php if($t=='1') echo('selected="selected" '); ?>value=1><?php echo $lang->get('acpgc_field_sfnet_logo_style_1'); ?></option>
+						<option <?php if($t=='2') echo('selected="selected" '); ?>value=2><?php echo $lang->get('acpgc_field_sfnet_logo_style_2'); ?></option>
+						<option <?php if($t=='3') echo('selected="selected" '); ?>value=3><?php echo $lang->get('acpgc_field_sfnet_logo_style_3'); ?></option>
+						<option <?php if($t=='4') echo('selected="selected" '); ?>value=4><?php echo $lang->get('acpgc_field_sfnet_logo_style_4'); ?></option>
+						<option <?php if($t=='5') echo('selected="selected" '); ?>value=5><?php echo $lang->get('acpgc_field_sfnet_logo_style_5'); ?></option>
+						<option <?php if($t=='6') echo('selected="selected" '); ?>value=6><?php echo $lang->get('acpgc_field_sfnet_logo_style_6'); ?></option>
+						<option <?php if($t=='7') echo('selected="selected" '); ?>value=7><?php echo $lang->get('acpgc_field_sfnet_logo_style_7'); ?></option>
+					</select>
+				</td>
+			</tr>
+			
+		<!-- W3C validator buttons -->
+			
+			<tr><th class="subhead" colspan="2"><?php echo $lang->get('acpgc_heading_w3clogos'); ?></th></tr>
+			<tr><td colspan="2" class="row3"><?php echo $lang->get('acpgc_w3clogos_intro'); ?></th></tr>
+			
+			<tr><td class="row1"><label for="w3c-vh32"><?php     echo $lang->get('acpgc_w3clogos_btn_html32');  ?></label></td><td class="row1"><input type="checkbox" <?php if(getConfig('w3c_vh32')=='1')     echo('checked="checked" '); ?> id="w3c-vh32"     name="w3c-vh32"     /></td></tr>
+			<tr><td class="row2"><label for="w3c-vh40"><?php     echo $lang->get('acpgc_w3clogos_btn_html40');  ?></label></td><td class="row2"><input type="checkbox" <?php if(getConfig('w3c_vh40')=='1')     echo('checked="checked" '); ?> id="w3c-vh40"     name="w3c-vh40"     /></td></tr>
+			<tr><td class="row1"><label for="w3c-vh401"><?php    echo $lang->get('acpgc_w3clogos_btn_html401'); ?></label></td><td class="row1"><input type="checkbox" <?php if(getConfig('w3c_vh401')=='1')    echo('checked="checked" '); ?> id="w3c-vh401"    name="w3c-vh401"    /></td></tr>
+			<tr><td class="row2"><label for="w3c-vxhtml10"><?php echo $lang->get('acpgc_w3clogos_btn_xhtml10'); ?></label></td><td class="row2"><input type="checkbox" <?php if(getConfig('w3c_vxhtml10')=='1') echo('checked="checked" '); ?> id="w3c-vxhtml10" name="w3c-vxhtml10" /></td></tr>
+			<tr><td class="row1"><label for="w3c-vxhtml11"><?php echo $lang->get('acpgc_w3clogos_btn_xhtml11'); ?></label></td><td class="row1"><input type="checkbox" <?php if(getConfig('w3c_vxhtml11')=='1') echo('checked="checked" '); ?> id="w3c-vxhtml11" name="w3c-vxhtml11" /></td></tr>
+			<tr><td class="row2"><label for="w3c-vcss"><?php     echo $lang->get('acpgc_w3clogos_btn_css');     ?></label></td><td class="row2"><input type="checkbox" <?php if(getConfig('w3c_vcss')=='1')     echo('checked="checked" '); ?> id="w3c-vcss"     name="w3c-vcss"     /></td></tr>
 
-    <!-- DefectiveByDesign.org ad -->      
-      
-      <tr>
-        <th class="subhead" colspan="2">
-          <?php echo $lang->get('acpgc_heading_dbd'); ?>
-        </th>
-      </tr>
-      
-      <tr>
-        <td colspan="2" class="row3">
-          <b><?php echo $lang->get('acpgc_dbd_intro'); ?></b>
-          <?php echo $lang->get('acpgc_dbd_explain'); ?>
-        </td>
-      </tr>
-      
-      <tr>
-        <td class="row1">
-          <label for="dbdbutton">
-            <?php echo $lang->get('acpgc_field_stopdrm'); ?>
-          </label>
-        </td>
-        <td class="row1">
-          <input type="checkbox" name="dbdbutton" id="dbdbutton" <?php if(getConfig('dbd_button')=='1')  echo('checked="checked" '); ?>/>
-        </td>
-      </tr>
-      
-    <!-- Allow plugins to add code -->
-      <?php
-      $code = $plugins->setHook('acp_general_sidebar');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      ?>
-      
-    <!-- Save button -->
-    
-    </table>
-    </div>
-    
-    <!-- Allow plugins to add code -->
-      <?php
-      $code = $plugins->setHook('acp_general_tail');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      ?>
-        
-    <div class="tblholder">
-    <table border="0" width="100%" cellspacing="1" cellpadding="4">
-      
-      <tr><th colspan="2"><input type="submit" name="submit" value="<?php echo $lang->get('acpgc_btn_save_changes'); ?>" /></th></tr>
-      
-    </table>
-  </div>
+		<!-- DefectiveByDesign.org ad -->      
+			
+			<tr>
+				<th class="subhead" colspan="2">
+					<?php echo $lang->get('acpgc_heading_dbd'); ?>
+				</th>
+			</tr>
+			
+			<tr>
+				<td colspan="2" class="row3">
+					<b><?php echo $lang->get('acpgc_dbd_intro'); ?></b>
+					<?php echo $lang->get('acpgc_dbd_explain'); ?>
+				</td>
+			</tr>
+			
+			<tr>
+				<td class="row1">
+					<label for="dbdbutton">
+						<?php echo $lang->get('acpgc_field_stopdrm'); ?>
+					</label>
+				</td>
+				<td class="row1">
+					<input type="checkbox" name="dbdbutton" id="dbdbutton" <?php if(getConfig('dbd_button')=='1')  echo('checked="checked" '); ?>/>
+				</td>
+			</tr>
+			
+		<!-- Allow plugins to add code -->
+			<?php
+			$code = $plugins->setHook('acp_general_sidebar');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			?>
+			
+		<!-- Save button -->
+		
+		</table>
+		</div>
+		
+		<!-- Allow plugins to add code -->
+			<?php
+			$code = $plugins->setHook('acp_general_tail');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			?>
+				
+		<div class="tblholder">
+		<table border="0" width="100%" cellspacing="1" cellpadding="4">
+			
+			<tr><th colspan="2"><input type="submit" name="submit" value="<?php echo $lang->get('acpgc_btn_save_changes'); ?>" /></th></tr>
+			
+		</table>
+	</div>
 </form>
 
 <script type="text/javascript">addOnloadHook(function() { admin_table_onload(namespace_list['Admin'] + 'GeneralConfig') });</script>
-  <?php
+	<?php
 }
 
 function page_Admin_UploadConfig()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  if(isset($_POST['save']))
-  {
-    if(isset($_POST['enable_uploads']) && getConfig('enable_uploads') != '1')
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'upload_enable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
-      if ( !$q )
-        $db->_die();
-      setConfig('enable_uploads', '1');
-    }
-    else if ( !isset($_POST['enable_uploads']) && getConfig('enable_uploads') == '1' )
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'upload_disable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
-      if ( !$q )
-        $db->_die();
-      setConfig('enable_uploads', '0');
-    }
-    if(isset($_POST['enable_imagemagick']) && getConfig('enable_imagemagick') != '1')
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'magick_enable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
-      if ( !$q )
-        $db->_die();
-      setConfig('enable_imagemagick', '1');
-    }
-    else if ( !isset($_POST['enable_imagemagick']) && getConfig('enable_imagemagick') == '1' )
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'magick_disable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
-      if ( !$q )
-        $db->_die();
-      setConfig('enable_imagemagick', '0');
-    }
-    if(isset($_POST['cache_thumbs']))
-    {
-      setConfig('cache_thumbs', '1');
-    }
-    else
-    {
-      setConfig('cache_thumbs', '0');
-    }
-    if(isset($_POST['file_history']) && getConfig('file_history') != '1' )
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'filehist_enable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\',' . $session->user_id . ');');
-      if ( !$q )
-        $db->_die();
-      setConfig('file_history', '1');
-    }
-    else if ( !isset($_POST['file_history']) && getConfig('file_history') == '1' )
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'filehist_disable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\',' . $session->user_id . ');');
-      if ( !$q )
-        $db->_die();
-      setConfig('file_history', '0');
-    }
-    if(file_exists($_POST['imagemagick_path']) && $_POST['imagemagick_path'] != getConfig('imagemagick_path'))
-    {
-      if ( defined('ENANO_DEMO_MODE') )
-        // Hackish but safe.
-        $_POST['imagemagick_path'] = '/usr/bin/convert';
-      $old = getConfig('imagemagick_path');
-      $oldnew = "{$old}||{$_POST['imagemagick_path']}";
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'magick_path\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\',' . $session->user_id . ',\'' . $db->escape($oldnew) . '\');');
-      if ( !$q )
-        $db->_die();
-      setConfig('imagemagick_path', $_POST['imagemagick_path']);
-    }
-    else if ( $_POST['imagemagick_path'] != getConfig('imagemagick_path') )
-    {
-      echo '<span style="color: red">' . $lang->get('acpup_err_magick_not_found', array('magick_path' => htmlspecialchars($_POST['imagemagick_path']))) . '</span>';
-    }
-    $max_upload = floor((float)$_POST['max_file_size'] * (int)$_POST['fs_units']);
-    if ( $max_upload > 1048576 && defined('ENANO_DEMO_MODE') )
-    {
-      echo '<div class="error-box">Wouldn\'t want the server DoS\'ed now. Stick to under a megabyte for the demo, please.</div>';
-    }
-    else
-    {
-      setConfig('max_file_size', $max_upload.'');
-    }
-  }
-  acp_start_form();
-  ?>
-  <h3><?php echo $lang->get('acpup_heading_main'); ?></h3>
-  
-  <p>
-    <?php echo $lang->get('acpup_intro'); ?>
-  </p>
-  <p>
-    <label>
-      <input type="checkbox" name="enable_uploads" <?php if(getConfig('enable_uploads')=='1') echo 'checked="checked"'; ?> />
-      <b><?php echo $lang->get('acpup_field_enable'); ?></b>
-    </label>
-  </p>
-  <p>
-    <?php echo $lang->get('acpup_field_max_size'); ?>
-    <input name="max_file_size" onkeyup="if(!this.value.match(/^([0-9\.]+)$/ig)) this.value = this.value.substr(0,this.value.length-1);" value="<?php echo getConfig('max_file_size', '256000'); ?>" />
-    <select name="fs_units">
-      <option value="1" selected="selected"><?php echo $lang->get('etc_unit_bytes'); ?></option>
-      <option value="1024"><?php echo $lang->get('etc_unit_kilobytes_short'); ?></option>
-      <option value="1048576"><?php echo $lang->get('etc_unit_megabytes_short'); ?></option>
-    </select>
-  </p>
-  
-  <p><?php echo $lang->get('acpup_info_magick'); ?></p>
-  <p>
-    <label>
-      <input type="checkbox" name="enable_imagemagick" <?php if(getConfig('enable_imagemagick')=='1') echo 'checked="checked"'; ?> />
-      <?php echo $lang->get('acpup_field_magick_enable'); ?>
-    </label>
-    <br />
-    <?php echo $lang->get('acpup_field_magick_path'); ?> <input type="text" name="imagemagick_path" value="<?php if(getConfig('imagemagick_path')) echo getConfig('imagemagick_path'); else echo '/usr/bin/convert'; ?>" /><br />
-    <?php echo $lang->get('acpup_field_magick_path_hint'); ?>
-  </p>
-     
-  <p><?php echo $lang->get('acpup_info_cache'); ?></p>
-  <p>
-    <?php echo $lang->get('acpup_info_cache_chmod'); ?>
-  
-    <?php
-      if(!is_writable(ENANO_ROOT.'/cache/'))
-        echo $lang->get('acpup_msg_cache_not_writable');
-    ?>
-  </p>
-  
-  <p>
-    <label>
-      <input type="checkbox" name="cache_thumbs" <?php if(getConfig('cache_thumbs')=='1' && is_writable(ENANO_ROOT.'/cache/')) echo 'checked="checked"'; else if ( ! is_writable(ENANO_ROOT . '/cache/') ) echo 'readonly="readonly"'; ?> />
-      <?php echo $lang->get('acpup_field_cache'); ?>
-    </label>
-  </p>
-  
-  <p><?php echo $lang->get('acpup_info_history'); ?></p>
-  <p>
-    <label>
-      <input type="checkbox" name="file_history" <?php if(getConfig('file_history')=='1') echo 'checked="checked"'; ?> />
-      <?php echo $lang->get('acpup_field_history'); ?>
-    </label>
-  </p>
-  
-  <hr style="margin-left: 1em;" />
-  <p><input type="submit" name="save" value="<?php echo $lang->get('acpup_btn_save'); ?>" style="font-weight: bold;" /></p>
-  <?php
-  echo '</form>';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	if(isset($_POST['save']))
+	{
+		if(isset($_POST['enable_uploads']) && getConfig('enable_uploads') != '1')
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'upload_enable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
+			if ( !$q )
+				$db->_die();
+			setConfig('enable_uploads', '1');
+		}
+		else if ( !isset($_POST['enable_uploads']) && getConfig('enable_uploads') == '1' )
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'upload_disable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
+			if ( !$q )
+				$db->_die();
+			setConfig('enable_uploads', '0');
+		}
+		if(isset($_POST['enable_imagemagick']) && getConfig('enable_imagemagick') != '1')
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'magick_enable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
+			if ( !$q )
+				$db->_die();
+			setConfig('enable_imagemagick', '1');
+		}
+		else if ( !isset($_POST['enable_imagemagick']) && getConfig('enable_imagemagick') == '1' )
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'magick_disable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
+			if ( !$q )
+				$db->_die();
+			setConfig('enable_imagemagick', '0');
+		}
+		if(isset($_POST['cache_thumbs']))
+		{
+			setConfig('cache_thumbs', '1');
+		}
+		else
+		{
+			setConfig('cache_thumbs', '0');
+		}
+		if(isset($_POST['file_history']) && getConfig('file_history') != '1' )
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'filehist_enable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\',' . $session->user_id . ');');
+			if ( !$q )
+				$db->_die();
+			setConfig('file_history', '1');
+		}
+		else if ( !isset($_POST['file_history']) && getConfig('file_history') == '1' )
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'filehist_disable\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\',' . $session->user_id . ');');
+			if ( !$q )
+				$db->_die();
+			setConfig('file_history', '0');
+		}
+		if(file_exists($_POST['imagemagick_path']) && $_POST['imagemagick_path'] != getConfig('imagemagick_path'))
+		{
+			if ( defined('ENANO_DEMO_MODE') )
+				// Hackish but safe.
+				$_POST['imagemagick_path'] = '/usr/bin/convert';
+			$old = getConfig('imagemagick_path');
+			$oldnew = "{$old}||{$_POST['imagemagick_path']}";
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'magick_path\',' . time() . ',\'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\',\'' . $db->escape($session->username) . '\',' . $session->user_id . ',\'' . $db->escape($oldnew) . '\');');
+			if ( !$q )
+				$db->_die();
+			setConfig('imagemagick_path', $_POST['imagemagick_path']);
+		}
+		else if ( $_POST['imagemagick_path'] != getConfig('imagemagick_path') )
+		{
+			echo '<span style="color: red">' . $lang->get('acpup_err_magick_not_found', array('magick_path' => htmlspecialchars($_POST['imagemagick_path']))) . '</span>';
+		}
+		$max_upload = floor((float)$_POST['max_file_size'] * (int)$_POST['fs_units']);
+		if ( $max_upload > 1048576 && defined('ENANO_DEMO_MODE') )
+		{
+			echo '<div class="error-box">Wouldn\'t want the server DoS\'ed now. Stick to under a megabyte for the demo, please.</div>';
+		}
+		else
+		{
+			setConfig('max_file_size', $max_upload.'');
+		}
+	}
+	acp_start_form();
+	?>
+	<h3><?php echo $lang->get('acpup_heading_main'); ?></h3>
+	
+	<p>
+		<?php echo $lang->get('acpup_intro'); ?>
+	</p>
+	<p>
+		<label>
+			<input type="checkbox" name="enable_uploads" <?php if(getConfig('enable_uploads')=='1') echo 'checked="checked"'; ?> />
+			<b><?php echo $lang->get('acpup_field_enable'); ?></b>
+		</label>
+	</p>
+	<p>
+		<?php echo $lang->get('acpup_field_max_size'); ?>
+		<input name="max_file_size" onkeyup="if(!this.value.match(/^([0-9\.]+)$/ig)) this.value = this.value.substr(0,this.value.length-1);" value="<?php echo getConfig('max_file_size', '256000'); ?>" />
+		<select name="fs_units">
+			<option value="1" selected="selected"><?php echo $lang->get('etc_unit_bytes'); ?></option>
+			<option value="1024"><?php echo $lang->get('etc_unit_kilobytes_short'); ?></option>
+			<option value="1048576"><?php echo $lang->get('etc_unit_megabytes_short'); ?></option>
+		</select>
+	</p>
+	
+	<p><?php echo $lang->get('acpup_info_magick'); ?></p>
+	<p>
+		<label>
+			<input type="checkbox" name="enable_imagemagick" <?php if(getConfig('enable_imagemagick')=='1') echo 'checked="checked"'; ?> />
+			<?php echo $lang->get('acpup_field_magick_enable'); ?>
+		</label>
+		<br />
+		<?php echo $lang->get('acpup_field_magick_path'); ?> <input type="text" name="imagemagick_path" value="<?php if(getConfig('imagemagick_path')) echo getConfig('imagemagick_path'); else echo '/usr/bin/convert'; ?>" /><br />
+		<?php echo $lang->get('acpup_field_magick_path_hint'); ?>
+	</p>
+ 		
+	<p><?php echo $lang->get('acpup_info_cache'); ?></p>
+	<p>
+		<?php echo $lang->get('acpup_info_cache_chmod'); ?>
+	
+		<?php
+			if(!is_writable(ENANO_ROOT.'/cache/'))
+				echo $lang->get('acpup_msg_cache_not_writable');
+		?>
+	</p>
+	
+	<p>
+		<label>
+			<input type="checkbox" name="cache_thumbs" <?php if(getConfig('cache_thumbs')=='1' && is_writable(ENANO_ROOT.'/cache/')) echo 'checked="checked"'; else if ( ! is_writable(ENANO_ROOT . '/cache/') ) echo 'readonly="readonly"'; ?> />
+			<?php echo $lang->get('acpup_field_cache'); ?>
+		</label>
+	</p>
+	
+	<p><?php echo $lang->get('acpup_info_history'); ?></p>
+	<p>
+		<label>
+			<input type="checkbox" name="file_history" <?php if(getConfig('file_history')=='1') echo 'checked="checked"'; ?> />
+			<?php echo $lang->get('acpup_field_history'); ?>
+		</label>
+	</p>
+	
+	<hr style="margin-left: 1em;" />
+	<p><input type="submit" name="save" value="<?php echo $lang->get('acpup_btn_save'); ?>" style="font-weight: bold;" /></p>
+	<?php
+	echo '</form>';
 }
 
 function page_Admin_UploadAllowedMimeTypes()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  global $mime_types, $mimetype_exps, $mimetype_extlist;
-  if(isset($_POST['save']) && !defined('ENANO_DEMO_MODE'))
-  {
-    $bits = '';
-    $keys = array_keys($mime_types);
-    foreach($keys as $i => $k)
-    {
-      if(isset($_POST['ext_'.$k])) $bits .= '1';
-      else $bits .= '0';
-    }
-    $bits = compress_bitfield($bits);
-    setConfig('allowed_mime_types', $bits);
-    echo '<div class="info-box">' . $lang->get('acpft_msg_saved') . '</div>';
-  }
-  else if ( isset($_POST['save']) && defined('ENANO_DEMO_MODE') )
-  {
-    echo '<div class="error-box">' . $lang->get('acpft_msg_demo_mode') . '</div>';
-  }
-  $allowed = fetch_allowed_extensions();
-  ?>
-  <h3><?php echo $lang->get('acpft_heading_main'); ?></h3>
-   <p><?php echo $lang->get('acpft_hint'); ?></p>
-  <?php
-  acp_start_form();
-    $c = -1;
-    $t = -1;
-    $cl = 'row1';
-    echo "\n".'    <div class="tblholder">'."\n".'      <table cellspacing="1" cellpadding="2" style="margin: 0; padding: 0;" border="0">'."\n".'        <tr>'."\n        ";
-    ksort($mime_types);
-    foreach($mime_types as $e => $m)
-    {
-      $c++;
-      $t++;
-      if($c == 3)
-      {
-        $c = 0;
-        $cl = ( $cl == 'row1' ) ? 'row2' : 'row1';
-        echo '</tr>'."\n".'        <tr>'."\n        ";
-      }
-      $seed = "extchkbx_{$e}_".md5(microtime() . mt_rand());
-      $chk = (!empty($allowed[$e])) ? ' checked="checked"' : '';
-      echo "  <td class='$cl'>\n            <label><input id='{$seed}' type='checkbox' name='ext_{$e}'{$chk} />.{$e}\n            ({$m})</label>\n          </td>\n        ";
-    }
-    while($c < 2)
-    {
-      $c++;
-      echo "  <td class='{$cl}'></td>\n        ";
-    }
-    echo '<tr><th class="subhead" colspan="3"><input type="submit" name="save" value="' . $lang->get('etc_save_changes') . '" /></th></tr>';
-    echo '</tr>'."\n".'      </table>'."\n".'    </div>';
-    echo '</form>';
-  ?>
-  <?php
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	global $mime_types, $mimetype_exps, $mimetype_extlist;
+	if(isset($_POST['save']) && !defined('ENANO_DEMO_MODE'))
+	{
+		$bits = '';
+		$keys = array_keys($mime_types);
+		foreach($keys as $i => $k)
+		{
+			if(isset($_POST['ext_'.$k])) $bits .= '1';
+			else $bits .= '0';
+		}
+		$bits = compress_bitfield($bits);
+		setConfig('allowed_mime_types', $bits);
+		echo '<div class="info-box">' . $lang->get('acpft_msg_saved') . '</div>';
+	}
+	else if ( isset($_POST['save']) && defined('ENANO_DEMO_MODE') )
+	{
+		echo '<div class="error-box">' . $lang->get('acpft_msg_demo_mode') . '</div>';
+	}
+	$allowed = fetch_allowed_extensions();
+	?>
+	<h3><?php echo $lang->get('acpft_heading_main'); ?></h3>
+ 	<p><?php echo $lang->get('acpft_hint'); ?></p>
+	<?php
+	acp_start_form();
+		$c = -1;
+		$t = -1;
+		$cl = 'row1';
+		echo "\n".'    <div class="tblholder">'."\n".'      <table cellspacing="1" cellpadding="2" style="margin: 0; padding: 0;" border="0">'."\n".'        <tr>'."\n        ";
+		ksort($mime_types);
+		foreach($mime_types as $e => $m)
+		{
+			$c++;
+			$t++;
+			if($c == 3)
+			{
+				$c = 0;
+				$cl = ( $cl == 'row1' ) ? 'row2' : 'row1';
+				echo '</tr>'."\n".'        <tr>'."\n        ";
+			}
+			$seed = "extchkbx_{$e}_".md5(microtime() . mt_rand());
+			$chk = (!empty($allowed[$e])) ? ' checked="checked"' : '';
+			echo "  <td class='$cl'>\n            <label><input id='{$seed}' type='checkbox' name='ext_{$e}'{$chk} />.{$e}\n            ({$m})</label>\n          </td>\n        ";
+		}
+		while($c < 2)
+		{
+			$c++;
+			echo "  <td class='{$cl}'></td>\n        ";
+		}
+		echo '<tr><th class="subhead" colspan="3"><input type="submit" name="save" value="' . $lang->get('etc_save_changes') . '" /></th></tr>';
+		echo '</tr>'."\n".'      </table>'."\n".'    </div>';
+		echo '</form>';
+	?>
+	<?php
 }
 
 function page_Admin_DBBackup()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  if ( ENANO_DBLAYER != 'MYSQL' )
-    die('<h3>' . $lang->get('acpdb_err_not_supported_title') . '</h3>
-          <p>' . $lang->get('acpdb_err_not_supported_desc') . '</p>');
-  
-  if(isset($_GET['submitting']) && $_GET['submitting'] == 'yes' && defined('ENANO_DEMO_MODE') )
-  {
-    redirect(makeUrlComplete('Special', 'Administration'), $lang->get('acpdb_err_demo_mode_title'), $lang->get('acpdb_err_demo_mode_desc'), 5);
-  }
-  
-  global $system_table_list;
-  if(isset($_GET['submitting']) && $_GET['submitting'] == 'yes')
-  {
-    
-    if(defined('SQL_BACKUP_CRYPT'))
-      // Try to increase our time limit
-      @set_time_limit(0);
-    // Do the actual export
-    $aesext = ( defined('SQL_BACKUP_CRYPT') ) ? '.tea' : '';
-    $filename = 'enano_backup_' . enano_date('ymd') . '.sql' . $aesext;
-    ob_start();
-    // Spew some headers
-    $headdate = enano_date(ED_DATE | ED_TIME);
-    echo <<<HEADER
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	if ( ENANO_DBLAYER != 'MYSQL' )
+		die('<h3>' . $lang->get('acpdb_err_not_supported_title') . '</h3>
+					<p>' . $lang->get('acpdb_err_not_supported_desc') . '</p>');
+	
+	if(isset($_GET['submitting']) && $_GET['submitting'] == 'yes' && defined('ENANO_DEMO_MODE') )
+	{
+		redirect(makeUrlComplete('Special', 'Administration'), $lang->get('acpdb_err_demo_mode_title'), $lang->get('acpdb_err_demo_mode_desc'), 5);
+	}
+	
+	global $system_table_list;
+	if(isset($_GET['submitting']) && $_GET['submitting'] == 'yes')
+	{
+		
+		if(defined('SQL_BACKUP_CRYPT'))
+			// Try to increase our time limit
+			@set_time_limit(0);
+		// Do the actual export
+		$aesext = ( defined('SQL_BACKUP_CRYPT') ) ? '.tea' : '';
+		$filename = 'enano_backup_' . enano_date('ymd') . '.sql' . $aesext;
+		ob_start();
+		// Spew some headers
+		$headdate = enano_date(ED_DATE | ED_TIME);
+		echo <<<HEADER
 -- Enano CMS SQL backup
 -- Generated on {$headdate} by {$session->username}
 
 HEADER;
-    // build the table list
-    $base = ( isset($_POST['do_system_tables']) ) ? $system_table_list : Array();
-    $add  = ( isset($_POST['additional_tables'])) ? $_POST['additional_tables'] : Array();
-    $tables = array_merge($base, $add);
-    
-    // Log it!
-    $e = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'db_backup\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($session->username).'\',' . $session->user_id . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', \'' . $db->escape(implode(', ', $tables)) . '\')');
-    if ( !$e )
-      $db->_die();
-    
-    foreach($tables as $i => $t)
-    {
-      if(!preg_match('#^([a-z0-9_]+)$#i', $t))
-        die('Hacking attempt');
-      // if($t == table_prefix.'files' && isset($_POST['do_data']))
-      //   unset($tables[$i]);
-    }
-    foreach($tables as $t)
-    {
-      // THE FOLLOWING COMMENT DOES NOT APPLY AS OF 1.0.
-      // Sorry folks - this script CAN'T backup enano_files and enano_search_index due to the sheer size of the tables.
-      // If encryption is enabled the log data will be excluded too.
-      $result = export_table(
-        $t,
-        isset($_POST['do_struct']),
-        ( isset($_POST['do_data']) ),
-        false
-        ) . "\n";
-      if ( !$result )
-      {
-        $db->_die();
-      }
-      echo $result;
-    }
-    $data = ob_get_contents();
-    ob_end_clean();
-    if(defined('SQL_BACKUP_CRYPT'))
-    {
-      // Free some memory, we don't need this stuff any more
-      $db->close();
-      unset($paths, $db, $template, $plugins);
-      $tea = new TEACrypt();
-      $data = $tea->encrypt($data, $session->private_key);
-    }
-    header('Content-disposition: attachment; filename='.$filename.'');
-    header('Content-type: application/octet-stream');
-    header('Content-length: '.strlen($data));
-    echo $data;
-    exit;
-  }
-  else
-  {
-    // Show the UI
-    echo '<form action="'.makeUrlNS('Admin', 'DBBackup', 'submitting=yes', true).'" method="post" enctype="multipart/form-data">';
-    ?>
-    <p><?php echo $lang->get('acpdb_intro'); ?></p>
-    <p><label><input type="checkbox" name="do_system_tables" checked="checked" /> <?php echo $lang->get('acpdb_lbl_system_tables'); ?></label><p>
-    <p><?php echo $lang->get('acpdb_lbl_additional_tables'); ?></p>
-    <p><select name="additional_tables[]" multiple="multiple">
-       <?php
-         if ( ENANO_DBLAYER == 'MYSQL' )
-         {
-           $q = $db->sql_query('SHOW TABLES;') or $db->_die('Somehow we were denied the request to get the list of tables.');
-         }
-         else if ( ENANO_DBLAYER == 'PGSQL' )
-         {
-           $q = $db->sql_query('SELECT relname FROM pg_stat_user_tables ORDER BY relname;') or $db->_die('Somehow we were denied the request to get the list of tables.');
-         }
-         while($row = $db->fetchrow_num())
-         {
-           if(!in_array($row[0], $system_table_list)) echo '<option value="'.$row[0].'">'.$row[0].'</option>';
-         }
-       ?>
-       </select>
-       </p>
-    <p><label><input type="checkbox" name="do_struct" checked="checked" /> <?php echo $lang->get('acpdb_lbl_include_structure'); ?></label><br />
-       <label><input type="checkbox" name="do_data"   checked="checked" /> <?php echo $lang->get('acpdb_lbl_include_data'); ?></label>
-       </p>
-    <p><input type="submit" value="<?php echo $lang->get('acpdb_btn_create_backup'); ?>" /></p>
-    <?php
-    echo '</form>';
-  }
+		// build the table list
+		$base = ( isset($_POST['do_system_tables']) ) ? $system_table_list : Array();
+		$add  = ( isset($_POST['additional_tables'])) ? $_POST['additional_tables'] : Array();
+		$tables = array_merge($base, $add);
+		
+		// Log it!
+		$e = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,date_string,author,author_uid,edit_summary,page_text) VALUES(\'security\', \'db_backup\', '.time().', \''.enano_date(ED_DATE | ED_TIME).'\', \''.$db->escape($session->username).'\',' . $session->user_id . ', \''.$db->escape($_SERVER['REMOTE_ADDR']).'\', \'' . $db->escape(implode(', ', $tables)) . '\')');
+		if ( !$e )
+			$db->_die();
+		
+		foreach($tables as $i => $t)
+		{
+			if(!preg_match('#^([a-z0-9_]+)$#i', $t))
+				die('Hacking attempt');
+			// if($t == table_prefix.'files' && isset($_POST['do_data']))
+			//   unset($tables[$i]);
+		}
+		foreach($tables as $t)
+		{
+			// THE FOLLOWING COMMENT DOES NOT APPLY AS OF 1.0.
+			// Sorry folks - this script CAN'T backup enano_files and enano_search_index due to the sheer size of the tables.
+			// If encryption is enabled the log data will be excluded too.
+			$result = export_table(
+				$t,
+				isset($_POST['do_struct']),
+				( isset($_POST['do_data']) ),
+				false
+				) . "\n";
+			if ( !$result )
+			{
+				$db->_die();
+			}
+			echo $result;
+		}
+		$data = ob_get_contents();
+		ob_end_clean();
+		if(defined('SQL_BACKUP_CRYPT'))
+		{
+			// Free some memory, we don't need this stuff any more
+			$db->close();
+			unset($paths, $db, $template, $plugins);
+			$tea = new TEACrypt();
+			$data = $tea->encrypt($data, $session->private_key);
+		}
+		header('Content-disposition: attachment; filename='.$filename.'');
+		header('Content-type: application/octet-stream');
+		header('Content-length: '.strlen($data));
+		echo $data;
+		exit;
+	}
+	else
+	{
+		// Show the UI
+		echo '<form action="'.makeUrlNS('Admin', 'DBBackup', 'submitting=yes', true).'" method="post" enctype="multipart/form-data">';
+		?>
+		<p><?php echo $lang->get('acpdb_intro'); ?></p>
+		<p><label><input type="checkbox" name="do_system_tables" checked="checked" /> <?php echo $lang->get('acpdb_lbl_system_tables'); ?></label><p>
+		<p><?php echo $lang->get('acpdb_lbl_additional_tables'); ?></p>
+		<p><select name="additional_tables[]" multiple="multiple">
+ 			<?php
+ 				if ( ENANO_DBLAYER == 'MYSQL' )
+ 				{
+ 					$q = $db->sql_query('SHOW TABLES;') or $db->_die('Somehow we were denied the request to get the list of tables.');
+ 				}
+ 				else if ( ENANO_DBLAYER == 'PGSQL' )
+ 				{
+ 					$q = $db->sql_query('SELECT relname FROM pg_stat_user_tables ORDER BY relname;') or $db->_die('Somehow we were denied the request to get the list of tables.');
+ 				}
+ 				while($row = $db->fetchrow_num())
+ 				{
+ 					if(!in_array($row[0], $system_table_list)) echo '<option value="'.$row[0].'">'.$row[0].'</option>';
+ 				}
+ 			?>
+ 			</select>
+ 			</p>
+		<p><label><input type="checkbox" name="do_struct" checked="checked" /> <?php echo $lang->get('acpdb_lbl_include_structure'); ?></label><br />
+ 			<label><input type="checkbox" name="do_data"   checked="checked" /> <?php echo $lang->get('acpdb_lbl_include_data'); ?></label>
+ 			</p>
+		<p><input type="submit" value="<?php echo $lang->get('acpdb_btn_create_backup'); ?>" /></p>
+		<?php
+		echo '</form>';
+	}
 }
 
 /*
@@ -1461,1191 +1461,1191 @@
 
 function page_Admin_COPPA()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  echo '<h2>' . $lang->get('acpcp_heading_main') . '</h2>';
-  echo '<p>
-          ' . $lang->get('acpcp_intro') . '
-        </p>';
-  
-  // Start form
-  
-  if ( isset($_POST['coppa_address']) )
-  {
-    // Saving changes
-    $enable_coppa = ( isset($_POST['enable_coppa']) ) ? '1' : '0';
-    setConfig('enable_coppa', $enable_coppa);
-    
-    $address = $_POST['coppa_address']; // RenderMan::preprocess_text($_POST['coppa_address'], true, false);
-    setConfig('coppa_address', $address);
-    
-    echo '<div class="info-box">' . $lang->get('acpcp_msg_save_success') . '</div>';
-  }
-  
-  acp_start_form();
-  
-  echo '<div class="tblholder">';
-  echo '<table border="0" cellspacing="1" cellpadding="4">';
-  echo '<tr>
-          <th colspan="2">
-            ' . $lang->get('acpcp_th_form') . '
-          </th>
-        </tr>';
-        
-  echo '<tr>
-          <td class="row1">
-            ' . $lang->get('acpcp_field_enable_title') . '
-          </td>
-          <td class="row2">
-            <label><input type="checkbox" name="enable_coppa" ' . ( ( getConfig('enable_coppa') == '1' ) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('acpcp_field_enable') . '</label><br />
-            <small>' . $lang->get('acpcp_field_enable_hint') . '</small>
-          </td>
-        </tr>';
-        
-  echo '<tr>
-          <td class="row1">
-            ' . $lang->get('acpcp_field_address') . '<br />
-            <small>' . $lang->get('acpcp_field_address_hint') . '</small>
-          </td>
-          <td class="row2">
-            <textarea name="coppa_address" rows="7" cols="40">' . getConfig('coppa_address') . '</textarea>
-          </td>
-        </tr>';
-        
-  echo '<tr>
-          <th colspan="2" class="subhead">
-            <input type="submit" value="' . $lang->get('etc_save_changes') . '" />
-          </th>
-        </tr>';
-        
-  echo '</table>';
-  
-  echo '</form>';
-  
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	echo '<h2>' . $lang->get('acpcp_heading_main') . '</h2>';
+	echo '<p>
+					' . $lang->get('acpcp_intro') . '
+				</p>';
+	
+	// Start form
+	
+	if ( isset($_POST['coppa_address']) )
+	{
+		// Saving changes
+		$enable_coppa = ( isset($_POST['enable_coppa']) ) ? '1' : '0';
+		setConfig('enable_coppa', $enable_coppa);
+		
+		$address = $_POST['coppa_address']; // RenderMan::preprocess_text($_POST['coppa_address'], true, false);
+		setConfig('coppa_address', $address);
+		
+		echo '<div class="info-box">' . $lang->get('acpcp_msg_save_success') . '</div>';
+	}
+	
+	acp_start_form();
+	
+	echo '<div class="tblholder">';
+	echo '<table border="0" cellspacing="1" cellpadding="4">';
+	echo '<tr>
+					<th colspan="2">
+						' . $lang->get('acpcp_th_form') . '
+					</th>
+				</tr>';
+				
+	echo '<tr>
+					<td class="row1">
+						' . $lang->get('acpcp_field_enable_title') . '
+					</td>
+					<td class="row2">
+						<label><input type="checkbox" name="enable_coppa" ' . ( ( getConfig('enable_coppa') == '1' ) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('acpcp_field_enable') . '</label><br />
+						<small>' . $lang->get('acpcp_field_enable_hint') . '</small>
+					</td>
+				</tr>';
+				
+	echo '<tr>
+					<td class="row1">
+						' . $lang->get('acpcp_field_address') . '<br />
+						<small>' . $lang->get('acpcp_field_address_hint') . '</small>
+					</td>
+					<td class="row2">
+						<textarea name="coppa_address" rows="7" cols="40">' . getConfig('coppa_address') . '</textarea>
+					</td>
+				</tr>';
+				
+	echo '<tr>
+					<th colspan="2" class="subhead">
+						<input type="submit" value="' . $lang->get('etc_save_changes') . '" />
+					</th>
+				</tr>';
+				
+	echo '</table>';
+	
+	echo '</form>';
+	
 }
 
 function page_Admin_MassEmail()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  global $enano_config;
-  if ( isset($_POST['do_send']) && !defined('ENANO_DEMO_MODE') )
-  {
-    $use_smtp = getConfig('smtp_enabled') == '1';
-    
-    //
-    // Let's do some checking to make sure that mass mail functions
-    // are working in win32 versions of php. (copied from phpBB)
-    //
-    if ( preg_match('/[c-z]:\\\.*/i', getenv('PATH')) && !$use_smtp)
-    {
-      $ini_val = ( @phpversion() >= '4.0.0' ) ? 'ini_get' : 'get_cfg_var';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	global $enano_config;
+	if ( isset($_POST['do_send']) && !defined('ENANO_DEMO_MODE') )
+	{
+		$use_smtp = getConfig('smtp_enabled') == '1';
+		
+		//
+		// Let's do some checking to make sure that mass mail functions
+		// are working in win32 versions of php. (copied from phpBB)
+		//
+		if ( preg_match('/[c-z]:\\\.*/i', getenv('PATH')) && !$use_smtp)
+		{
+			$ini_val = ( @phpversion() >= '4.0.0' ) ? 'ini_get' : 'get_cfg_var';
 
-      // We are running on windows, force delivery to use our smtp functions
-      // since php's are broken by default
-      $use_smtp = true;
-      $enano_config['smtp_server'] = @$ini_val('SMTP');
-    }
-    
-    $mail = new emailer( !empty($use_smtp) );
-    
-    // Validate subject/message body
-    $subject = stripslashes(trim($_POST['subject']));
-    $message = stripslashes(trim($_POST['message']));
-    
-    if ( empty($subject) )
-      $errors[] = $lang->get('acpmm_err_need_subject');
-    if ( empty($message) )
-      $errors[] = $lang->get('acpmm_err_need_message');
-    
-    // Get list of members
-    if ( !empty($_POST['userlist']) )
-    {
-      $userlist = str_replace(', ', ',', $_POST['userlist']);
-      $userlist = explode(',', $userlist);
-      foreach ( $userlist as $k => $u )
-      {
-        if ( $u == $session->username )
-        {
-          // Message is automatically sent to the sender
-          unset($userlist[$k]);
-        }
-        else
-        {
-          $userlist[$k] = $db->escape($u);
-        }
-      }
-      $userlist = 'WHERE username=\'' . implode('\' OR username=\'', $userlist) . '\'';
-      
-      $q = $db->sql_query('SELECT email FROM '.table_prefix.'users ' . $userlist . ';');
-      if ( !$q )
-        $db->_die();
-      
-      if ( $row = $db->fetchrow() )
-      {
-        do {
-          $mail->cc($row['email']);
-        } while ( $row = $db->fetchrow() );
-      }
-      
-      $db->free_result();
-      
-    }
-    else
-    {
-      // Sending to a usergroup
-      
-      $group_id = intval($_POST['group_id']);
-      if ( $group_id < 1 )
-      {
-        $errors[] = 'Invalid group ID';
-      }
-      else
-      {
-        $q = $db->sql_query('SELECT u.email FROM '.table_prefix.'group_members AS g
-                               LEFT JOIN '.table_prefix.'users AS u
-                                 ON (u.user_id=g.user_id)
-                               WHERE g.group_id=' . $group_id . ';');
-        if ( !$q )
-          $db->_die();
-        
-        if ( $row = $db->fetchrow() )
-        {
-          do {
-            $mail->cc($row['email']);
-          } while ( $row = $db->fetchrow() );
-        }
-        
-        $db->free_result();
-      }
-    }
-    
-    if ( sizeof($errors) < 1 )
-    {
-    
-      $mail->from(getConfig('contact_email'));
-      $mail->replyto(getConfig('contact_email'));
-      $mail->set_subject($subject);
-      $mail->email_address(getConfig('contact_email'));
-      
-      // Copied/modified from phpBB
-      $email_headers = 'X-AntiAbuse: Website server name - ' . $_SERVER['SERVER_NAME'] . "\n";
-      $email_headers .= 'X-AntiAbuse: User_id - ' . $session->user_id . "\n";
-      $email_headers .= 'X-AntiAbuse: Username - ' . $session->username . "\n";
-      $email_headers .= 'X-AntiAbuse: User IP - ' . $_SERVER['REMOTE_ADDR'] . "\n";
-      
-      $mail->extra_headers($email_headers);
-      
-      // FIXME: how to handle l10n with this?
-      $tpl = 'The following message was mass-mailed by {SENDER}, one of the administrators from {SITE_NAME}. If this message contains spam or any comments which you find abusive or offensive, please contact the administration team at:
-  
+			// We are running on windows, force delivery to use our smtp functions
+			// since php's are broken by default
+			$use_smtp = true;
+			$enano_config['smtp_server'] = @$ini_val('SMTP');
+		}
+		
+		$mail = new emailer( !empty($use_smtp) );
+		
+		// Validate subject/message body
+		$subject = stripslashes(trim($_POST['subject']));
+		$message = stripslashes(trim($_POST['message']));
+		
+		if ( empty($subject) )
+			$errors[] = $lang->get('acpmm_err_need_subject');
+		if ( empty($message) )
+			$errors[] = $lang->get('acpmm_err_need_message');
+		
+		// Get list of members
+		if ( !empty($_POST['userlist']) )
+		{
+			$userlist = str_replace(', ', ',', $_POST['userlist']);
+			$userlist = explode(',', $userlist);
+			foreach ( $userlist as $k => $u )
+			{
+				if ( $u == $session->username )
+				{
+					// Message is automatically sent to the sender
+					unset($userlist[$k]);
+				}
+				else
+				{
+					$userlist[$k] = $db->escape($u);
+				}
+			}
+			$userlist = 'WHERE username=\'' . implode('\' OR username=\'', $userlist) . '\'';
+			
+			$q = $db->sql_query('SELECT email FROM '.table_prefix.'users ' . $userlist . ';');
+			if ( !$q )
+				$db->_die();
+			
+			if ( $row = $db->fetchrow() )
+			{
+				do {
+					$mail->cc($row['email']);
+				} while ( $row = $db->fetchrow() );
+			}
+			
+			$db->free_result();
+			
+		}
+		else
+		{
+			// Sending to a usergroup
+			
+			$group_id = intval($_POST['group_id']);
+			if ( $group_id < 1 )
+			{
+				$errors[] = 'Invalid group ID';
+			}
+			else
+			{
+				$q = $db->sql_query('SELECT u.email FROM '.table_prefix.'group_members AS g
+ 															LEFT JOIN '.table_prefix.'users AS u
+ 																ON (u.user_id=g.user_id)
+ 															WHERE g.group_id=' . $group_id . ';');
+				if ( !$q )
+					$db->_die();
+				
+				if ( $row = $db->fetchrow() )
+				{
+					do {
+						$mail->cc($row['email']);
+					} while ( $row = $db->fetchrow() );
+				}
+				
+				$db->free_result();
+			}
+		}
+		
+		if ( sizeof($errors) < 1 )
+		{
+		
+			$mail->from(getConfig('contact_email'));
+			$mail->replyto(getConfig('contact_email'));
+			$mail->set_subject($subject);
+			$mail->email_address(getConfig('contact_email'));
+			
+			// Copied/modified from phpBB
+			$email_headers = 'X-AntiAbuse: Website server name - ' . $_SERVER['SERVER_NAME'] . "\n";
+			$email_headers .= 'X-AntiAbuse: User_id - ' . $session->user_id . "\n";
+			$email_headers .= 'X-AntiAbuse: Username - ' . $session->username . "\n";
+			$email_headers .= 'X-AntiAbuse: User IP - ' . $_SERVER['REMOTE_ADDR'] . "\n";
+			
+			$mail->extra_headers($email_headers);
+			
+			// FIXME: how to handle l10n with this?
+			$tpl = 'The following message was mass-mailed by {SENDER}, one of the administrators from {SITE_NAME}. If this message contains spam or any comments which you find abusive or offensive, please contact the administration team at:
+	
 {CONTACT_EMAIL}
 
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 {MESSAGE}
 ';
-  
-      $mail->use_template($tpl);
-      
-      $mail->assign_vars(array(
-          'SENDER' => $session->username,
-          'SITE_NAME' => getConfig('site_name'),
-          'CONTACT_EMAIL' => getConfig('contact_email'),
-          'MESSAGE' => $message
-        ));
-      
-      //echo '<pre>'.print_r($mail,true).'</pre>';
-      
-      // All done
-      $mail->send();
-      $mail->reset();
-      
-      echo '<div class="info-box">' . $lang->get('acpmm_msg_send_success') . '</div>';
-      
-    }
-    else
-    {
-      echo '<div class="warning-box">' . $lang->get('acpmm_err_send_fail') . '<ul><li>' . implode('</li><li>', $errors) . '</li></ul></div>';
-    }
-    
-  }
-  else if ( isset($_POST['do_send']) && defined('ENANO_DEMO_MODE') )
-  {
-    echo '<div class="error-box">' . $lang->get('acpmm_err_demo') . '</div>';
-  }
-  acp_start_form();
-  ?>
-  <div class="tblholder">
-    <table border="0" cellspacing="1" cellpadding="4">
-      <tr>
-        <th colspan="2"><?php echo $lang->get('acpmm_heading_main'); ?></th>
-      </tr>
-      <tr>
-        <td class="row2" rowspan="2" style="width: 30%; min-width: 200px;">
-          <?php echo $lang->get('acpmm_field_group_to'); ?><br />
-          <small>
-            <?php echo $lang->get('acpmm_field_group_to_hint'); ?>
-          </small>
-        </td>
-        <td class="row1">
-          <select name="group_id">
-            <?php
-            $q = $db->sql_query('SELECT group_name,group_id FROM '.table_prefix.'groups ORDER BY group_name ASC;');
-            if ( !$q )
-              $db->_die();
-            while ( $row = $db->fetchrow() )
-            {
-              list($g_name) = array_values($row);
-              $g_name_langstr = 'groupcp_grp_' . strtolower($g_name);
-              if ( ($g_langstr = $lang->get($g_name_langstr)) != $g_name_langstr )
-              {
-                $g_name = $g_langstr;
-              }
-              echo '<option value="' . $row['group_id'] . '">' . htmlspecialchars($g_name) . '</option>';
-            }
-            ?>
-          </select>
-        </td>
-      </tr>
-      <tr>
-        <td class="row1">
-          <?php echo $lang->get('acpmm_field_username'); ?> <input type="text" name="userlist" size="50" />
-        </td>
-      </tr>
-      <tr>
-        <td class="row2" style="width: 30%; min-width: 200px;">
-          <?php echo $lang->get('acpmm_field_subject'); ?>
-        </td>
-        <td class="row1">
-          <input name="subject" type="text" size="50" />
-        </td>
-      </tr>
-      <tr>
-        <td class="row2"  style="width: 30%; min-width: 200px;">
-          <?php echo $lang->get('acpmm_field_message'); ?>
-        </td>
-        <td class="row1">
-          <textarea name="message" rows="30" cols="60" style="width: 100%;"></textarea>
-        </td>
-      </tr>
-      <tr>
-        <th class="subhead" colspan="2" style="text-align: left;" valign="middle">
-          <div style="float: right;"><input type="submit" name="do_send" value="<?php echo $lang->get('acpmm_btn_send'); ?>" /></div>
-          <small style="font-weight: normal;"><?php echo $lang->get('acpmm_msg_send_takeawhile'); ?></small>
-        </th>
-      </tr>
-      
-    </table>
-  </div>
-  <?php
-  echo '</form>';
+	
+			$mail->use_template($tpl);
+			
+			$mail->assign_vars(array(
+					'SENDER' => $session->username,
+					'SITE_NAME' => getConfig('site_name'),
+					'CONTACT_EMAIL' => getConfig('contact_email'),
+					'MESSAGE' => $message
+				));
+			
+			//echo '<pre>'.print_r($mail,true).'</pre>';
+			
+			// All done
+			$mail->send();
+			$mail->reset();
+			
+			echo '<div class="info-box">' . $lang->get('acpmm_msg_send_success') . '</div>';
+			
+		}
+		else
+		{
+			echo '<div class="warning-box">' . $lang->get('acpmm_err_send_fail') . '<ul><li>' . implode('</li><li>', $errors) . '</li></ul></div>';
+		}
+		
+	}
+	else if ( isset($_POST['do_send']) && defined('ENANO_DEMO_MODE') )
+	{
+		echo '<div class="error-box">' . $lang->get('acpmm_err_demo') . '</div>';
+	}
+	acp_start_form();
+	?>
+	<div class="tblholder">
+		<table border="0" cellspacing="1" cellpadding="4">
+			<tr>
+				<th colspan="2"><?php echo $lang->get('acpmm_heading_main'); ?></th>
+			</tr>
+			<tr>
+				<td class="row2" rowspan="2" style="width: 30%; min-width: 200px;">
+					<?php echo $lang->get('acpmm_field_group_to'); ?><br />
+					<small>
+						<?php echo $lang->get('acpmm_field_group_to_hint'); ?>
+					</small>
+				</td>
+				<td class="row1">
+					<select name="group_id">
+						<?php
+						$q = $db->sql_query('SELECT group_name,group_id FROM '.table_prefix.'groups ORDER BY group_name ASC;');
+						if ( !$q )
+							$db->_die();
+						while ( $row = $db->fetchrow() )
+						{
+							list($g_name) = array_values($row);
+							$g_name_langstr = 'groupcp_grp_' . strtolower($g_name);
+							if ( ($g_langstr = $lang->get($g_name_langstr)) != $g_name_langstr )
+							{
+								$g_name = $g_langstr;
+							}
+							echo '<option value="' . $row['group_id'] . '">' . htmlspecialchars($g_name) . '</option>';
+						}
+						?>
+					</select>
+				</td>
+			</tr>
+			<tr>
+				<td class="row1">
+					<?php echo $lang->get('acpmm_field_username'); ?> <input type="text" name="userlist" size="50" />
+				</td>
+			</tr>
+			<tr>
+				<td class="row2" style="width: 30%; min-width: 200px;">
+					<?php echo $lang->get('acpmm_field_subject'); ?>
+				</td>
+				<td class="row1">
+					<input name="subject" type="text" size="50" />
+				</td>
+			</tr>
+			<tr>
+				<td class="row2"  style="width: 30%; min-width: 200px;">
+					<?php echo $lang->get('acpmm_field_message'); ?>
+				</td>
+				<td class="row1">
+					<textarea name="message" rows="30" cols="60" style="width: 100%;"></textarea>
+				</td>
+			</tr>
+			<tr>
+				<th class="subhead" colspan="2" style="text-align: left;" valign="middle">
+					<div style="float: right;"><input type="submit" name="do_send" value="<?php echo $lang->get('acpmm_btn_send'); ?>" /></div>
+					<small style="font-weight: normal;"><?php echo $lang->get('acpmm_msg_send_takeawhile'); ?></small>
+				</th>
+			</tr>
+			
+		</table>
+	</div>
+	<?php
+	echo '</form>';
 }
 
 function page_Admin_BanControl()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  if(isset($_GET['action']) && $_GET['action'] == 'delete' && isset($_GET['id']) && $_GET['id'] != '')
-  {
-    $e = $db->sql_query('DELETE FROM '.table_prefix.'banlist WHERE ban_id=' . intval($_GET['id']) . '');
-    if ( !$e )
-      $db->_die('The ban list entry was not deleted.');
-  }
-  if(isset($_POST['create']) && !defined('ENANO_DEMO_MODE'))
-  {
-    $type = intval($_POST['type']);
-    $value = trim($_POST['value']);
-    if ( !in_array($type, array(BAN_IP, BAN_USER, BAN_EMAIL)) )
-    {
-      echo '<div class="error-box">Hacking attempt.</div>';
-    }
-    else if ( empty($value) )
-    {
-      echo '<div class="error-box">' . $lang->get('acpbc_err_empty') . '</div>';
-    }
-    else
-    {
-      $entries = array();
-      $input = explode(',', $_POST['value']);
-      $error = false;
-      foreach ( $input as $entry )
-      {
-        $entry = trim($entry);
-        if ( empty($entry) )
-        {
-          echo '<div class="error-box">' . $lang->get('acpbc_err_invalid_ip_range') . '</div>';
-          $error = true;
-          break;
-        }
-        if ( $type == BAN_IP )
-        {
-          if ( !isset($_POST['regex']) )
-          {
-            // as of 1.0.2 parsing is done at runtime
-            $entries[] = $entry;
-          }
-          else
-          {
-            $entries[] = $entry;
-          }
-        }
-        else
-        {
-          $entries[] = $entry;
-        }
-      }
-      if ( !$error )
-      {
-        $regex = ( isset($_POST['regex']) ) ? '1' : '0';
-        $to_insert = array();                                                         
-        $reason = $db->escape($_POST['reason']);
-        foreach ( $entries as $entry )
-        {
-          $entry = $db->escape($entry);
-          $to_insert[] = "($type, '$entry', '$reason', $regex)";
-        }
-        $q = 'INSERT INTO '.table_prefix."banlist(ban_type, ban_value, reason, is_regex)\n  VALUES" . implode(",\n  ", $to_insert) . ';';
-        @set_time_limit(0);
-        $e = $db->sql_query($q);
-        if(!$e) $db->_die('The banlist could not be updated.');
-      }
-    }
-  }
-  else if ( isset($_POST['create']) && defined('ENANO_DEMO_MODE') )
-  {
-    echo '<div class="error-box">' . $lang->get('acpbc_err_demo', array('ban_target' => htmlspecialchars($_POST['value']))) . '</div>';
-  }
-  $q = $db->sql_query('SELECT ban_id,ban_type,ban_value,is_regex FROM '.table_prefix.'banlist ORDER BY ban_type;');
-  if ( !$q )
-    $db->_die('The banlist data could not be selected.');
-  echo '<div class="tblholder" style="max-height: 800px; clip: rect(0px,auto,auto,0px); overflow: auto;">
-          <table border="0" cellspacing="1" cellpadding="4">';
-  echo '<tr>
-          <th>' . $lang->get('acpbc_col_type') . '</th>
-          <th>' . $lang->get('acpbc_col_value') . '</th>
-          <th>' . $lang->get('acpbc_col_regex') . '</th>
-          <th></th>
-        </tr>';
-  if ( $db->numrows() < 1 )
-  {
-    echo '<td class="row1" colspan="4">' . $lang->get('acpbc_msg_no_rules') . '</td>';
-  }
-  $cls = 'row2';
-  while ( $r = $db->fetchrow() )
-  {
-    $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-    if ( $r['ban_type'] == BAN_IP )
-      $t = $lang->get('acpbc_ban_type_ip');
-    else if ( $r['ban_type'] == BAN_USER )
-      $t = $lang->get('acpbc_ban_type_username');
-    else if ( $r['ban_type'] == BAN_EMAIL )
-      $t = $lang->get('acpbc_ban_type_email');
-    $g = ( $r['is_regex'] ) ? '<b>' . $lang->get('acpbc_ban_regex_yes') . '</b>' : $lang->get('acpbc_ban_regex_no');
-    echo '<tr>
-            <td class="'.$cls.'">'.$t.'</td>
-            <td class="'.$cls.'">'.htmlspecialchars($r['ban_value']).'</td>
-            <td class="'.$cls.'">'.$g.'</td>
-            <td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'BanControl&amp;action=delete&amp;id='.$r['ban_id']).'">' . $lang->get('acpbc_btn_delete') . '</a></td>
-          </tr>';
-  }
-  $db->free_result();
-  echo '</table></div>';
-  echo '<h3>' . $lang->get('acpbc_heading_create_new') . '</h3>';
-  acp_start_form();
-  ?>
-  
-  <?php echo $lang->get('acpbc_field_type'); ?>
-    <select name="type">
-      <option value="<?php echo BAN_IP; ?>"><?php echo $lang->get('acpbc_ban_type_ip'); ?></option>
-      <option value="<?php echo BAN_USER; ?>"><?php echo $lang->get('acpbc_ban_type_username'); ?></option>
-      <option value="<?php echo BAN_EMAIL; ?>"><?php echo $lang->get('acpbc_ban_type_email'); ?></option>
-    </select>
-    <br />
-    
-  <?php echo $lang->get('acpbc_field_rule'); ?>
-    <input type="text" name="value" size="30" /><br />
-    <small><?php echo $lang->get('acpbc_field_rule_hint'); ?></small><br />
-    
-  <?php echo $lang->get('acpbc_field_reason'); ?>
-    <textarea name="reason" rows="7" cols="40"></textarea><br />
-    
-  <label><input type="checkbox" name="regex" id="regex" /> <?php echo $lang->get('acpbc_field_regex'); ?></label>
-    <?php echo $lang->get('acpbc_field_regex_hint'); ?><br />
-    
-  <input type="submit" style="font-weight: bold;" name="create" value="<?php echo $lang->get('acpbc_btn_create'); ?>" />
-  <?php
-  echo '</form>';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	if(isset($_GET['action']) && $_GET['action'] == 'delete' && isset($_GET['id']) && $_GET['id'] != '')
+	{
+		$e = $db->sql_query('DELETE FROM '.table_prefix.'banlist WHERE ban_id=' . intval($_GET['id']) . '');
+		if ( !$e )
+			$db->_die('The ban list entry was not deleted.');
+	}
+	if(isset($_POST['create']) && !defined('ENANO_DEMO_MODE'))
+	{
+		$type = intval($_POST['type']);
+		$value = trim($_POST['value']);
+		if ( !in_array($type, array(BAN_IP, BAN_USER, BAN_EMAIL)) )
+		{
+			echo '<div class="error-box">Hacking attempt.</div>';
+		}
+		else if ( empty($value) )
+		{
+			echo '<div class="error-box">' . $lang->get('acpbc_err_empty') . '</div>';
+		}
+		else
+		{
+			$entries = array();
+			$input = explode(',', $_POST['value']);
+			$error = false;
+			foreach ( $input as $entry )
+			{
+				$entry = trim($entry);
+				if ( empty($entry) )
+				{
+					echo '<div class="error-box">' . $lang->get('acpbc_err_invalid_ip_range') . '</div>';
+					$error = true;
+					break;
+				}
+				if ( $type == BAN_IP )
+				{
+					if ( !isset($_POST['regex']) )
+					{
+						// as of 1.0.2 parsing is done at runtime
+						$entries[] = $entry;
+					}
+					else
+					{
+						$entries[] = $entry;
+					}
+				}
+				else
+				{
+					$entries[] = $entry;
+				}
+			}
+			if ( !$error )
+			{
+				$regex = ( isset($_POST['regex']) ) ? '1' : '0';
+				$to_insert = array();                                                         
+				$reason = $db->escape($_POST['reason']);
+				foreach ( $entries as $entry )
+				{
+					$entry = $db->escape($entry);
+					$to_insert[] = "($type, '$entry', '$reason', $regex)";
+				}
+				$q = 'INSERT INTO '.table_prefix."banlist(ban_type, ban_value, reason, is_regex)\n  VALUES" . implode(",\n  ", $to_insert) . ';';
+				@set_time_limit(0);
+				$e = $db->sql_query($q);
+				if(!$e) $db->_die('The banlist could not be updated.');
+			}
+		}
+	}
+	else if ( isset($_POST['create']) && defined('ENANO_DEMO_MODE') )
+	{
+		echo '<div class="error-box">' . $lang->get('acpbc_err_demo', array('ban_target' => htmlspecialchars($_POST['value']))) . '</div>';
+	}
+	$q = $db->sql_query('SELECT ban_id,ban_type,ban_value,is_regex FROM '.table_prefix.'banlist ORDER BY ban_type;');
+	if ( !$q )
+		$db->_die('The banlist data could not be selected.');
+	echo '<div class="tblholder" style="max-height: 800px; clip: rect(0px,auto,auto,0px); overflow: auto;">
+					<table border="0" cellspacing="1" cellpadding="4">';
+	echo '<tr>
+					<th>' . $lang->get('acpbc_col_type') . '</th>
+					<th>' . $lang->get('acpbc_col_value') . '</th>
+					<th>' . $lang->get('acpbc_col_regex') . '</th>
+					<th></th>
+				</tr>';
+	if ( $db->numrows() < 1 )
+	{
+		echo '<td class="row1" colspan="4">' . $lang->get('acpbc_msg_no_rules') . '</td>';
+	}
+	$cls = 'row2';
+	while ( $r = $db->fetchrow() )
+	{
+		$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+		if ( $r['ban_type'] == BAN_IP )
+			$t = $lang->get('acpbc_ban_type_ip');
+		else if ( $r['ban_type'] == BAN_USER )
+			$t = $lang->get('acpbc_ban_type_username');
+		else if ( $r['ban_type'] == BAN_EMAIL )
+			$t = $lang->get('acpbc_ban_type_email');
+		$g = ( $r['is_regex'] ) ? '<b>' . $lang->get('acpbc_ban_regex_yes') . '</b>' : $lang->get('acpbc_ban_regex_no');
+		echo '<tr>
+						<td class="'.$cls.'">'.$t.'</td>
+						<td class="'.$cls.'">'.htmlspecialchars($r['ban_value']).'</td>
+						<td class="'.$cls.'">'.$g.'</td>
+						<td class="'.$cls.'"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'BanControl&amp;action=delete&amp;id='.$r['ban_id']).'">' . $lang->get('acpbc_btn_delete') . '</a></td>
+					</tr>';
+	}
+	$db->free_result();
+	echo '</table></div>';
+	echo '<h3>' . $lang->get('acpbc_heading_create_new') . '</h3>';
+	acp_start_form();
+	?>
+	
+	<?php echo $lang->get('acpbc_field_type'); ?>
+		<select name="type">
+			<option value="<?php echo BAN_IP; ?>"><?php echo $lang->get('acpbc_ban_type_ip'); ?></option>
+			<option value="<?php echo BAN_USER; ?>"><?php echo $lang->get('acpbc_ban_type_username'); ?></option>
+			<option value="<?php echo BAN_EMAIL; ?>"><?php echo $lang->get('acpbc_ban_type_email'); ?></option>
+		</select>
+		<br />
+		
+	<?php echo $lang->get('acpbc_field_rule'); ?>
+		<input type="text" name="value" size="30" /><br />
+		<small><?php echo $lang->get('acpbc_field_rule_hint'); ?></small><br />
+		
+	<?php echo $lang->get('acpbc_field_reason'); ?>
+		<textarea name="reason" rows="7" cols="40"></textarea><br />
+		
+	<label><input type="checkbox" name="regex" id="regex" /> <?php echo $lang->get('acpbc_field_regex'); ?></label>
+		<?php echo $lang->get('acpbc_field_regex_hint'); ?><br />
+		
+	<input type="submit" style="font-weight: bold;" name="create" value="<?php echo $lang->get('acpbc_btn_create'); ?>" />
+	<?php
+	echo '</form>';
 }
 
 function page_Admin_AdminLogout()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  $session->logout(USER_LEVEL_ADMIN);
-  echo '<h3>' . $lang->get('acplo_heading_main') . '</h3>
-         <p>' . $lang->get('acplo_msg_logout_complete', array('mainpage_link' => makeUrl(get_main_page()))) . '</p>';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	$session->logout(USER_LEVEL_ADMIN);
+	echo '<h3>' . $lang->get('acplo_heading_main') . '</h3>
+ 				<p>' . $lang->get('acplo_msg_logout_complete', array('mainpage_link' => makeUrl(get_main_page()))) . '</p>';
 }
 
 function page_Special_Administration()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $output;
-  
-  if ( $session->auth_level < USER_LEVEL_ADMIN )
-  {
-    $query_string = 'level=' . USER_LEVEL_ADMIN;
-    if ( !empty($_SERVER['QUERY_STRING']) )
-    {
-      $query_string .= '&' . trim(preg_replace('/(?:&|^)title=.+?(?:&|$)/', '&', $_SERVER['QUERY_STRING']), '&');
-    }
-    redirect(makeUrlNS('Special', 'Login/'.$paths->page, $query_string), 'Not authorized', 'You need an authorization level of '.USER_LEVEL_ADMIN.' to use this page, your auth level is: ' . $session->auth_level, 0);
-    exit;
-  }
-  else
-  {
-    $template->set_theme('admin', 'default');
-    $template->preload_js('fat');
-    $template->preload_js('ajax');
-    $template->preload_js('l10n');
-    $template->preload_js('jquery');
-    $template->preload_js('jquery-ui');
-    $template->preload_js('autofill');
-    $template->preload_js('admin-menu');
-    
-    $output->header();
-    
-    echo $lang->get('adm_page_tagline');
-    ?>
-    <script type="text/javascript">
-    function ajaxPage(t, qs)
-    {
-      if ( KILL_SWITCH )
-      {
-        document.getElementById('ajaxPageContainer').innerHTML = '<div class="error-box">Because of the lack of AJAX support, support for Internet Explorer versions less than 6.0 has been disabled in Runt. You can download and use Mozilla Firefox (or Seamonkey under Windows 95); both have an up-to-date standards-compliant rendering engine that has been tested thoroughly with Enano.</div>';
-        return false;
-      }
-      if ( t == namespace_list.Admin + 'AdminLogout' )
-      {
-        load_component('messagebox');
-        miniPromptMessage({
-            title: $lang.get('user_logout_confirm_title_elev'),
-            message: $lang.get('user_logout_confirm_body_elev'),
-            buttons: [
-              {
-                text: $lang.get('user_logout_confirm_btn_logout'),
-                color: 'red',
-                style: {
-                  fontWeight: 'bold'
-                },
-                onclick: function()
-                {
-                  var tigraentry = document.getElementById('i_div0_0').parentNode;
-                  var tigraobj = $dynano(tigraentry);
-                  var div = document.createElement('div');
-                  div.style.backgroundColor = '#FFFFFF';
-                  domObjChangeOpac(70, div);
-                  div.style.position = 'absolute';
-                  var top = tigraobj.Top();
-                  var left = tigraobj.Left();
-                  var width = tigraobj.Width();
-                  var height = tigraobj.Height();
-                  div.style.top = top + 'px';
-                  div.style.left = left + 'px';
-                  div.style.width = width + 'px';
-                  div.style.height = height + 'px';
-                  var body = document.getElementsByTagName('body')[0];
-                  miniPromptDestroy(this);
-                  body.appendChild(div);
-                  ajaxPageBin(namespace_list.Admin + 'AdminLogout');
-                }
-              },
-              {
-                text: $lang.get('etc_cancel'),
-                onclick: function()
-                {
-                  miniPromptDestroy(this);
-                }
-              }
-            ]
-          });
-        return;
-      }
-      ajaxPageBin(t, qs);
-    }
-    function ajaxPageBin(t, qs)
-    {
-      if ( KILL_SWITCH )
-      {
-        document.getElementById('ajaxPageContainer').innerHTML = '<div class="error-box">Because of the lack of AJAX support, support for Internet Explorer versions less than 6.0 has been disabled in Runt. You can download and use Mozilla Firefox (or Seamonkey under Windows 95); both have an up-to-date standards-compliant rendering engine that has been tested thoroughly with Enano.</div>';
-        return false;
-      }
-      document.getElementById('ajaxPageContainer').innerHTML = '<div class="wait-box">Loading page...</div>';
-      qs = qs ? '&' + qs : '';
-      ajaxGet(makeUrl(t, 'noheaders' + qs), function(ajax)
-        {
-          if ( ajax.readyState == 4 && ajax.status == 200 )
-          {
-            var response = String(ajax.responseText + '');
-            if ( check_json_response(response) )
-            {
-              response = parseJSON(response);
-              if ( response.mode == 'error' )
-              {
-                if ( response.error == 'need_auth_to_admin' )
-                {
-                  load_component('login');
-                  ajaxDynamicReauth(t);
-                }
-                else
-                {
-                  alert(response.error);
-                }
-              }
-            }
-            else
-            {
-              document.getElementById('ajaxPageContainer').innerHTML = ajax.responseText;
-              fadeInfoBoxes();
-              autofill_onload();
-              admin_table_onload(t);
-              // allow JS hooks
-              eval(setHook('admin_page_onload'));
-            }
-          }
-        });
-    }
-    <?php
-    if ( !isset($_GET['module']) )
-    {
-      echo <<<EOF
-    var _enanoAdminOnload = function() { ajaxPage('{$paths->nslist['Admin']}Home'); };
-    addOnloadHook(_enanoAdminOnload);
-    
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $output;
+	
+	if ( $session->auth_level < USER_LEVEL_ADMIN )
+	{
+		$query_string = 'level=' . USER_LEVEL_ADMIN;
+		if ( !empty($_SERVER['QUERY_STRING']) )
+		{
+			$query_string .= '&' . trim(preg_replace('/(?:&|^)title=.+?(?:&|$)/', '&', $_SERVER['QUERY_STRING']), '&');
+		}
+		redirect(makeUrlNS('Special', 'Login/'.$paths->page, $query_string), 'Not authorized', 'You need an authorization level of '.USER_LEVEL_ADMIN.' to use this page, your auth level is: ' . $session->auth_level, 0);
+		exit;
+	}
+	else
+	{
+		$template->set_theme('admin', 'default');
+		$template->preload_js('fat');
+		$template->preload_js('ajax');
+		$template->preload_js('l10n');
+		$template->preload_js('jquery');
+		$template->preload_js('jquery-ui');
+		$template->preload_js('autofill');
+		$template->preload_js('admin-menu');
+		
+		$output->header();
+		
+		echo $lang->get('adm_page_tagline');
+		?>
+		<script type="text/javascript">
+		function ajaxPage(t, qs)
+		{
+			if ( KILL_SWITCH )
+			{
+				document.getElementById('ajaxPageContainer').innerHTML = '<div class="error-box">Because of the lack of AJAX support, support for Internet Explorer versions less than 6.0 has been disabled in Runt. You can download and use Mozilla Firefox (or Seamonkey under Windows 95); both have an up-to-date standards-compliant rendering engine that has been tested thoroughly with Enano.</div>';
+				return false;
+			}
+			if ( t == namespace_list.Admin + 'AdminLogout' )
+			{
+				load_component('messagebox');
+				miniPromptMessage({
+						title: $lang.get('user_logout_confirm_title_elev'),
+						message: $lang.get('user_logout_confirm_body_elev'),
+						buttons: [
+							{
+								text: $lang.get('user_logout_confirm_btn_logout'),
+								color: 'red',
+								style: {
+									fontWeight: 'bold'
+								},
+								onclick: function()
+								{
+									var tigraentry = document.getElementById('i_div0_0').parentNode;
+									var tigraobj = $dynano(tigraentry);
+									var div = document.createElement('div');
+									div.style.backgroundColor = '#FFFFFF';
+									domObjChangeOpac(70, div);
+									div.style.position = 'absolute';
+									var top = tigraobj.Top();
+									var left = tigraobj.Left();
+									var width = tigraobj.Width();
+									var height = tigraobj.Height();
+									div.style.top = top + 'px';
+									div.style.left = left + 'px';
+									div.style.width = width + 'px';
+									div.style.height = height + 'px';
+									var body = document.getElementsByTagName('body')[0];
+									miniPromptDestroy(this);
+									body.appendChild(div);
+									ajaxPageBin(namespace_list.Admin + 'AdminLogout');
+								}
+							},
+							{
+								text: $lang.get('etc_cancel'),
+								onclick: function()
+								{
+									miniPromptDestroy(this);
+								}
+							}
+						]
+					});
+				return;
+			}
+			ajaxPageBin(t, qs);
+		}
+		function ajaxPageBin(t, qs)
+		{
+			if ( KILL_SWITCH )
+			{
+				document.getElementById('ajaxPageContainer').innerHTML = '<div class="error-box">Because of the lack of AJAX support, support for Internet Explorer versions less than 6.0 has been disabled in Runt. You can download and use Mozilla Firefox (or Seamonkey under Windows 95); both have an up-to-date standards-compliant rendering engine that has been tested thoroughly with Enano.</div>';
+				return false;
+			}
+			document.getElementById('ajaxPageContainer').innerHTML = '<div class="wait-box">Loading page...</div>';
+			qs = qs ? '&' + qs : '';
+			ajaxGet(makeUrl(t, 'noheaders' + qs), function(ajax)
+				{
+					if ( ajax.readyState == 4 && ajax.status == 200 )
+					{
+						var response = String(ajax.responseText + '');
+						if ( check_json_response(response) )
+						{
+							response = parseJSON(response);
+							if ( response.mode == 'error' )
+							{
+								if ( response.error == 'need_auth_to_admin' )
+								{
+									load_component('login');
+									ajaxDynamicReauth(t);
+								}
+								else
+								{
+									alert(response.error);
+								}
+							}
+						}
+						else
+						{
+							document.getElementById('ajaxPageContainer').innerHTML = ajax.responseText;
+							fadeInfoBoxes();
+							autofill_onload();
+							admin_table_onload(t);
+							// allow JS hooks
+							eval(setHook('admin_page_onload'));
+						}
+					}
+				});
+		}
+		<?php
+		if ( !isset($_GET['module']) )
+		{
+			echo <<<EOF
+		var _enanoAdminOnload = function() { ajaxPage('{$paths->nslist['Admin']}Home'); };
+		addOnloadHook(_enanoAdminOnload);
+		
 EOF;
-    }
-    ?>
-    var TREE_TPL = {
-      'target'  : '_self',  // name of the frame links will be opened in
-                  // other possible values are: _blank, _parent, _search, _self and _top
-    
-      'icon_e'  : '<?php echo cdnPath; ?>/images/icons/empty.gif',      // empty image
-      'icon_l'  : '<?php echo cdnPath; ?>/images/icons/line.gif',       // vertical line
-      'icon_32' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root leaf icon normal
-      'icon_36' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root leaf icon selected
-      'icon_48' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root icon normal
-      'icon_52' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root icon selected
-      'icon_56' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root icon opened
-      'icon_60' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root icon selected
-      'icon_16' : '<?php echo cdnPath; ?>/images/spacer.gif',           // node icon normal
-      'icon_20' : '<?php echo cdnPath; ?>/images/spacer.gif',           // node icon selected
-      'icon_24' : '<?php echo cdnPath; ?>/images/spacer.gif',           // node icon opened
-      'icon_28' : '<?php echo cdnPath; ?>/images/spacer.gif',           // node icon selected opened
-      'icon_0'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon normal
-      'icon_4'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon selected
-      'icon_8'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon opened
-      'icon_12' : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon selected
-      'icon_2'  : '<?php echo cdnPath; ?>/images/icons/joinbottom.gif', // junction for leaf
-      'icon_3'  : '<?php echo cdnPath; ?>/images/icons/join.gif',       // junction for last leaf
-      'icon_18' : '<?php echo cdnPath; ?>/images/icons/plusbottom.gif', // junction for closed node
-      'icon_19' : '<?php echo cdnPath; ?>/images/icons/plus.gif',       // junction for last closed node
-      'icon_26' : '<?php echo cdnPath; ?>/images/icons/minusbottom.gif',// junction for opened node
-      'icon_27' : '<?php echo cdnPath; ?>/images/icons/minus.gif'       // junction for last opended node
-    };
-    
-    <?php
-    echo $paths->parseAdminTree(); // Make a Javascript array that defines the tree
-    ?>
-    
-    addOnloadHook(function()
-      {
-        new tree(TREE_ITEMS, TREE_TPL, 'admin_tree');
-        keepalive_onload();
-      });
-    </script>
-    <table border="0" width="100%">
-      <tr>
-        <td class="holder" valign="top">
-          <div class="pad" style="padding-right: 20px;" id="admin_tree">
-          </div>
-        </td>
-        <td width="100%" valign="top">
-          <div class="pad" id="ajaxPageContainer">
-          <?php
-          if ( isset($_GET['module']) ) 
-          {
-            list($module) = explode('/', $_GET['module']);
-            list($page_id, $namespace) = RenderMan::strToPageID($module);
-            if ( $namespace != 'Admin' )
-            {
-              echo '<div class="error-box">Module must be in the Admin namespace</div>';
-            }
-            else
-            {
-              $paths->fullpage = $_GET['module'];
-              $paths->cpage['module'] = $_GET['module'];
-              $page = new PageProcessor($page_id, $namespace);
-              $page->send_headers = false;
-              $page->send();
-              $paths->fullpage = $paths->page;
-            }
-          } 
-          else 
-          {
-            echo '<script type="text/javascript">document.write(\'<div class="wait-box">Please wait while the administration panel loads. You need to be using a recent browser with AJAX support in order to use Runt.</div>\');</script><noscript><div class="error-box">It looks like Javascript isn\'t enabled in your browser. Please enable Javascript or use a different browser to continue.</div></noscript>';
-          }
-          ?>
-          </div>
-          <script type="text/javascript">
-            addOnloadHook(function()
-              {
-                if ( KILL_SWITCH )
-                {
-                  document.getElementById('ajaxPageContainer').innerHTML = '<div class="error-box">Because of the lack of AJAX support, support for Internet Explorer versions less than 6.0 has been disabled in Runt. You can download and use Mozilla Firefox (or Seamonkey under Windows 95); both have an up-to-date standards-compliant rendering engine that has been tested thoroughly with Enano.</div>';
-                }
-              }
-            );
-        </script>
-        </td>
-      </tr>
-    </table>
-  
-    <?php
-    $output->footer();
-  }
+		}
+		?>
+		var TREE_TPL = {
+			'target'  : '_self',  // name of the frame links will be opened in
+									// other possible values are: _blank, _parent, _search, _self and _top
+		
+			'icon_e'  : '<?php echo cdnPath; ?>/images/icons/empty.gif',      // empty image
+			'icon_l'  : '<?php echo cdnPath; ?>/images/icons/line.gif',       // vertical line
+			'icon_32' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root leaf icon normal
+			'icon_36' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root leaf icon selected
+			'icon_48' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root icon normal
+			'icon_52' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root icon selected
+			'icon_56' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root icon opened
+			'icon_60' : '<?php echo cdnPath; ?>/images/spacer.gif',           // root icon selected
+			'icon_16' : '<?php echo cdnPath; ?>/images/spacer.gif',           // node icon normal
+			'icon_20' : '<?php echo cdnPath; ?>/images/spacer.gif',           // node icon selected
+			'icon_24' : '<?php echo cdnPath; ?>/images/spacer.gif',           // node icon opened
+			'icon_28' : '<?php echo cdnPath; ?>/images/spacer.gif',           // node icon selected opened
+			'icon_0'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon normal
+			'icon_4'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon selected
+			'icon_8'  : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon opened
+			'icon_12' : '<?php echo cdnPath; ?>/images/icons/page.gif',       // leaf icon selected
+			'icon_2'  : '<?php echo cdnPath; ?>/images/icons/joinbottom.gif', // junction for leaf
+			'icon_3'  : '<?php echo cdnPath; ?>/images/icons/join.gif',       // junction for last leaf
+			'icon_18' : '<?php echo cdnPath; ?>/images/icons/plusbottom.gif', // junction for closed node
+			'icon_19' : '<?php echo cdnPath; ?>/images/icons/plus.gif',       // junction for last closed node
+			'icon_26' : '<?php echo cdnPath; ?>/images/icons/minusbottom.gif',// junction for opened node
+			'icon_27' : '<?php echo cdnPath; ?>/images/icons/minus.gif'       // junction for last opended node
+		};
+		
+		<?php
+		echo $paths->parseAdminTree(); // Make a Javascript array that defines the tree
+		?>
+		
+		addOnloadHook(function()
+			{
+				new tree(TREE_ITEMS, TREE_TPL, 'admin_tree');
+				keepalive_onload();
+			});
+		</script>
+		<table border="0" width="100%">
+			<tr>
+				<td class="holder" valign="top">
+					<div class="pad" style="padding-right: 20px;" id="admin_tree">
+					</div>
+				</td>
+				<td width="100%" valign="top">
+					<div class="pad" id="ajaxPageContainer">
+					<?php
+					if ( isset($_GET['module']) ) 
+					{
+						list($module) = explode('/', $_GET['module']);
+						list($page_id, $namespace) = RenderMan::strToPageID($module);
+						if ( $namespace != 'Admin' )
+						{
+							echo '<div class="error-box">Module must be in the Admin namespace</div>';
+						}
+						else
+						{
+							$paths->fullpage = $_GET['module'];
+							$paths->cpage['module'] = $_GET['module'];
+							$page = new PageProcessor($page_id, $namespace);
+							$page->send_headers = false;
+							$page->send();
+							$paths->fullpage = $paths->page;
+						}
+					} 
+					else 
+					{
+						echo '<script type="text/javascript">document.write(\'<div class="wait-box">Please wait while the administration panel loads. You need to be using a recent browser with AJAX support in order to use Runt.</div>\');</script><noscript><div class="error-box">It looks like Javascript isn\'t enabled in your browser. Please enable Javascript or use a different browser to continue.</div></noscript>';
+					}
+					?>
+					</div>
+					<script type="text/javascript">
+						addOnloadHook(function()
+							{
+								if ( KILL_SWITCH )
+								{
+									document.getElementById('ajaxPageContainer').innerHTML = '<div class="error-box">Because of the lack of AJAX support, support for Internet Explorer versions less than 6.0 has been disabled in Runt. You can download and use Mozilla Firefox (or Seamonkey under Windows 95); both have an up-to-date standards-compliant rendering engine that has been tested thoroughly with Enano.</div>';
+								}
+							}
+						);
+				</script>
+				</td>
+			</tr>
+		</table>
+	
+		<?php
+		$output->footer();
+	}
 }
 
 function page_Special_EditSidebar()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $cache;
-  
-  if($session->auth_level < USER_LEVEL_ADMIN) 
-  {
-    redirect(makeUrlNS('Special', 'Login/'.$paths->page, 'level='.USER_LEVEL_ADMIN), '', '', false);
-    exit;
-  }
-  else 
-  {
-    if ( isset($_GET['update_order']) )
-    {
-      header('Content-type: text/javascript');
-      $order = @$_POST['order'];
-      try
-      {
-        $order = enano_json_decode($order);
-      }
-      catch ( Zend_Json_Exception $e )
-      {
-        return print enano_json_encode(array(
-            'mode' => 'error',
-            'error' => 'bad order'
-          ));
-      }
-      
-      foreach ( $order as $sidebar_id => $blocks )
-      {
-        foreach ( $blocks as $order => $block_id )
-        {
-          $sbid = intval($sidebar_id);
-          $order = intval($order);
-          $block_id = intval($block_id);
-          $q = $db->sql_query('UPDATE ' . table_prefix . "sidebar SET sidebar_id = $sbid, item_order = $order WHERE item_id = $block_id;");
-          if ( !$q )
-            $db->die_json();
-        }
-      }
-      
-      return print enano_json_encode(array(
-          'mode' => 'success'
-        ));
-    }
-    
-    $template->preload_js(array('l10n', 'jquery', 'jquery-ui'));
-    $template->add_header('<script type="text/javascript" src="'.cdnPath.'/includes/clientside/sbedit.js"></script>');
-    
-    $template->header();
-    
-    if(isset($_POST['save']))
-    {
-      // Write the new block order to the database
-      // The only way to do this is with tons of queries (one per block + one select query at the start to count everything) but afaik its safe...
-      // Anyone know a better way to do this?
-      $q = $db->sql_query('SELECT item_order,item_id,sidebar_id FROM '.table_prefix.'sidebar ORDER BY sidebar_id ASC, item_order ASC;');
-      if ( !$q )
-      {
-        $db->_die('The sidebar order data could not be selected.');
-      }
-      $orders = Array();
-      while($row = $db->fetchrow())
-      {
-        $orders[] = Array(
-            count($orders),
-            $row['item_id'],
-            $row['sidebar_id'],
-          );
-      }
-      $db->free_result();
-      
-      // We now have an array with each sidebar ID in its respective order. Explode the order string in $_POST['order_(left|right)'] and use it to build a set of queries.
-      $ol = explode(',', $_POST['order_left']);
-      $odr = explode(',', $_POST['order_right']);
-      $om = array_merge($ol, $odr);
-      unset($ol, $odr);
-      $queries = Array();
-      foreach($orders as $k => $v)
-      {
-        $queries[] = 'UPDATE '.table_prefix.'sidebar SET item_order='.intval($om[$k]).' WHERE item_id='.intval($v[1]).';';
-      }
-      foreach($queries as $sql)
-      {
-        $q = $db->sql_query($sql);
-        if(!$q)
-        {
-          $t = $db->get_error();
-          echo $t;
-          $template->footer();
-          exit;
-        }
-      }
-      $cache->purge('anon_sidebar');
-      echo '<div class="info-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_order_update_success') . '</div>';
-    }
-    elseif(isset($_POST['create']))
-    {
-      switch((int)$_POST['type'])
-      {
-        case BLOCK_WIKIFORMAT:
-          $content = $_POST['wikiformat_content'];
-          break;
-        case BLOCK_TEMPLATEFORMAT:
-          $content = $_POST['templateformat_content'];
-          break;
-        case BLOCK_HTML:
-          $content = $_POST['html_content'];
-          break;
-        case BLOCK_PHP:
-          $content = $_POST['php_content'];
-          break;
-        case BLOCK_PLUGIN:
-          $content = $_POST['plugin_id'];
-          break;
-      }
-      
-      if ( defined('ENANO_DEMO_MODE') )
-      {
-        // Sanitize the HTML
-        $content = sanitize_html($content, true);
-      }
-      
-      if ( defined('ENANO_DEMO_MODE') && intval($_POST['type']) == BLOCK_PHP )
-      {
-        echo '<div class="error-box" style="margin: 10px 0 10px 0;">' . $lang->get('sbedit_err_demo_php_disable') . '</div>';
-        $_POST['php_content'] = '?>&lt;Nulled&gt;';
-        $content = $_POST['php_content'];
-      }
-      
-      // Get the value of item_order
-      
-      $q = $db->sql_query('SELECT * FROM '.table_prefix.'sidebar WHERE sidebar_id='.intval($_POST['sidebar_id']).';');
-      if(!$q) $db->_die('The order number could not be selected');
-      $io = $db->numrows();
-      
-      $db->free_result();
-      
-      $q = 'INSERT INTO '.table_prefix.'sidebar(block_name, block_type, sidebar_id, block_content, item_order) VALUES ( \''.$db->escape($_POST['title']).'\', \''.$db->escape($_POST['type']).'\', \''.$db->escape($_POST['sidebar_id']).'\', \''.$db->escape($content).'\', '.$io.' );';
-      $result = $db->sql_query($q);
-      if(!$result)
-      {
-        echo $db->get_error();
-        $template->footer();
-        exit;
-      }
-    
-      $cache->purge('anon_sidebar');
-      echo '<div class="info-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_item_added') . '</div>';
-      
-    }
-    
-    if(isset($_GET['action']) && isset($_GET['id']))
-    {
-      if(!preg_match('#^([0-9]*)$#', $_GET['id']))
-      {
-        echo '<div class="warning-box">Error with action: $_GET["id"] was not an integer, aborting to prevent SQL injection</div>';
-      }
-      switch($_GET['action'])
-      {
-        case 'new':
-          ?>
-          <script type="text/javascript">
-          function setType(input)
-          {
-            val = input.value;
-            if(!val)
-            {
-              return false;
-            }
-            var divs = getElementsByClassName(document, 'div', 'sbadd_block');
-            for(var i in divs)
-            {
-              if(divs[i].id == 'blocktype_'+val) divs[i].style.display = 'block';
-              else divs[i].style.display = 'none';
-            }
-          }
-          </script>
-          
-          <form action="<?php echo makeUrl($paths->page); ?>" method="post">
-          
-            <p>
-              <?php echo $lang->get('sbedit_create_intro'); ?>
-            </p>
-            <p>
-              <select name="type" onchange="setType(this)"> <?php /* (NOT WORKING, at least in firefox 2) onload="var thingy = this; setTimeout('setType(thingy)', 500);" */ ?>
-                <option value="<?php echo BLOCK_WIKIFORMAT; ?>"><?php echo $lang->get('sbedit_block_type_wiki'); ?></option>
-                <option value="<?php echo BLOCK_TEMPLATEFORMAT; ?>"><?php echo $lang->get('sbedit_block_type_tpl'); ?></option>
-                <option value="<?php echo BLOCK_HTML; ?>"><?php echo $lang->get('sbedit_block_type_html'); ?></option>
-                <option value="<?php echo BLOCK_PHP; ?>"><?php echo $lang->get('sbedit_block_type_php'); ?></option>
-                <option value="<?php echo BLOCK_PLUGIN; ?>"><?php echo $lang->get('sbedit_block_type_plugin'); ?></option>
-              </select>
-            </p>
-            
-            <p>
-            
-              <?php echo $lang->get('sbedit_field_block_title'); ?> <input name="title" type="text" size="40" /><br />
-              <?php echo $lang->get('sbedit_field_block_sidebar'); ?>
-                <select name="sidebar_id">
-                  <option value="<?php echo SIDEBAR_LEFT; ?>"><?php echo $lang->get('sbedit_field_block_sidebar_left'); ?></option>
-                  <option value="<?php echo SIDEBAR_RIGHT; ?>"><?php echo $lang->get('sbedit_field_block_sidebar_right'); ?></option>
-                </select>
-            
-            </p>
-            
-            <div class="sbadd_block" id="blocktype_<?php echo BLOCK_WIKIFORMAT; ?>">
-              <?php echo $lang->get('sbedit_field_wikitext'); ?>
-              <p>
-                <textarea style="width: 98%;" name="wikiformat_content" rows="15" cols="50"></textarea>
-              </p>
-            </div>
-            
-            <div class="sbadd_block" id="blocktype_<?php echo BLOCK_TEMPLATEFORMAT; ?>">
-              <?php echo $lang->get('sbedit_field_tplcode'); ?>
-              <p>
-                <textarea style="width: 98%;" name="templateformat_content" rows="15" cols="50"></textarea>
-              </p>
-            </div>
-            
-            <div class="sbadd_block" id="blocktype_<?php echo BLOCK_HTML; ?>">
-              <?php echo $lang->get('sbedit_field_html'); ?>
-              <p>
-                <textarea style="width: 98%;" name="html_content" rows="15" cols="50"></textarea>
-              </p>
-            </div>
-            
-            <div class="sbadd_block" id="blocktype_<?php echo BLOCK_PHP; ?>">
-              <?php if ( defined('ENANO_DEMO_MODE') ) { ?>
-                <p><?php echo $lang->get('sbedit_field_php_disabled'); ?></p>
-              <?php } else { ?>
-              <?php echo $lang->get('sbedit_field_php'); ?>
-              
-              <p>
-                <textarea style="width: 98%;" name="php_content" rows="15" cols="50"></textarea>
-              </p>
-              <?php } ?>
-            </div>
-            
-            <div class="sbadd_block" id="blocktype_<?php echo BLOCK_PLUGIN; ?>">
-              <?php echo $lang->get('sbedit_field_plugin'); ?>
-              <p>
-                <select name="plugin_id">
-                <?php
-                  foreach($template->plugin_blocks as $k => $c)
-                  {
-                    echo '<option value="'.$k.'">'.$lang->get($k).'</option>';
-                  }
-                ?>
-                </select>
-              </p>
-            </div>
-            
-            <p>
-            
-              <input type="submit" name="create" value="<?php echo $lang->get('sbedit_btn_create_block'); ?>" style="font-weight: bold;" />&nbsp;
-              <input type="submit" name="cancel" value="<?php echo $lang->get('etc_cancel'); ?>" />
-            
-            </p>
-            
-          </form>
-          
-          <script type="text/javascript">
-            addOnloadHook(function()
-              {
-                var divs = getElementsByClassName(document, 'div', 'sbadd_block');
-                for(var i in divs)
-                {
-                  if(divs[i].id != 'blocktype_<?php echo BLOCK_WIKIFORMAT; ?>') setTimeout("document.getElementById('"+divs[i].id+"').style.display = 'none';", 500);
-                }
-              });
-          </script>
-          
-          <?php
-          $template->footer();
-          return;
-          break;
-        case 'move':
-          $cache->purge('anon_sidebar');
-          if( !isset($_GET['side']) || ( isset($_GET['side']) && !preg_match('#^([0-9]+)$#', $_GET['side']) ) )
-          {
-            echo '<div class="warning-box" style="margin: 10px 0;">$_GET[\'side\'] contained an SQL injection attempt</div>';
-            break;
-          }
-          $query = $db->sql_query('UPDATE '.table_prefix.'sidebar SET sidebar_id=' . $db->escape($_GET['side']) . ' WHERE item_id=' . intval($_GET['id']) . ';');
-          if(!$query)
-          {
-            echo $db->get_error();
-            $template->footer();
-            exit;
-          }
-          echo '<div class="info-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_block_moved') . '</div>';
-          break;
-        case 'delete':
-          $query = $db->sql_query('DELETE FROM '.table_prefix.'sidebar WHERE item_id=' . intval($_GET['id']) . ';'); // Already checked for injection attempts ;-)
-          if(!$query)
-          {
-            echo $db->get_error();
-            $template->footer();
-            exit;
-          }
-          $cache->purge('anon_sidebar');
-          if(isset($_GET['ajax']))
-          {
-            die('GOOD');
-          }
-          echo '<div class="error-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_block_deleted') . '</div>';
-          break;
-        case 'disenable';
-          $q = $db->sql_query('SELECT item_enabled FROM '.table_prefix.'sidebar WHERE item_id=' . intval($_GET['id']) . ';');
-          if(!$q)
-          {
-            echo $db->get_error();
-            $template->footer();
-            exit;
-          }
-          $r = $db->fetchrow();
-          $db->free_result();
-          $e = ( $r['item_enabled'] == 1 ) ? '0' : '1';
-          $q = $db->sql_query('UPDATE '.table_prefix.'sidebar SET item_enabled='.$e.' WHERE item_id=' . intval($_GET['id']) . ';');
-          if(!$q)
-          {
-            echo $db->get_error();
-            $template->footer();
-            exit;
-          }
-          if(isset($_GET['ajax']))
-          {
-            die('GOOD');
-          }
-          break;
-        case 'rename';
-          $newname = $db->escape($_POST['newname']);
-          $q = $db->sql_query('UPDATE '.table_prefix.'sidebar SET block_name=\''.$newname.'\' WHERE item_id=' . intval($_GET['id']) . ';');
-          if(!$q)
-          {
-            echo $db->get_error();
-            $template->footer();
-            exit;
-          }
-          if(isset($_GET['ajax']))
-          {
-            die('GOOD');
-          }
-          break;
-        case 'getsource':
-          $q = $db->sql_query('SELECT block_content,block_type FROM '.table_prefix.'sidebar WHERE item_id=' . intval($_GET['id']) . ';');
-          if(!$q)
-          {
-            echo $db->get_error();
-            $template->footer();
-            exit;
-          }
-          $r = $db->fetchrow();
-          $db->free_result();
-          $cache->purge('anon_sidebar');
-          
-          if($r['block_type'] == BLOCK_PLUGIN) die('HOUSTON_WE_HAVE_A_PLUGIN');
-          die($r['block_content']);
-          break;
-        case 'save':
-          if ( defined('ENANO_DEMO_MODE') )
-          {
-            $q = $db->sql_query('SELECT block_type FROM '.table_prefix.'sidebar WHERE item_id=' . intval($_GET['id']) . ';');
-            if(!$q)
-            {
-              echo 'var status=unescape(\''.hexencode($db->get_error()).'\');';
-              exit;
-            }
-            $row = $db->fetchrow();
-            if ( $row['block_type'] == BLOCK_PHP )
-            {
-              $_POST['content'] = '?>&lt;Nulled&gt;';
-            }
-            else
-            {
-              $_POST['content'] = sanitize_html($_POST['content'], true);
-            }
-          }
-          $q = $db->sql_query('UPDATE '.table_prefix.'sidebar SET block_content=\''.$db->escape(rawurldecode($_POST['content'])).'\' WHERE item_id=' . intval($_GET['id']) . ';');
-          if(!$q)
-          {
-            echo 'var status=unescape(\''.hexencode($db->get_error()).'\');';
-            exit;
-          }
-          echo 'GOOD';
-          return;
-          
-          break;
-      }
-    }
-    
-    ?>
-      <p>
-        <?php echo $lang->get('sbedit_header_msg', array( 'create_link' => makeUrlNS('Special', 'EditSidebar', 'action=new&id=0', true) )); ?>
-      </p>
-    <?php
-    
-    $q = $db->sql_query('SELECT item_id, sidebar_id, block_name, block_type, block_content, item_enabled FROM ' . table_prefix . "sidebar ORDER BY sidebar_id ASC, item_order ASC;");
-    if ( !$q )
-      $db->_die();
-    
-    $switched_to_right = false;
-    
-    echo '<table border="0" cellspacing="4" cellpadding="0"><tr><td class="sbedit-column">';
-    while ( $row = $db->fetchrow() )
-    {
-      if ( $row['sidebar_id'] == SIDEBAR_RIGHT && !$switched_to_right )
-      {
-        echo '</td><td class="sbedit-column">';
-        $switched_to_right = true;
-      }
-      $disabled_class = ( $row['item_enabled'] ) ? '' : ' disabled';
-      echo '<div class="sbedit-block' . $disabled_class . '" id="block:' . $row['item_id'] . '">
-              <div class="sbedit-handle">
-                <span>' . htmlspecialchars($template->compile_template_text_post($row['block_name'])) . '</span>
-                <input type="text" id="block_name:' . $row['item_id'] . '" value="' . htmlspecialchars($row['block_name']) . '" />
-              </div>';
-      ?>
-      <div class="sbedit-metainfo">
-        <?php
-        $toolbarvars = $template->extract_vars('toolbar.tpl');
-        $parser_start = $template->makeParserText($toolbarvars['toolbar_vert_start']);
-        echo $parser_start->run();
-        
-        $button = $template->makeParserText($toolbarvars['toolbar_vert_button']);
-        $label = $template->makeParserText($toolbarvars['toolbar_vert_label']);
-        
-        $type = '<b>';
-        switch($row['block_type'])
-        {
-          case BLOCK_WIKIFORMAT: $type .= $lang->get('sbedit_block_type_wiki'); break;
-          case BLOCK_TEMPLATEFORMAT: $type .= $lang->get('sbedit_block_type_tpl'); break;
-          case BLOCK_HTML: $type .= $lang->get('sbedit_block_type_html'); break;
-          case BLOCK_PHP: $type .= $lang->get('sbedit_block_type_php'); break;
-          case BLOCK_PLUGIN: $type .= $lang->get('sbedit_block_type_plugin'); break;
-          default: $type .= '$&#@'; break;
-        }
-        $type .= '</b>';
-        if ( $row['block_type'] == BLOCK_PLUGIN )
-        {
-          $type .= ': ' . $lang->get($row['block_content']);
-        }
-        
-        $label->assign_vars(array(
-            'TITLE' => $type
-          ));
-        echo $label->run();
-        
-        // edit
-        if ( $row['block_type'] != BLOCK_PLUGIN )
-        {
-          $button->assign_vars(array(
-              'TITLE' => $lang->get('sbedit_tip_edit'),
-              'FLAGS' => 'href="#" onclick="sbedit_open_editor(this); return false;"',
-              'IMAGE' => cdnPath . '/images/edit.png'
-            ));
-          echo $button->run();
-        }
-        
-        // delete
-        $button->assign_vars(array(
-            'TITLE' => $lang->get('sbedit_tip_delete'),
-            'FLAGS' => 'href="#" onclick="sbedit_delete_block(this); return false;"',
-            'IMAGE' => cdnPath . '/images/delete.png'
-          ));
-        echo $button->run();
-        
-        // rename
-        $button->assign_vars(array(
-            'TITLE' => $lang->get('sbedit_tip_rename'),
-            'FLAGS' => 'href="#" onclick="sbedit_rename_block(this); return false;"',
-            'IMAGE' => cdnPath . '/images/rename.png'
-          ));
-        echo $button->run();
-        
-        // disenable
-        $button->assign_vars(array(
-            'TITLE' => $lang->get('sbedit_tip_disenable'),
-            'FLAGS' => 'href="#" onclick="sbedit_disenable_block(this); return false;"',
-            'IMAGE' => cdnPath . '/images/disenable.png'
-          ));
-        echo $button->run();
-        
-        $parser_end = $template->makeParserText($toolbarvars['toolbar_vert_end']);
-        echo $parser_end->run();
-        ?>
-      </div>
-      <?php
-      echo '</div>';
-    }
-    
-    if ( !$switched_to_right )
-      echo '</td><td class="sbedit-column">';
-    
-    echo '</td></tr></table>';
-  }
-  
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $cache;
+	
+	if($session->auth_level < USER_LEVEL_ADMIN) 
+	{
+		redirect(makeUrlNS('Special', 'Login/'.$paths->page, 'level='.USER_LEVEL_ADMIN), '', '', false);
+		exit;
+	}
+	else 
+	{
+		if ( isset($_GET['update_order']) )
+		{
+			header('Content-type: text/javascript');
+			$order = @$_POST['order'];
+			try
+			{
+				$order = enano_json_decode($order);
+			}
+			catch ( Zend_Json_Exception $e )
+			{
+				return print enano_json_encode(array(
+						'mode' => 'error',
+						'error' => 'bad order'
+					));
+			}
+			
+			foreach ( $order as $sidebar_id => $blocks )
+			{
+				foreach ( $blocks as $order => $block_id )
+				{
+					$sbid = intval($sidebar_id);
+					$order = intval($order);
+					$block_id = intval($block_id);
+					$q = $db->sql_query('UPDATE ' . table_prefix . "sidebar SET sidebar_id = $sbid, item_order = $order WHERE item_id = $block_id;");
+					if ( !$q )
+						$db->die_json();
+				}
+			}
+			
+			return print enano_json_encode(array(
+					'mode' => 'success'
+				));
+		}
+		
+		$template->preload_js(array('l10n', 'jquery', 'jquery-ui'));
+		$template->add_header('<script type="text/javascript" src="'.cdnPath.'/includes/clientside/sbedit.js"></script>');
+		
+		$template->header();
+		
+		if(isset($_POST['save']))
+		{
+			// Write the new block order to the database
+			// The only way to do this is with tons of queries (one per block + one select query at the start to count everything) but afaik its safe...
+			// Anyone know a better way to do this?
+			$q = $db->sql_query('SELECT item_order,item_id,sidebar_id FROM '.table_prefix.'sidebar ORDER BY sidebar_id ASC, item_order ASC;');
+			if ( !$q )
+			{
+				$db->_die('The sidebar order data could not be selected.');
+			}
+			$orders = Array();
+			while($row = $db->fetchrow())
+			{
+				$orders[] = Array(
+						count($orders),
+						$row['item_id'],
+						$row['sidebar_id'],
+					);
+			}
+			$db->free_result();
+			
+			// We now have an array with each sidebar ID in its respective order. Explode the order string in $_POST['order_(left|right)'] and use it to build a set of queries.
+			$ol = explode(',', $_POST['order_left']);
+			$odr = explode(',', $_POST['order_right']);
+			$om = array_merge($ol, $odr);
+			unset($ol, $odr);
+			$queries = Array();
+			foreach($orders as $k => $v)
+			{
+				$queries[] = 'UPDATE '.table_prefix.'sidebar SET item_order='.intval($om[$k]).' WHERE item_id='.intval($v[1]).';';
+			}
+			foreach($queries as $sql)
+			{
+				$q = $db->sql_query($sql);
+				if(!$q)
+				{
+					$t = $db->get_error();
+					echo $t;
+					$template->footer();
+					exit;
+				}
+			}
+			$cache->purge('anon_sidebar');
+			echo '<div class="info-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_order_update_success') . '</div>';
+		}
+		elseif(isset($_POST['create']))
+		{
+			switch((int)$_POST['type'])
+			{
+				case BLOCK_WIKIFORMAT:
+					$content = $_POST['wikiformat_content'];
+					break;
+				case BLOCK_TEMPLATEFORMAT:
+					$content = $_POST['templateformat_content'];
+					break;
+				case BLOCK_HTML:
+					$content = $_POST['html_content'];
+					break;
+				case BLOCK_PHP:
+					$content = $_POST['php_content'];
+					break;
+				case BLOCK_PLUGIN:
+					$content = $_POST['plugin_id'];
+					break;
+			}
+			
+			if ( defined('ENANO_DEMO_MODE') )
+			{
+				// Sanitize the HTML
+				$content = sanitize_html($content, true);
+			}
+			
+			if ( defined('ENANO_DEMO_MODE') && intval($_POST['type']) == BLOCK_PHP )
+			{
+				echo '<div class="error-box" style="margin: 10px 0 10px 0;">' . $lang->get('sbedit_err_demo_php_disable') . '</div>';
+				$_POST['php_content'] = '?>&lt;Nulled&gt;';
+				$content = $_POST['php_content'];
+			}
+			
+			// Get the value of item_order
+			
+			$q = $db->sql_query('SELECT * FROM '.table_prefix.'sidebar WHERE sidebar_id='.intval($_POST['sidebar_id']).';');
+			if(!$q) $db->_die('The order number could not be selected');
+			$io = $db->numrows();
+			
+			$db->free_result();
+			
+			$q = 'INSERT INTO '.table_prefix.'sidebar(block_name, block_type, sidebar_id, block_content, item_order) VALUES ( \''.$db->escape($_POST['title']).'\', \''.$db->escape($_POST['type']).'\', \''.$db->escape($_POST['sidebar_id']).'\', \''.$db->escape($content).'\', '.$io.' );';
+			$result = $db->sql_query($q);
+			if(!$result)
+			{
+				echo $db->get_error();
+				$template->footer();
+				exit;
+			}
+		
+			$cache->purge('anon_sidebar');
+			echo '<div class="info-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_item_added') . '</div>';
+			
+		}
+		
+		if(isset($_GET['action']) && isset($_GET['id']))
+		{
+			if(!preg_match('#^([0-9]*)$#', $_GET['id']))
+			{
+				echo '<div class="warning-box">Error with action: $_GET["id"] was not an integer, aborting to prevent SQL injection</div>';
+			}
+			switch($_GET['action'])
+			{
+				case 'new':
+					?>
+					<script type="text/javascript">
+					function setType(input)
+					{
+						val = input.value;
+						if(!val)
+						{
+							return false;
+						}
+						var divs = getElementsByClassName(document, 'div', 'sbadd_block');
+						for(var i in divs)
+						{
+							if(divs[i].id == 'blocktype_'+val) divs[i].style.display = 'block';
+							else divs[i].style.display = 'none';
+						}
+					}
+					</script>
+					
+					<form action="<?php echo makeUrl($paths->page); ?>" method="post">
+					
+						<p>
+							<?php echo $lang->get('sbedit_create_intro'); ?>
+						</p>
+						<p>
+							<select name="type" onchange="setType(this)"> <?php /* (NOT WORKING, at least in firefox 2) onload="var thingy = this; setTimeout('setType(thingy)', 500);" */ ?>
+								<option value="<?php echo BLOCK_WIKIFORMAT; ?>"><?php echo $lang->get('sbedit_block_type_wiki'); ?></option>
+								<option value="<?php echo BLOCK_TEMPLATEFORMAT; ?>"><?php echo $lang->get('sbedit_block_type_tpl'); ?></option>
+								<option value="<?php echo BLOCK_HTML; ?>"><?php echo $lang->get('sbedit_block_type_html'); ?></option>
+								<option value="<?php echo BLOCK_PHP; ?>"><?php echo $lang->get('sbedit_block_type_php'); ?></option>
+								<option value="<?php echo BLOCK_PLUGIN; ?>"><?php echo $lang->get('sbedit_block_type_plugin'); ?></option>
+							</select>
+						</p>
+						
+						<p>
+						
+							<?php echo $lang->get('sbedit_field_block_title'); ?> <input name="title" type="text" size="40" /><br />
+							<?php echo $lang->get('sbedit_field_block_sidebar'); ?>
+								<select name="sidebar_id">
+									<option value="<?php echo SIDEBAR_LEFT; ?>"><?php echo $lang->get('sbedit_field_block_sidebar_left'); ?></option>
+									<option value="<?php echo SIDEBAR_RIGHT; ?>"><?php echo $lang->get('sbedit_field_block_sidebar_right'); ?></option>
+								</select>
+						
+						</p>
+						
+						<div class="sbadd_block" id="blocktype_<?php echo BLOCK_WIKIFORMAT; ?>">
+							<?php echo $lang->get('sbedit_field_wikitext'); ?>
+							<p>
+								<textarea style="width: 98%;" name="wikiformat_content" rows="15" cols="50"></textarea>
+							</p>
+						</div>
+						
+						<div class="sbadd_block" id="blocktype_<?php echo BLOCK_TEMPLATEFORMAT; ?>">
+							<?php echo $lang->get('sbedit_field_tplcode'); ?>
+							<p>
+								<textarea style="width: 98%;" name="templateformat_content" rows="15" cols="50"></textarea>
+							</p>
+						</div>
+						
+						<div class="sbadd_block" id="blocktype_<?php echo BLOCK_HTML; ?>">
+							<?php echo $lang->get('sbedit_field_html'); ?>
+							<p>
+								<textarea style="width: 98%;" name="html_content" rows="15" cols="50"></textarea>
+							</p>
+						</div>
+						
+						<div class="sbadd_block" id="blocktype_<?php echo BLOCK_PHP; ?>">
+							<?php if ( defined('ENANO_DEMO_MODE') ) { ?>
+								<p><?php echo $lang->get('sbedit_field_php_disabled'); ?></p>
+							<?php } else { ?>
+							<?php echo $lang->get('sbedit_field_php'); ?>
+							
+							<p>
+								<textarea style="width: 98%;" name="php_content" rows="15" cols="50"></textarea>
+							</p>
+							<?php } ?>
+						</div>
+						
+						<div class="sbadd_block" id="blocktype_<?php echo BLOCK_PLUGIN; ?>">
+							<?php echo $lang->get('sbedit_field_plugin'); ?>
+							<p>
+								<select name="plugin_id">
+								<?php
+									foreach($template->plugin_blocks as $k => $c)
+									{
+										echo '<option value="'.$k.'">'.$lang->get($k).'</option>';
+									}
+								?>
+								</select>
+							</p>
+						</div>
+						
+						<p>
+						
+							<input type="submit" name="create" value="<?php echo $lang->get('sbedit_btn_create_block'); ?>" style="font-weight: bold;" />&nbsp;
+							<input type="submit" name="cancel" value="<?php echo $lang->get('etc_cancel'); ?>" />
+						
+						</p>
+						
+					</form>
+					
+					<script type="text/javascript">
+						addOnloadHook(function()
+							{
+								var divs = getElementsByClassName(document, 'div', 'sbadd_block');
+								for(var i in divs)
+								{
+									if(divs[i].id != 'blocktype_<?php echo BLOCK_WIKIFORMAT; ?>') setTimeout("document.getElementById('"+divs[i].id+"').style.display = 'none';", 500);
+								}
+							});
+					</script>
+					
+					<?php
+					$template->footer();
+					return;
+					break;
+				case 'move':
+					$cache->purge('anon_sidebar');
+					if( !isset($_GET['side']) || ( isset($_GET['side']) && !preg_match('#^([0-9]+)$#', $_GET['side']) ) )
+					{
+						echo '<div class="warning-box" style="margin: 10px 0;">$_GET[\'side\'] contained an SQL injection attempt</div>';
+						break;
+					}
+					$query = $db->sql_query('UPDATE '.table_prefix.'sidebar SET sidebar_id=' . $db->escape($_GET['side']) . ' WHERE item_id=' . intval($_GET['id']) . ';');
+					if(!$query)
+					{
+						echo $db->get_error();
+						$template->footer();
+						exit;
+					}
+					echo '<div class="info-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_block_moved') . '</div>';
+					break;
+				case 'delete':
+					$query = $db->sql_query('DELETE FROM '.table_prefix.'sidebar WHERE item_id=' . intval($_GET['id']) . ';'); // Already checked for injection attempts ;-)
+					if(!$query)
+					{
+						echo $db->get_error();
+						$template->footer();
+						exit;
+					}
+					$cache->purge('anon_sidebar');
+					if(isset($_GET['ajax']))
+					{
+						die('GOOD');
+					}
+					echo '<div class="error-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_block_deleted') . '</div>';
+					break;
+				case 'disenable';
+					$q = $db->sql_query('SELECT item_enabled FROM '.table_prefix.'sidebar WHERE item_id=' . intval($_GET['id']) . ';');
+					if(!$q)
+					{
+						echo $db->get_error();
+						$template->footer();
+						exit;
+					}
+					$r = $db->fetchrow();
+					$db->free_result();
+					$e = ( $r['item_enabled'] == 1 ) ? '0' : '1';
+					$q = $db->sql_query('UPDATE '.table_prefix.'sidebar SET item_enabled='.$e.' WHERE item_id=' . intval($_GET['id']) . ';');
+					if(!$q)
+					{
+						echo $db->get_error();
+						$template->footer();
+						exit;
+					}
+					if(isset($_GET['ajax']))
+					{
+						die('GOOD');
+					}
+					break;
+				case 'rename';
+					$newname = $db->escape($_POST['newname']);
+					$q = $db->sql_query('UPDATE '.table_prefix.'sidebar SET block_name=\''.$newname.'\' WHERE item_id=' . intval($_GET['id']) . ';');
+					if(!$q)
+					{
+						echo $db->get_error();
+						$template->footer();
+						exit;
+					}
+					if(isset($_GET['ajax']))
+					{
+						die('GOOD');
+					}
+					break;
+				case 'getsource':
+					$q = $db->sql_query('SELECT block_content,block_type FROM '.table_prefix.'sidebar WHERE item_id=' . intval($_GET['id']) . ';');
+					if(!$q)
+					{
+						echo $db->get_error();
+						$template->footer();
+						exit;
+					}
+					$r = $db->fetchrow();
+					$db->free_result();
+					$cache->purge('anon_sidebar');
+					
+					if($r['block_type'] == BLOCK_PLUGIN) die('HOUSTON_WE_HAVE_A_PLUGIN');
+					die($r['block_content']);
+					break;
+				case 'save':
+					if ( defined('ENANO_DEMO_MODE') )
+					{
+						$q = $db->sql_query('SELECT block_type FROM '.table_prefix.'sidebar WHERE item_id=' . intval($_GET['id']) . ';');
+						if(!$q)
+						{
+							echo 'var status=unescape(\''.hexencode($db->get_error()).'\');';
+							exit;
+						}
+						$row = $db->fetchrow();
+						if ( $row['block_type'] == BLOCK_PHP )
+						{
+							$_POST['content'] = '?>&lt;Nulled&gt;';
+						}
+						else
+						{
+							$_POST['content'] = sanitize_html($_POST['content'], true);
+						}
+					}
+					$q = $db->sql_query('UPDATE '.table_prefix.'sidebar SET block_content=\''.$db->escape(rawurldecode($_POST['content'])).'\' WHERE item_id=' . intval($_GET['id']) . ';');
+					if(!$q)
+					{
+						echo 'var status=unescape(\''.hexencode($db->get_error()).'\');';
+						exit;
+					}
+					echo 'GOOD';
+					return;
+					
+					break;
+			}
+		}
+		
+		?>
+			<p>
+				<?php echo $lang->get('sbedit_header_msg', array( 'create_link' => makeUrlNS('Special', 'EditSidebar', 'action=new&id=0', true) )); ?>
+			</p>
+		<?php
+		
+		$q = $db->sql_query('SELECT item_id, sidebar_id, block_name, block_type, block_content, item_enabled FROM ' . table_prefix . "sidebar ORDER BY sidebar_id ASC, item_order ASC;");
+		if ( !$q )
+			$db->_die();
+		
+		$switched_to_right = false;
+		
+		echo '<table border="0" cellspacing="4" cellpadding="0"><tr><td class="sbedit-column">';
+		while ( $row = $db->fetchrow() )
+		{
+			if ( $row['sidebar_id'] == SIDEBAR_RIGHT && !$switched_to_right )
+			{
+				echo '</td><td class="sbedit-column">';
+				$switched_to_right = true;
+			}
+			$disabled_class = ( $row['item_enabled'] ) ? '' : ' disabled';
+			echo '<div class="sbedit-block' . $disabled_class . '" id="block:' . $row['item_id'] . '">
+							<div class="sbedit-handle">
+								<span>' . htmlspecialchars($template->compile_template_text_post($row['block_name'])) . '</span>
+								<input type="text" id="block_name:' . $row['item_id'] . '" value="' . htmlspecialchars($row['block_name']) . '" />
+							</div>';
+			?>
+			<div class="sbedit-metainfo">
+				<?php
+				$toolbarvars = $template->extract_vars('toolbar.tpl');
+				$parser_start = $template->makeParserText($toolbarvars['toolbar_vert_start']);
+				echo $parser_start->run();
+				
+				$button = $template->makeParserText($toolbarvars['toolbar_vert_button']);
+				$label = $template->makeParserText($toolbarvars['toolbar_vert_label']);
+				
+				$type = '<b>';
+				switch($row['block_type'])
+				{
+					case BLOCK_WIKIFORMAT: $type .= $lang->get('sbedit_block_type_wiki'); break;
+					case BLOCK_TEMPLATEFORMAT: $type .= $lang->get('sbedit_block_type_tpl'); break;
+					case BLOCK_HTML: $type .= $lang->get('sbedit_block_type_html'); break;
+					case BLOCK_PHP: $type .= $lang->get('sbedit_block_type_php'); break;
+					case BLOCK_PLUGIN: $type .= $lang->get('sbedit_block_type_plugin'); break;
+					default: $type .= '$&#@'; break;
+				}
+				$type .= '</b>';
+				if ( $row['block_type'] == BLOCK_PLUGIN )
+				{
+					$type .= ': ' . $lang->get($row['block_content']);
+				}
+				
+				$label->assign_vars(array(
+						'TITLE' => $type
+					));
+				echo $label->run();
+				
+				// edit
+				if ( $row['block_type'] != BLOCK_PLUGIN )
+				{
+					$button->assign_vars(array(
+							'TITLE' => $lang->get('sbedit_tip_edit'),
+							'FLAGS' => 'href="#" onclick="sbedit_open_editor(this); return false;"',
+							'IMAGE' => cdnPath . '/images/edit.png'
+						));
+					echo $button->run();
+				}
+				
+				// delete
+				$button->assign_vars(array(
+						'TITLE' => $lang->get('sbedit_tip_delete'),
+						'FLAGS' => 'href="#" onclick="sbedit_delete_block(this); return false;"',
+						'IMAGE' => cdnPath . '/images/delete.png'
+					));
+				echo $button->run();
+				
+				// rename
+				$button->assign_vars(array(
+						'TITLE' => $lang->get('sbedit_tip_rename'),
+						'FLAGS' => 'href="#" onclick="sbedit_rename_block(this); return false;"',
+						'IMAGE' => cdnPath . '/images/rename.png'
+					));
+				echo $button->run();
+				
+				// disenable
+				$button->assign_vars(array(
+						'TITLE' => $lang->get('sbedit_tip_disenable'),
+						'FLAGS' => 'href="#" onclick="sbedit_disenable_block(this); return false;"',
+						'IMAGE' => cdnPath . '/images/disenable.png'
+					));
+				echo $button->run();
+				
+				$parser_end = $template->makeParserText($toolbarvars['toolbar_vert_end']);
+				echo $parser_end->run();
+				?>
+			</div>
+			<?php
+			echo '</div>';
+		}
+		
+		if ( !$switched_to_right )
+			echo '</td><td class="sbedit-column">';
+		
+		echo '</td></tr></table>';
+	}
+	
+	$template->footer();
 }
 
 ?>
\ No newline at end of file
--- a/plugins/SpecialCSS.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialCSS.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_specialcss_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_specialcss_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_specialcss_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_specialcss_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -25,24 +25,24 @@
 
 function SpecialCSS_paths_init()
 {
-  global $paths;
-  register_special_page('CSS', 'specialpage_css', false);
+	global $paths;
+	register_special_page('CSS', 'specialpage_css', false);
 }
 
 // function names are IMPORTANT!!! The name pattern is: page_<namespace ID>_<page URLname, without namespace>
 
 function page_Special_CSS()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  header('Content-type: text/css');
-  if ( isset($_GET['printable']) || $paths->getParam(0) == 'printable' )
-  {
-    echo $template->get_css('_printable.css');
-  }
-  else
-  {
-    echo $template->get_css();
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	header('Content-type: text/css');
+	if ( isset($_GET['printable']) || $paths->getParam(0) == 'printable' )
+	{
+		echo $template->get_css('_printable.css');
+	}
+	else
+	{
+		echo $template->get_css();
+	}
 }
 
 ?>
\ No newline at end of file
--- a/plugins/SpecialGroups.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialGroups.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_specialgroups_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_specialgroups_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_specialgroups_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_specialgroups_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -25,553 +25,553 @@
 
 function SpecialGroups_paths_init()
 {
-  register_special_page('Usergroups', 'specialpage_groupcp');
+	register_special_page('Usergroups', 'specialpage_groupcp');
 }
 
 function page_Special_Usergroups()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $email; // Import e-mail encryption functions
-  global $lang;
-  
-  if ( !$session->user_logged_in )
-  {
-    header('Location: ' . makeUrlComplete('Special', 'Login/' . $paths->page));
-    $db->close();
-    exit;
-  }
-  
-  $template->header();
-  userprefs_show_menu();
-  if ( isset($_POST['do_view']) || isset($_POST['do_view_n']) || ( isset($_GET['act']) && isset($_POST['group_id']) ) )
-  {
-    $gid = ( isset ( $_POST['do_view_n'] ) ) ? intval($_POST['group_id_n']) : intval($_POST['group_id']);
-    if ( empty($gid) || $gid < 1 )
-    {
-      die_friendly('Error', '<p>Hacking attempt</p>');
-    }
-    $q = $db->sql_query('SELECT group_name,group_type,system_group FROM '.table_prefix.'groups WHERE group_id=' . $gid . ';');
-    if ( !$q )
-    {
-      $db->_die('SpecialGroups.php, line ' . __LINE__);
-    }
-    $row = $db->fetchrow();
-    $db->free_result();
-    $members = array();
-    $pending = array();
-    $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,m.pending,COUNT(c.comment_id) AS num_comments
-                           FROM '.table_prefix.'users AS u
-                           LEFT JOIN '.table_prefix.'group_members AS m
-                             ON ( m.user_id = u.user_id )
-                           LEFT JOIN '.table_prefix.'comments AS c
-                             ON ( c.name = u.username )
-                           WHERE m.group_id=' . $gid . '
-                           GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,m.pending
-                           ORDER BY m.is_mod DESC,u.username ASC;');
-    if ( !$q )
-    {
-      $db->_die('SpecialGroups.php, line ' . __LINE__);
-    }
-    
-    $is_member = false;
-    $is_mod = false;
-    $is_pending = false;
-    
-    while ( $mr = $db->fetchrow() )
-    {
-      if ( $mr['pending'] == 1 )
-      {
-        $pending[] = $mr;
-        if ( $mr['user_id'] == $session->user_id )
-        {
-          $is_pending = true;
-        }
-      }
-      else
-      {
-        $members[] = $mr;
-        if ( $mr['user_id'] == $session->user_id )
-        {
-          $is_member = true;
-          if ( $mr['is_mod'] == 1 )
-          {
-            $is_mod = true;
-          }
-        }
-      }
-    }
-    
-    $status = ( $is_member && $is_mod )
-      ? $lang->get('groupcp_status_mod')
-      : ( ( $is_member && !$is_mod ) 
-        ? $lang->get('groupcp_status_member')
-        : $lang->get('groupcp_status_not_member')
-        );
-      
-    $can_do_admin_stuff = ( $is_mod || $session->user_level >= USER_LEVEL_ADMIN );
-      
-    switch ( $row['group_type'] )
-    {
-      case GROUP_HIDDEN:  $g_state = $lang->get('groupcp_type_hidden'); break;
-      case GROUP_CLOSED:  $g_state = $lang->get('groupcp_type_closed'); break;
-      case GROUP_REQUEST: $g_state = $lang->get('groupcp_type_request'); break;
-      case GROUP_OPEN:    $g_state = $lang->get('groupcp_type_open'); break;
-    }
-    
-    if ( isset($_GET['act']) && $can_do_admin_stuff )
-    {
-      switch($_GET['act'])
-      {
-        case 'update':
-          if(!in_array(intval($_POST['group_state']), Array(GROUP_CLOSED, GROUP_OPEN, GROUP_HIDDEN, GROUP_REQUEST)))
-          {
-            die_friendly('ERROR', '<p>Hacking attempt</p>');
-          }
-          $q = $db->sql_query('SELECT group_type, system_group FROM '.table_prefix.'groups WHERE group_id=' . intval( $_POST['group_id']) . ';');
-          if ( !$q )
-            $db->_die('SpecialGroups.php, line ' . __LINE__);
-          $error = false;
-          if ( $db->numrows() < 1 )
-          {
-            echo '<div class="error-box" style="margin-left: 0;">The group you selected does not exist.</div>';
-            $error = true;
-          }
-          $r = $db->fetchrow();
-          if ( $r['system_group'] == 1 && ( intval($_POST['group_state']) == GROUP_OPEN || intval($_POST['group_state']) == GROUP_REQUEST ) )
-          {
-            echo '<div class="error-box" style="margin-left: 0;">' . $lang->get('groupcp_err_state_system_group') . '</div>';
-            $error = true;
-          }
-          if ( !$error )
-          {
-            $q = $db->sql_query('UPDATE '.table_prefix.'groups SET group_type=' . intval($_POST['group_state']) . ' WHERE group_id=' . intval( $_POST['group_id']) . ';');
-            if (!$q)
-              $db->_die('SpecialGroups.php, line ' . __LINE__);
-            $row['group_type'] = $_POST['group_state'];
-            echo '<div class="info-box" style="margin-left: 0;">' . $lang->get('groupcp_msg_state_updated') . '</div>';
-          }
-          break;
-        case 'adduser':
-          $username = $_POST['add_username'];
-          $mod = ( isset($_POST['add_mod']) ) ? '1' : '0';
-          
-          $q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\'' . $db->escape($username) . '\';');
-          if (!$q)
-            $db->_die('SpecialGroups.php, line ' . __LINE__);
-          if ($db->numrows() < 1)
-          {
-            echo '<div class="error-box">' . $lang->get('groupcp_err_user_not_found') . '</div>';
-            break;
-          }
-          $r = $db->fetchrow();
-          $db->free_result();
-          $uid = intval($r['user_id']);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $email; // Import e-mail encryption functions
+	global $lang;
+	
+	if ( !$session->user_logged_in )
+	{
+		header('Location: ' . makeUrlComplete('Special', 'Login/' . $paths->page));
+		$db->close();
+		exit;
+	}
+	
+	$template->header();
+	userprefs_show_menu();
+	if ( isset($_POST['do_view']) || isset($_POST['do_view_n']) || ( isset($_GET['act']) && isset($_POST['group_id']) ) )
+	{
+		$gid = ( isset ( $_POST['do_view_n'] ) ) ? intval($_POST['group_id_n']) : intval($_POST['group_id']);
+		if ( empty($gid) || $gid < 1 )
+		{
+			die_friendly('Error', '<p>Hacking attempt</p>');
+		}
+		$q = $db->sql_query('SELECT group_name,group_type,system_group FROM '.table_prefix.'groups WHERE group_id=' . $gid . ';');
+		if ( !$q )
+		{
+			$db->_die('SpecialGroups.php, line ' . __LINE__);
+		}
+		$row = $db->fetchrow();
+		$db->free_result();
+		$members = array();
+		$pending = array();
+		$q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,m.pending,COUNT(c.comment_id) AS num_comments
+ 													FROM '.table_prefix.'users AS u
+ 													LEFT JOIN '.table_prefix.'group_members AS m
+ 														ON ( m.user_id = u.user_id )
+ 													LEFT JOIN '.table_prefix.'comments AS c
+ 														ON ( c.name = u.username )
+ 													WHERE m.group_id=' . $gid . '
+ 													GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,m.pending
+ 													ORDER BY m.is_mod DESC,u.username ASC;');
+		if ( !$q )
+		{
+			$db->_die('SpecialGroups.php, line ' . __LINE__);
+		}
+		
+		$is_member = false;
+		$is_mod = false;
+		$is_pending = false;
+		
+		while ( $mr = $db->fetchrow() )
+		{
+			if ( $mr['pending'] == 1 )
+			{
+				$pending[] = $mr;
+				if ( $mr['user_id'] == $session->user_id )
+				{
+					$is_pending = true;
+				}
+			}
+			else
+			{
+				$members[] = $mr;
+				if ( $mr['user_id'] == $session->user_id )
+				{
+					$is_member = true;
+					if ( $mr['is_mod'] == 1 )
+					{
+						$is_mod = true;
+					}
+				}
+			}
+		}
+		
+		$status = ( $is_member && $is_mod )
+			? $lang->get('groupcp_status_mod')
+			: ( ( $is_member && !$is_mod ) 
+				? $lang->get('groupcp_status_member')
+				: $lang->get('groupcp_status_not_member')
+				);
+			
+		$can_do_admin_stuff = ( $is_mod || $session->user_level >= USER_LEVEL_ADMIN );
+			
+		switch ( $row['group_type'] )
+		{
+			case GROUP_HIDDEN:  $g_state = $lang->get('groupcp_type_hidden'); break;
+			case GROUP_CLOSED:  $g_state = $lang->get('groupcp_type_closed'); break;
+			case GROUP_REQUEST: $g_state = $lang->get('groupcp_type_request'); break;
+			case GROUP_OPEN:    $g_state = $lang->get('groupcp_type_open'); break;
+		}
+		
+		if ( isset($_GET['act']) && $can_do_admin_stuff )
+		{
+			switch($_GET['act'])
+			{
+				case 'update':
+					if(!in_array(intval($_POST['group_state']), Array(GROUP_CLOSED, GROUP_OPEN, GROUP_HIDDEN, GROUP_REQUEST)))
+					{
+						die_friendly('ERROR', '<p>Hacking attempt</p>');
+					}
+					$q = $db->sql_query('SELECT group_type, system_group FROM '.table_prefix.'groups WHERE group_id=' . intval( $_POST['group_id']) . ';');
+					if ( !$q )
+						$db->_die('SpecialGroups.php, line ' . __LINE__);
+					$error = false;
+					if ( $db->numrows() < 1 )
+					{
+						echo '<div class="error-box" style="margin-left: 0;">The group you selected does not exist.</div>';
+						$error = true;
+					}
+					$r = $db->fetchrow();
+					if ( $r['system_group'] == 1 && ( intval($_POST['group_state']) == GROUP_OPEN || intval($_POST['group_state']) == GROUP_REQUEST ) )
+					{
+						echo '<div class="error-box" style="margin-left: 0;">' . $lang->get('groupcp_err_state_system_group') . '</div>';
+						$error = true;
+					}
+					if ( !$error )
+					{
+						$q = $db->sql_query('UPDATE '.table_prefix.'groups SET group_type=' . intval($_POST['group_state']) . ' WHERE group_id=' . intval( $_POST['group_id']) . ';');
+						if (!$q)
+							$db->_die('SpecialGroups.php, line ' . __LINE__);
+						$row['group_type'] = $_POST['group_state'];
+						echo '<div class="info-box" style="margin-left: 0;">' . $lang->get('groupcp_msg_state_updated') . '</div>';
+					}
+					break;
+				case 'adduser':
+					$username = $_POST['add_username'];
+					$mod = ( isset($_POST['add_mod']) ) ? '1' : '0';
+					
+					$q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\'' . $db->escape($username) . '\';');
+					if (!$q)
+						$db->_die('SpecialGroups.php, line ' . __LINE__);
+					if ($db->numrows() < 1)
+					{
+						echo '<div class="error-box">' . $lang->get('groupcp_err_user_not_found') . '</div>';
+						break;
+					}
+					$r = $db->fetchrow();
+					$db->free_result();
+					$uid = intval($r['user_id']);
 
-          // Check if the user is already in the group, and if so, only update modship
-          $q = $db->sql_query('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $uid . ' AND group_id=' . intval($_POST['group_id']) . ';');
-          if ( !$q )
-            $db->_die('SpecialGroups.php, line ' . __LINE__);
-          if ( $db->numrows() > 0 )
-          {
-            $r = $db->fetchrow();
-            if ( (string) $r['is_mod'] != $mod )
-            {
-              $q = $db->sql_query('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod . ' WHERE member_id=' . $r['member_id'] . ';');
-              if ( !$q )
-                $db->_die('SpecialGroups.php, line ' . __LINE__);
-              foreach ( $members as $i => $member )
-              {
-                if ( $member['member_id'] == $r['member_id'] )
-                  $members[$i]['is_mod'] = (int)$mod;
-              }
-              echo '<div class="info-box">' . $lang->get('groupcp_msg_user_already_in_mod_updated', array('username' => $username)) . '</div>';
-            }
-            else
-            {
-              echo '<div class="info-box">' . $lang->get('groupcp_msg_user_already_in', array('username' => $username)) . '</div>';
-            }
-            break;
-          }
-          
-          $db->free_result();
-          
-          $q = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id,is_mod) VALUES(' . intval($_POST['group_id']) . ', ' . $uid . ', ' . $mod . ');');
-          if (!$q)
-            $db->_die('SpecialGroups.php, line ' . __LINE__);
-          echo '<div class="info-box">' . $lang->get('groupcp_msg_user_added', array('username' => $username)) . '</div>';
-          
-          $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,COUNT(c.comment_id) AS num_comments
-                                 FROM '.table_prefix.'users AS u
-                                 LEFT JOIN '.table_prefix.'group_members AS m
-                                   ON ( m.user_id = u.user_id )
-                                 LEFT JOIN '.table_prefix.'comments AS c
-                                   ON ( c.name = u.username )
-                                 WHERE m.group_id=' . $gid . '
-                                   AND m.pending!=1
-                                   AND u.user_id=' . $uid . '
-                                 GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod
-                                 ORDER BY m.is_mod DESC,u.username ASC
-                                 LIMIT 1;');
-          if ( !$q )
-            $db->_die('SpecialGroups.php, line ' . __LINE__);
-          
-          $r = $db->fetchrow();
-          $members[] = $r;
-          $db->free_result();
-          
-          // just added a user to the group, so regenerate the ranks cache
-          generate_cache_userranks();
-          
-          break;
-        case 'del_users':
-          foreach ( $members as $i => $member )
-          {
-            if ( isset($_POST['del_user'][$member['member_id']]) )
-            {
-              $q = $db->sql_query('DELETE FROM '.table_prefix.'group_members WHERE member_id=' . $member['member_id'] . ';');
-              if (!$q)
-                $db->_die('SpecialGroups.php, line ' . __LINE__);
-              unset($members[$i]);
-            }
-          }
-          // regenerate the ranks cache
-          generate_cache_userranks();
-          
-          break;
-        case 'pending':
-          foreach ( $pending as $i => $member )
-          {
-            if ( isset( $_POST['with_user'][$member['member_id']]) )
-            {
-              if ( isset ( $_POST['do_appr_pending'] ) )
-              {
-                $q = $db->sql_query('UPDATE '.table_prefix.'group_members SET pending=0 WHERE member_id=' . $member['member_id'] . ';');
-                if (!$q)
-                  $db->_die('SpecialGroups.php, line ' . __LINE__);
-                $members[] = $member;
-                unset($pending[$i]);
-                continue;
-              }
-              elseif ( isset ( $_POST['do_reject_pending'] ) )
-              {
-                $q = $db->sql_query('DELETE FROM '.table_prefix.'group_members WHERE member_id=' . $member['member_id'] . ';');
-                if (!$q)
-                  $db->_die('SpecialGroups.php, line ' . __LINE__);
-                unset($pending[$i]);
-              }
-            }
-          }
-          // memberships updated/changed, regenerate ranks cache
-          generate_cache_userranks();
-          
-          echo '<div class="info-box">' . $lang->get('groupcp_msg_pending_updated') . '</div>';
-          break;
-      }
-    }
-    
-    if ( isset($_GET['act']) && $_GET['act'] == 'update' && !$is_member && $row['group_type'] == GROUP_OPEN && !$can_do_admin_stuff )
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id) VALUES(' . $gid . ', ' . $session->user_id . ');');
-      if (!$q)
-        $db->_die('SpecialGroups.php, line ' . __LINE__);
-      echo '<div class="info-box">' . $lang->get('groupcp_msg_self_added') . '</div>';
-      
-      $q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,COUNT(c.comment_id) AS num_comments
-                             FROM '.table_prefix.'users AS u
-                             LEFT JOIN '.table_prefix.'group_members AS m
-                               ON ( m.user_id = u.user_id )
-                             LEFT JOIN '.table_prefix.'comments AS c
-                               ON ( c.name = u.username )
-                             WHERE m.group_id=' . $gid . '
-                               AND m.pending!=1
-                               AND u.user_id=' . $session->user_id . '
-                             GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod
-                             ORDER BY m.is_mod DESC,u.username ASC
-                             LIMIT 1;');
-      if ( !$q )
-        $db->_die('SpecialGroups.php, line ' . __LINE__);
-      
-      $r = $db->fetchrow();
-      $members[] = $r;
-      $db->free_result();
-      
-    }
-    
-    if ( isset($_GET['act']) && $_GET['act'] == 'update' && !$is_member && $row['group_type'] == GROUP_REQUEST && !$is_pending && !$can_do_admin_stuff )
-    {
-      $q = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id,pending) VALUES(' . $gid . ', ' . $session->user_id . ', 1);');
-      if (!$q)
-        $db->_die('SpecialGroups.php, line ' . __LINE__);
-      echo '<div class="info-box">' . $lang->get('groupcp_msg_membership_requested') . '</div>';
-    }
-    
-    $state_btns = ( $can_do_admin_stuff ) ?
-                  '<label><input type="radio" name="group_state" value="' . GROUP_HIDDEN . '" ' . (( $row['group_type'] == GROUP_HIDDEN ) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('groupcp_type_hidden') . '</label>
-                   <label><input type="radio" name="group_state" value="' . GROUP_CLOSED . '" ' . (( $row['group_type'] == GROUP_CLOSED ) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('groupcp_type_closed') . '</label>
-                   <label><input type="radio" name="group_state" value="' . GROUP_REQUEST. '" ' . (( $row['group_type'] == GROUP_REQUEST) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('groupcp_type_request') . '</label>
-                   <label><input type="radio" name="group_state" value="' . GROUP_OPEN   . '" ' . (( $row['group_type'] == GROUP_OPEN   ) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('groupcp_type_open') . '</label>'
-                   : $g_state;
-    if ( !$can_do_admin_stuff && $row['group_type'] == GROUP_REQUEST && !$is_member )
-    {
-      if ( $is_pending )
-        $state_btns .= ' ' . $lang->get('groupcp_msg_status_pending');
-      else
-        $state_btns .= ' <input type="submit" value="' . $lang->get('groupcp_btn_request_join') . '" />';
-    }
-    
-    if ( !$can_do_admin_stuff && $row['group_type'] == GROUP_OPEN && !$is_member )
-    {
-      $state_btns .= ' <input type="submit" value="' . $lang->get('groupcp_btn_join') . '" />';
-    }
-    
-    $g_name_local = 'groupcp_grp_' . strtolower($row['group_name']);
-    $str = $lang->get($g_name_local);
-    if ( $str != $g_name_local )
-      $row['group_name'] = $str;
-    
-    echo '<form action="' . makeUrl($paths->page, 'act=update') . '" method="post" enctype="multipart/form-data">
-          <div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">
-              <tr>
-                <th colspan="2">' . $lang->get('groupcp_th_group_info') . '</th>
-              </tr>
-              <tr>
-                <td class="row2">' . $lang->get('groupcp_lbl_group_name') . '</td>
-                <td class="row1">' . $row['group_name'] . ( $row['system_group'] == 1 ? ' ' . $lang->get('groupcp_msg_system_group') : '' ) . '</td>
-              </tr>
-              <tr>
-                <td class="row2">' . $lang->get('groupcp_lbl_status') . '</td>
-                <td class="row1">' . $status . '</td>
-              </tr>
-              <tr>
-                <td class="row2">' . $lang->get('groupcp_lbl_state') . '</td>
-                <td class="row1">' . $state_btns . '</td>
-              </tr>   
-              ' . ( ( $is_mod || $session->user_level >= USER_LEVEL_ADMIN ) ? '
-              <tr>
-                <th class="subhead" colspan="2">
-                  <input type="submit" value="' . $lang->get('etc_save_changes') . '" />
-                </th>
-              </tr>
-              ' : '' ) . '
-            </table>
-          </div>
-          <input name="group_id" value="' . $gid . '" type="hidden" />
-          </form>';
-    if ( sizeof ( $pending ) > 0 && $can_do_admin_stuff )
-    {
-      echo '<form action="' . makeUrl($paths->page, 'act=pending') . '" method="post" enctype="multipart/form-data">
-            <input name="group_id" value="' . $gid . '" type="hidden" />
-            <h2>' . $lang->get('groupcp_th_pending_memberships') . '</h2>
-            <div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">
-              <tr>
-                <th>' . $lang->get('groupcp_th_username') . '</th>
-                <th>' . $lang->get('groupcp_th_email') . '</th>
-                <th>' . $lang->get('groupcp_th_reg_time') . '</th>
-                <th>' . $lang->get('groupcp_th_comments') . '</th>
-                <th>' . $lang->get('groupcp_th_select') . '</th>
-              </tr>';
-      $cls = 'row2';
-      foreach ( $pending as $member )
-      {
-        
-        $date = enano_date(ED_DATE, $member['reg_time']);
-        $cls = ( $cls == 'row2' ) ? 'row1' : 'row2';
-        $addy = $email->encryptEmail($member['email']);
-        
-        echo "<tr>
-                <td class='{$cls}'>{$member['username']}</td>
-                <td class='{$cls}'>{$addy}</td>
-                <td class='{$cls}'>{$date}</td>
-                <td class='{$cls}'>{$member['num_comments']}</td>
-                <td class='{$cls}' style='text-align: center;'><input type='checkbox' name='with_user[{$member['member_id']}]' /></td>
-              </tr>";
-      }
-      echo '</table>
-            </div>
-            <div style="margin: 10px 0 0 auto;">
-              With selected: 
-              <input type="submit" name="do_appr_pending" value="' . $lang->get('groupcp_btn_approve_pending') . '" />
-              <input type="submit" name="do_reject_pending" value="' . $lang->get('groupcp_btn_reject_pending') . '" />
-            </div>
-            </form>';
-    }
-    echo '<form action="' . makeUrl($paths->page, 'act=del_users') . '" method="post" enctype="multipart/form-data">
-          <h2>' . $lang->get('groupcp_th_group_members') . '</h2>
-          <div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">
-              <tr>
-                <th>' . $lang->get('groupcp_th_username') . '</th>
-                <th>' . $lang->get('groupcp_th_email') . '</th>
-                <th>' . $lang->get('groupcp_th_reg_time') . '</th>
-                <th>' . $lang->get('groupcp_th_comments') . '</th>
-                ' . ( ( $can_do_admin_stuff ) ? '
-                <th>' . $lang->get('groupcp_th_remove') . '</th>
-                ' : '' ) . '
-              </tr>
-              <tr>
-                <th colspan="5" class="subhead">' . $lang->get('groupcp_th_group_mods') . '</th>
-              </tr>';
-    $mod_printed = false;
-    $mem_printed = false;
-    $cls = 'row2';
-    
-    foreach ( $members as $member )
-    {
-      if ( $member['is_mod'] != 1 )
-        break;
-      
-      $date = enano_date(ED_DATE, $member['reg_time']);
-      $cls = ( $cls == 'row2' ) ? 'row1' : 'row2';
-      $addy = $email->encryptEmail($member['email']);
-      
-      $mod_printed = true;
-      
-      echo "<tr>
-              <td class='{$cls}'>{$member['username']}</td>
-              <td class='{$cls}'>{$addy}</td>
-              <td class='{$cls}'>{$date}</td>
-              <td class='{$cls}'>{$member['num_comments']}</td>
-              " . ( ( $can_do_admin_stuff ) ? "
-              <td class='{$cls}' style='text-align: center;'><input type='checkbox' name='del_user[{$member['member_id']}]' /></td>
-              " : '' ) . "
-            </tr>";
-    }
-    if (!$mod_printed)
-      echo '<tr><td class="' . $cls . '" colspan="5">' . $lang->get('groupcp_msg_no_mods') . '</td></th>';
-    echo '<tr><th class="subhead" colspan="5">' . $lang->get('groupcp_th_group_members') . '</th></tr>';
-    foreach ( $members as $member )
-    {
-      if ( $member['is_mod'] == 1 )
-        continue;
-      
-      $date = enano_date(ED_DATE, $member['reg_time']);
-      $cls = ( $cls == 'row2' ) ? 'row1' : 'row2';
-      $addy = $email->encryptEmail($member['email']);
-      
-      $mem_printed = true;
-      
-      echo "<tr>
-              <td class='{$cls}'>{$member['username']}</td>
-              <td class='{$cls}'>{$addy}</td>
-              <td class='{$cls}'>{$date}</td>
-              <td class='{$cls}'>{$member['num_comments']}</td>
-              " . ( ( $can_do_admin_stuff ) ? "
-              <td class='{$cls}' style='text-align: center;'><input type='checkbox' name='del_user[{$member['member_id']}]' /></td>
-              " : '' ) . "
-            </tr>";
-    }
-    if (!$mem_printed)
-      echo '<tr><td class="' . $cls . '" colspan="5">' . $lang->get('groupcp_msg_no_members') . '</td></th>';
-    echo '  </table>
-          </div>';
-    if ( $can_do_admin_stuff )
-    {
-      echo "<div style='margin: 10px 0 0 auto;'><input type='submit' name='do_del_user' value=\"" . $lang->get('groupcp_btn_remove_selected') . "\" /></div>";
-    }
-    echo '<input name="group_id" value="' . $gid . '" type="hidden" />
-          </form>';
-    if ( $can_do_admin_stuff )
-    {
-      echo '<form action="' . makeUrl($paths->page, 'act=adduser') . '" method="post" enctype="multipart/form-data" onsubmit="if(!submitAuthorized) return false;">
-              <div class="tblholder">
-                <table border="0" cellspacing="1" cellpadding="4">
-                  <tr>
-                    <th colspan="2">' . $lang->get('groupcp_th_add_member') . '</th>
-                  </tr>
-                  <tr>
-                    <td class="row2">' . $lang->get('groupcp_lbl_username') . '</td><td class="row1">' . $template->username_field('add_username') . '</td>
-                  </tr>
-                  <tr>
-                    <td class="row2">' . $lang->get('groupcp_lbl_moderator') . '</td><td class="row1"><label><input type="checkbox" name="add_mod" /> ' . $lang->get('groupcp_lbl_make_mod') . '</label></td>
-                  </tr>
-                  <tr>
-                    <th class="subhead" colspan="2">
-                      <input type="submit" value="' . $lang->get('groupcp_btn_add_member') . '" />
-                    </th>
-                  </tr>
-                </table>
-              </div>
-              <input name="group_id" value="' . $gid . '" type="hidden" />
-            </form>';
-    }
-  }
-  else
-  {
-    echo '<form action="'.makeUrlNS('Special', 'Usergroups').'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-    echo '<div class="tblholder">
-          <table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
-            <tr>
-              <th colspan="2">' . $lang->get('groupcp_th_select_group') . '</th>
-            </tr>
-            <tr>
-              <td class="row2" style="text-align: right; width: 50%;">
-                ' . $lang->get('groupcp_lbl_current_memberships') . '
-              </td>
-              <td class="row1" style="width: 50%;">';
-    $taboo = Array('Everyone');
-    if ( sizeof ( $session->groups ) > count($taboo) )
-    {
-      echo '<select name="group_id">';
-      foreach ( $session->groups as $id => $group )
-      {
-        $taboo[] = $db->escape($group);
-        $group = htmlspecialchars($group);
-        if ( $group != 'Everyone' )
-        {
-          $g_name_local = 'groupcp_grp_' . strtolower($group);
-          $str = $lang->get($g_name_local);
-          if ( $str != $g_name_local )
-            $group = $str;
-          echo '<option value="' . $id . '">' . $group . '</option>';
-        }
-      }
-      echo '</select>
-            <input type="submit" name="do_view" value="' . $lang->get('groupcp_btn_view') . '" />';
-    }
-    else
-    {
-      echo 'None';
-    }
-    
-    echo '</td>
-        </tr>';
-    $taboo = 'WHERE group_name != \'' . implode('\' AND group_name != \'', $taboo) . '\'';
-    $q = $db->sql_query('SELECT group_id,group_name FROM '.table_prefix.'groups '.$taboo.' AND group_type != ' . GROUP_HIDDEN . ' ORDER BY group_name ASC;');
-    if(!$q)
-    {
-      echo $db->get_error();
-      $template->footer();
-      return;
-    }
-    if($db->numrows() > 0)
-    {
-      echo '<tr>
-              <td class="row2" style="text-align: right;">
-                ' . $lang->get('groupcp_lbl_non_memberships') . '
-              </td>
-              <td class="row1">
-              <select name="group_id_n">';
-      while ( $row = $db->fetchrow() )
-      {
-        if ( $row['group_name'] != 'Everyone' )
-        {
-          echo '<option value="' . $row['group_id'] . '">' . htmlspecialchars($row['group_name']) . '</option>';
-        }
-      }
-      echo '</select>
-            <input type="submit" name="do_view_n" value="' . $lang->get('groupcp_btn_view') . '" />
-          </td>
-        </tr>
-      ';
-    }
-    $db->free_result();
-    echo '</table>
-        </div>
-        </form>';
-  }
-  $template->footer();
+					// Check if the user is already in the group, and if so, only update modship
+					$q = $db->sql_query('SELECT member_id,is_mod FROM '.table_prefix.'group_members WHERE user_id=' . $uid . ' AND group_id=' . intval($_POST['group_id']) . ';');
+					if ( !$q )
+						$db->_die('SpecialGroups.php, line ' . __LINE__);
+					if ( $db->numrows() > 0 )
+					{
+						$r = $db->fetchrow();
+						if ( (string) $r['is_mod'] != $mod )
+						{
+							$q = $db->sql_query('UPDATE '.table_prefix.'group_members SET is_mod=' . $mod . ' WHERE member_id=' . $r['member_id'] . ';');
+							if ( !$q )
+								$db->_die('SpecialGroups.php, line ' . __LINE__);
+							foreach ( $members as $i => $member )
+							{
+								if ( $member['member_id'] == $r['member_id'] )
+									$members[$i]['is_mod'] = (int)$mod;
+							}
+							echo '<div class="info-box">' . $lang->get('groupcp_msg_user_already_in_mod_updated', array('username' => $username)) . '</div>';
+						}
+						else
+						{
+							echo '<div class="info-box">' . $lang->get('groupcp_msg_user_already_in', array('username' => $username)) . '</div>';
+						}
+						break;
+					}
+					
+					$db->free_result();
+					
+					$q = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id,is_mod) VALUES(' . intval($_POST['group_id']) . ', ' . $uid . ', ' . $mod . ');');
+					if (!$q)
+						$db->_die('SpecialGroups.php, line ' . __LINE__);
+					echo '<div class="info-box">' . $lang->get('groupcp_msg_user_added', array('username' => $username)) . '</div>';
+					
+					$q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,COUNT(c.comment_id) AS num_comments
+ 																FROM '.table_prefix.'users AS u
+ 																LEFT JOIN '.table_prefix.'group_members AS m
+ 																	ON ( m.user_id = u.user_id )
+ 																LEFT JOIN '.table_prefix.'comments AS c
+ 																	ON ( c.name = u.username )
+ 																WHERE m.group_id=' . $gid . '
+ 																	AND m.pending!=1
+ 																	AND u.user_id=' . $uid . '
+ 																GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod
+ 																ORDER BY m.is_mod DESC,u.username ASC
+ 																LIMIT 1;');
+					if ( !$q )
+						$db->_die('SpecialGroups.php, line ' . __LINE__);
+					
+					$r = $db->fetchrow();
+					$members[] = $r;
+					$db->free_result();
+					
+					// just added a user to the group, so regenerate the ranks cache
+					generate_cache_userranks();
+					
+					break;
+				case 'del_users':
+					foreach ( $members as $i => $member )
+					{
+						if ( isset($_POST['del_user'][$member['member_id']]) )
+						{
+							$q = $db->sql_query('DELETE FROM '.table_prefix.'group_members WHERE member_id=' . $member['member_id'] . ';');
+							if (!$q)
+								$db->_die('SpecialGroups.php, line ' . __LINE__);
+							unset($members[$i]);
+						}
+					}
+					// regenerate the ranks cache
+					generate_cache_userranks();
+					
+					break;
+				case 'pending':
+					foreach ( $pending as $i => $member )
+					{
+						if ( isset( $_POST['with_user'][$member['member_id']]) )
+						{
+							if ( isset ( $_POST['do_appr_pending'] ) )
+							{
+								$q = $db->sql_query('UPDATE '.table_prefix.'group_members SET pending=0 WHERE member_id=' . $member['member_id'] . ';');
+								if (!$q)
+									$db->_die('SpecialGroups.php, line ' . __LINE__);
+								$members[] = $member;
+								unset($pending[$i]);
+								continue;
+							}
+							elseif ( isset ( $_POST['do_reject_pending'] ) )
+							{
+								$q = $db->sql_query('DELETE FROM '.table_prefix.'group_members WHERE member_id=' . $member['member_id'] . ';');
+								if (!$q)
+									$db->_die('SpecialGroups.php, line ' . __LINE__);
+								unset($pending[$i]);
+							}
+						}
+					}
+					// memberships updated/changed, regenerate ranks cache
+					generate_cache_userranks();
+					
+					echo '<div class="info-box">' . $lang->get('groupcp_msg_pending_updated') . '</div>';
+					break;
+			}
+		}
+		
+		if ( isset($_GET['act']) && $_GET['act'] == 'update' && !$is_member && $row['group_type'] == GROUP_OPEN && !$can_do_admin_stuff )
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id) VALUES(' . $gid . ', ' . $session->user_id . ');');
+			if (!$q)
+				$db->_die('SpecialGroups.php, line ' . __LINE__);
+			echo '<div class="info-box">' . $lang->get('groupcp_msg_self_added') . '</div>';
+			
+			$q = $db->sql_query('SELECT u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod,COUNT(c.comment_id) AS num_comments
+ 														FROM '.table_prefix.'users AS u
+ 														LEFT JOIN '.table_prefix.'group_members AS m
+ 															ON ( m.user_id = u.user_id )
+ 														LEFT JOIN '.table_prefix.'comments AS c
+ 															ON ( c.name = u.username )
+ 														WHERE m.group_id=' . $gid . '
+ 															AND m.pending!=1
+ 															AND u.user_id=' . $session->user_id . '
+ 														GROUP BY u.user_id,u.username,u.email,u.reg_time,m.member_id,m.user_id,m.is_mod
+ 														ORDER BY m.is_mod DESC,u.username ASC
+ 														LIMIT 1;');
+			if ( !$q )
+				$db->_die('SpecialGroups.php, line ' . __LINE__);
+			
+			$r = $db->fetchrow();
+			$members[] = $r;
+			$db->free_result();
+			
+		}
+		
+		if ( isset($_GET['act']) && $_GET['act'] == 'update' && !$is_member && $row['group_type'] == GROUP_REQUEST && !$is_pending && !$can_do_admin_stuff )
+		{
+			$q = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id,pending) VALUES(' . $gid . ', ' . $session->user_id . ', 1);');
+			if (!$q)
+				$db->_die('SpecialGroups.php, line ' . __LINE__);
+			echo '<div class="info-box">' . $lang->get('groupcp_msg_membership_requested') . '</div>';
+		}
+		
+		$state_btns = ( $can_do_admin_stuff ) ?
+									'<label><input type="radio" name="group_state" value="' . GROUP_HIDDEN . '" ' . (( $row['group_type'] == GROUP_HIDDEN ) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('groupcp_type_hidden') . '</label>
+ 									<label><input type="radio" name="group_state" value="' . GROUP_CLOSED . '" ' . (( $row['group_type'] == GROUP_CLOSED ) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('groupcp_type_closed') . '</label>
+ 									<label><input type="radio" name="group_state" value="' . GROUP_REQUEST. '" ' . (( $row['group_type'] == GROUP_REQUEST) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('groupcp_type_request') . '</label>
+ 									<label><input type="radio" name="group_state" value="' . GROUP_OPEN   . '" ' . (( $row['group_type'] == GROUP_OPEN   ) ? 'checked="checked"' : '' ) . ' /> ' . $lang->get('groupcp_type_open') . '</label>'
+ 									: $g_state;
+		if ( !$can_do_admin_stuff && $row['group_type'] == GROUP_REQUEST && !$is_member )
+		{
+			if ( $is_pending )
+				$state_btns .= ' ' . $lang->get('groupcp_msg_status_pending');
+			else
+				$state_btns .= ' <input type="submit" value="' . $lang->get('groupcp_btn_request_join') . '" />';
+		}
+		
+		if ( !$can_do_admin_stuff && $row['group_type'] == GROUP_OPEN && !$is_member )
+		{
+			$state_btns .= ' <input type="submit" value="' . $lang->get('groupcp_btn_join') . '" />';
+		}
+		
+		$g_name_local = 'groupcp_grp_' . strtolower($row['group_name']);
+		$str = $lang->get($g_name_local);
+		if ( $str != $g_name_local )
+			$row['group_name'] = $str;
+		
+		echo '<form action="' . makeUrl($paths->page, 'act=update') . '" method="post" enctype="multipart/form-data">
+					<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">
+							<tr>
+								<th colspan="2">' . $lang->get('groupcp_th_group_info') . '</th>
+							</tr>
+							<tr>
+								<td class="row2">' . $lang->get('groupcp_lbl_group_name') . '</td>
+								<td class="row1">' . $row['group_name'] . ( $row['system_group'] == 1 ? ' ' . $lang->get('groupcp_msg_system_group') : '' ) . '</td>
+							</tr>
+							<tr>
+								<td class="row2">' . $lang->get('groupcp_lbl_status') . '</td>
+								<td class="row1">' . $status . '</td>
+							</tr>
+							<tr>
+								<td class="row2">' . $lang->get('groupcp_lbl_state') . '</td>
+								<td class="row1">' . $state_btns . '</td>
+							</tr>   
+							' . ( ( $is_mod || $session->user_level >= USER_LEVEL_ADMIN ) ? '
+							<tr>
+								<th class="subhead" colspan="2">
+									<input type="submit" value="' . $lang->get('etc_save_changes') . '" />
+								</th>
+							</tr>
+							' : '' ) . '
+						</table>
+					</div>
+					<input name="group_id" value="' . $gid . '" type="hidden" />
+					</form>';
+		if ( sizeof ( $pending ) > 0 && $can_do_admin_stuff )
+		{
+			echo '<form action="' . makeUrl($paths->page, 'act=pending') . '" method="post" enctype="multipart/form-data">
+						<input name="group_id" value="' . $gid . '" type="hidden" />
+						<h2>' . $lang->get('groupcp_th_pending_memberships') . '</h2>
+						<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">
+							<tr>
+								<th>' . $lang->get('groupcp_th_username') . '</th>
+								<th>' . $lang->get('groupcp_th_email') . '</th>
+								<th>' . $lang->get('groupcp_th_reg_time') . '</th>
+								<th>' . $lang->get('groupcp_th_comments') . '</th>
+								<th>' . $lang->get('groupcp_th_select') . '</th>
+							</tr>';
+			$cls = 'row2';
+			foreach ( $pending as $member )
+			{
+				
+				$date = enano_date(ED_DATE, $member['reg_time']);
+				$cls = ( $cls == 'row2' ) ? 'row1' : 'row2';
+				$addy = $email->encryptEmail($member['email']);
+				
+				echo "<tr>
+								<td class='{$cls}'>{$member['username']}</td>
+								<td class='{$cls}'>{$addy}</td>
+								<td class='{$cls}'>{$date}</td>
+								<td class='{$cls}'>{$member['num_comments']}</td>
+								<td class='{$cls}' style='text-align: center;'><input type='checkbox' name='with_user[{$member['member_id']}]' /></td>
+							</tr>";
+			}
+			echo '</table>
+						</div>
+						<div style="margin: 10px 0 0 auto;">
+							With selected: 
+							<input type="submit" name="do_appr_pending" value="' . $lang->get('groupcp_btn_approve_pending') . '" />
+							<input type="submit" name="do_reject_pending" value="' . $lang->get('groupcp_btn_reject_pending') . '" />
+						</div>
+						</form>';
+		}
+		echo '<form action="' . makeUrl($paths->page, 'act=del_users') . '" method="post" enctype="multipart/form-data">
+					<h2>' . $lang->get('groupcp_th_group_members') . '</h2>
+					<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">
+							<tr>
+								<th>' . $lang->get('groupcp_th_username') . '</th>
+								<th>' . $lang->get('groupcp_th_email') . '</th>
+								<th>' . $lang->get('groupcp_th_reg_time') . '</th>
+								<th>' . $lang->get('groupcp_th_comments') . '</th>
+								' . ( ( $can_do_admin_stuff ) ? '
+								<th>' . $lang->get('groupcp_th_remove') . '</th>
+								' : '' ) . '
+							</tr>
+							<tr>
+								<th colspan="5" class="subhead">' . $lang->get('groupcp_th_group_mods') . '</th>
+							</tr>';
+		$mod_printed = false;
+		$mem_printed = false;
+		$cls = 'row2';
+		
+		foreach ( $members as $member )
+		{
+			if ( $member['is_mod'] != 1 )
+				break;
+			
+			$date = enano_date(ED_DATE, $member['reg_time']);
+			$cls = ( $cls == 'row2' ) ? 'row1' : 'row2';
+			$addy = $email->encryptEmail($member['email']);
+			
+			$mod_printed = true;
+			
+			echo "<tr>
+							<td class='{$cls}'>{$member['username']}</td>
+							<td class='{$cls}'>{$addy}</td>
+							<td class='{$cls}'>{$date}</td>
+							<td class='{$cls}'>{$member['num_comments']}</td>
+							" . ( ( $can_do_admin_stuff ) ? "
+							<td class='{$cls}' style='text-align: center;'><input type='checkbox' name='del_user[{$member['member_id']}]' /></td>
+							" : '' ) . "
+						</tr>";
+		}
+		if (!$mod_printed)
+			echo '<tr><td class="' . $cls . '" colspan="5">' . $lang->get('groupcp_msg_no_mods') . '</td></th>';
+		echo '<tr><th class="subhead" colspan="5">' . $lang->get('groupcp_th_group_members') . '</th></tr>';
+		foreach ( $members as $member )
+		{
+			if ( $member['is_mod'] == 1 )
+				continue;
+			
+			$date = enano_date(ED_DATE, $member['reg_time']);
+			$cls = ( $cls == 'row2' ) ? 'row1' : 'row2';
+			$addy = $email->encryptEmail($member['email']);
+			
+			$mem_printed = true;
+			
+			echo "<tr>
+							<td class='{$cls}'>{$member['username']}</td>
+							<td class='{$cls}'>{$addy}</td>
+							<td class='{$cls}'>{$date}</td>
+							<td class='{$cls}'>{$member['num_comments']}</td>
+							" . ( ( $can_do_admin_stuff ) ? "
+							<td class='{$cls}' style='text-align: center;'><input type='checkbox' name='del_user[{$member['member_id']}]' /></td>
+							" : '' ) . "
+						</tr>";
+		}
+		if (!$mem_printed)
+			echo '<tr><td class="' . $cls . '" colspan="5">' . $lang->get('groupcp_msg_no_members') . '</td></th>';
+		echo '  </table>
+					</div>';
+		if ( $can_do_admin_stuff )
+		{
+			echo "<div style='margin: 10px 0 0 auto;'><input type='submit' name='do_del_user' value=\"" . $lang->get('groupcp_btn_remove_selected') . "\" /></div>";
+		}
+		echo '<input name="group_id" value="' . $gid . '" type="hidden" />
+					</form>';
+		if ( $can_do_admin_stuff )
+		{
+			echo '<form action="' . makeUrl($paths->page, 'act=adduser') . '" method="post" enctype="multipart/form-data" onsubmit="if(!submitAuthorized) return false;">
+							<div class="tblholder">
+								<table border="0" cellspacing="1" cellpadding="4">
+									<tr>
+										<th colspan="2">' . $lang->get('groupcp_th_add_member') . '</th>
+									</tr>
+									<tr>
+										<td class="row2">' . $lang->get('groupcp_lbl_username') . '</td><td class="row1">' . $template->username_field('add_username') . '</td>
+									</tr>
+									<tr>
+										<td class="row2">' . $lang->get('groupcp_lbl_moderator') . '</td><td class="row1"><label><input type="checkbox" name="add_mod" /> ' . $lang->get('groupcp_lbl_make_mod') . '</label></td>
+									</tr>
+									<tr>
+										<th class="subhead" colspan="2">
+											<input type="submit" value="' . $lang->get('groupcp_btn_add_member') . '" />
+										</th>
+									</tr>
+								</table>
+							</div>
+							<input name="group_id" value="' . $gid . '" type="hidden" />
+						</form>';
+		}
+	}
+	else
+	{
+		echo '<form action="'.makeUrlNS('Special', 'Usergroups').'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+		echo '<div class="tblholder">
+					<table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
+						<tr>
+							<th colspan="2">' . $lang->get('groupcp_th_select_group') . '</th>
+						</tr>
+						<tr>
+							<td class="row2" style="text-align: right; width: 50%;">
+								' . $lang->get('groupcp_lbl_current_memberships') . '
+							</td>
+							<td class="row1" style="width: 50%;">';
+		$taboo = Array('Everyone');
+		if ( sizeof ( $session->groups ) > count($taboo) )
+		{
+			echo '<select name="group_id">';
+			foreach ( $session->groups as $id => $group )
+			{
+				$taboo[] = $db->escape($group);
+				$group = htmlspecialchars($group);
+				if ( $group != 'Everyone' )
+				{
+					$g_name_local = 'groupcp_grp_' . strtolower($group);
+					$str = $lang->get($g_name_local);
+					if ( $str != $g_name_local )
+						$group = $str;
+					echo '<option value="' . $id . '">' . $group . '</option>';
+				}
+			}
+			echo '</select>
+						<input type="submit" name="do_view" value="' . $lang->get('groupcp_btn_view') . '" />';
+		}
+		else
+		{
+			echo 'None';
+		}
+		
+		echo '</td>
+				</tr>';
+		$taboo = 'WHERE group_name != \'' . implode('\' AND group_name != \'', $taboo) . '\'';
+		$q = $db->sql_query('SELECT group_id,group_name FROM '.table_prefix.'groups '.$taboo.' AND group_type != ' . GROUP_HIDDEN . ' ORDER BY group_name ASC;');
+		if(!$q)
+		{
+			echo $db->get_error();
+			$template->footer();
+			return;
+		}
+		if($db->numrows() > 0)
+		{
+			echo '<tr>
+							<td class="row2" style="text-align: right;">
+								' . $lang->get('groupcp_lbl_non_memberships') . '
+							</td>
+							<td class="row1">
+							<select name="group_id_n">';
+			while ( $row = $db->fetchrow() )
+			{
+				if ( $row['group_name'] != 'Everyone' )
+				{
+					echo '<option value="' . $row['group_id'] . '">' . htmlspecialchars($row['group_name']) . '</option>';
+				}
+			}
+			echo '</select>
+						<input type="submit" name="do_view_n" value="' . $lang->get('groupcp_btn_view') . '" />
+					</td>
+				</tr>
+			';
+		}
+		$db->free_result();
+		echo '</table>
+				</div>
+				</form>';
+	}
+	$template->footer();
 }
 
 ?>
--- a/plugins/SpecialLog.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialLog.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_speciallog_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_speciallog_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_speciallog_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_speciallog_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -23,409 +23,409 @@
 
 function SpecialLog_paths_init()
 {
-  register_special_page('Log', 'specialpage_log');
+	register_special_page('Log', 'specialpage_log');
 }
 
 function page_Special_Log()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $output;
-  
-  // FIXME: This doesn't currently prohibit viewing of aggregate logs that might include a page for which
-  // we don't have permission to view history. It does, however, block access if a list of pages is given
-  // and one of those doesn't allow history_view.
-  
-  // FIXME: This is a real hack. We're trying to get permissions on a random non-existent article, which
-  // effectively forces calculation to occur based on site-wide permissions.
-  $pid = '';
-  for ( $i = 0; $i < 32; $i++ )
-  {
-    $pid .= chr(mt_rand(32, 126));
-  }
-  $perms = $session->fetch_page_acl($pid, 'Article');
-  $perms_changed = false;
-  
-  require_once(ENANO_ROOT . '/includes/log.php');
-  $log = new LogDisplay();
-  $page = 1;
-  $pagesize = 50;
-  $fmt = 'full';
-  
-  if ( $params = $paths->getAllParams() )
-  {
-    if ( $params === 'AddFilter' && !empty($_POST['type']) && !empty($_POST['value']) )
-    {
-      $type = $_POST['type'];
-      if ( $type == 'within' )
-        $value = strval(intval($_POST['value']['within'])) . $_POST['value']['withinunits'];
-      else
-        $value = $_POST['value'][$type];
-      
-      if ( !ctype_digit($value) )
-        $value = str_replace('/', '.2f', sanitize_page_id($value));
-      
-      if ( $value !== '0' && (empty($value) || ( $type == 'within' && intval($value) == 0 )) )
-      {
-        $adderror = $lang->get('log_err_addfilter_field_empty');
-      }
-      else
-      {
-        $append = ( !empty($_POST['existing_filters']) ) ? "{$_POST['existing_filters']}/" : '';
-        $url = makeUrlNS('Special', "Log/{$append}{$type}={$value}");
-        
-        redirect($url, '', '', 0);
-      }
-    }
-    $params = explode('/', $params);
-    foreach ( $params as $i => $param )
-    {
-      $param = str_replace('.2f', '/', dirtify_page_id($param));
-      if ( preg_match('/^([a-z]+)!?=(.+?)$/', $param, $match) )
-      {
-        $name =& $match[1];
-        $value =& $match[2];
-        switch($name)
-        {
-          case 'resultpage':
-            $page = intval($value);
-            break;
-          case 'size':
-            $pagesize = intval($value);
-            break;
-          case 'fmt':
-            switch($value)
-            {
-              case 'barenaked':
-              case 'ajax':
-                $fmt = 'naked';
-                $output = new Output_Naked();
-                break;
-            }
-            break;
-          case 'page':
-            // tolerate slashes
-            $j = $i;
-            while ( true )
-            {
-              if ( isset($params[++$j]) )
-              {
-                if ( preg_match('/^([a-z]+)!?=(.+?)$/', $params[$j]) )
-                  break;
-                
-                $value .= '/' . $params[$j];
-              }
-              else
-              {
-                break;
-              }
-            }
-            if ( get_class($perms) == 'sessionManager' )
-            {
-              unset($perms);
-              list($pid, $ns) = RenderMan::strToPageID($value);
-              $perms = $session->fetch_page_acl($pid, $ns);
-              if ( !$perms->get_permissions('history_view') )
-              {
-                die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('log_err_access_denied') . '</p>');
-              }
-            }
-            // no break here on purpose
-          default:
-            try
-            {
-              $log->add_criterion($name, $value);
-            }
-            catch ( Exception $e )
-            {
-            }
-            break;
-        }
-      }
-    }
-  }
-  if ( !$perms->get_permissions('history_view') )
-  {
-    die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('log_err_access_denied') . '</p>');
-  }
-  
-  $page--;
-  $rowcount = $log->get_row_count();
-  $paramsbit = rtrim(preg_replace('|/?resultpage=([0-9]+)/?|', '/', $paths->getAllParams()), '/');
-  $paramsbit = ( !empty($paramsbit) ) ? "/$paramsbit" : '';
-  $result_url = makeUrlNS('Special', 'Log' . $paramsbit . '/resultpage=%s', false, true);
-  $paginator = generate_paginator($page, ceil($rowcount / $pagesize), $result_url);
-  
-  $dataset = $log->get_data($page * $pagesize, $pagesize);
-  
-  $output->header();
-  
-  // breadcrumbs
-  if ( $fmt != 'naked' )
-  {
-    echo '<div class="breadcrumbs" style="font-weight: normal;" id="log-breadcrumbs">';
-    echo speciallog_generate_breadcrumbs($log->get_criteria());
-    echo '</div>';
-  
-  // form
-  ?>
-  
-  <!-- Begin filter add form -->
-  
-  <form action="<?php echo makeUrlNS('Special', 'Log/AddFilter', false, true); ?>" method="post" enctype="multipart/form-data">
-    <?php
-    // serialize parameters
-    $params_pre = rtrim(preg_replace('#/?resultpage=[0-9]+/?#', '/', $paths->getAllParams()), '/');
-    echo '<input type="hidden" name="existing_filters" value="' . htmlspecialchars($params_pre) . '" />';
-    ?>
-    <script type="text/javascript">//<![CDATA[
-      addOnloadHook(function()
-        {
-          load_component('jquery');
-          $('#log_addfilter_select').change(function()
-            {
-              var value = $(this).val();
-              $('.log_addfilter').hide();
-              $('#log_addform_' + value).show();
-            });
-          $('#log_addform_' + $('#log_addfilter_select').val()).show();
-        });
-    // ]]>
-    </script>
-    <?php
-    if ( isset($adderror) )
-    {
-      echo '<div class="error-box">' . $adderror . '</div>';
-    }
-    ?>
-    <div class="tblholder">
-    <table border="0" cellspacing="1" cellpadding="4">
-      <tr>
-        <th colspan="2">
-          <?php echo $lang->get('log_heading_addfilter'); ?>
-        </th>
-      </tr>
-      <tr>
-      <td class="row1" style="width: 50%; text-align: right;">
-          <select name="type" id="log_addfilter_select">
-            <option value="user"><?php echo $lang->get('log_form_filtertype_user'); ?></option>
-            <option value="page"><?php echo $lang->get('log_form_filtertype_page'); ?></option>
-            <option value="within"><?php echo $lang->get('log_form_filtertype_within'); ?></option>
-            <option value="action"><?php echo $lang->get('log_form_filtertype_action'); ?></option>
-            <option value="minor"><?php echo $lang->get('log_form_filtertype_minor'); ?></option>
-          </select>
-        </td>
-        <td class="row1" style="width: 50%; text-align: left;">
-          <div class="log_addfilter" id="log_addform_user">
-            <input type="text" class="autofill username" name="value[user]" size="40" />
-          </div>
-          <div class="log_addfilter" id="log_addform_page">
-            <input type="text" class="autofill page" name="value[page]" size="40" />
-          </div>
-          <div class="log_addfilter" id="log_addform_within">
-            <input type="text" name="value[within]" size="7" />
-            <select name="value[withinunits]">
-              <option value="d"><?php echo $lang->get('etc_unit_days'); ?></option>
-              <option value="w"><?php echo $lang->get('etc_unit_weeks'); ?></option>
-              <option value="m"><?php echo $lang->get('etc_unit_months'); ?></option>
-              <option value="y"><?php echo $lang->get('etc_unit_years'); ?></option>
-            </select>
-          </div>
-          <div class="log_addfilter" id="log_addform_action">
-            <select name="value[action]">
-              <option value="rename"><?php echo $lang->get('log_formaction_rename'); ?></option>
-              <option value="create"><?php echo $lang->get('log_formaction_create'); ?></option>
-              <option value="delete"><?php echo $lang->get('log_formaction_delete'); ?></option>
-              <option value="protect"><?php echo $lang->get('log_action_protect'); ?></option>
-              <option value="edit"><?php echo $lang->get('log_action_edit'); ?></option>
-              <option value="edit"><?php echo $lang->get('log_formaction_reupload'); ?></option>
-              <option value="edit"><?php echo $lang->get('log_formaction_votereset'); ?></option>
-            </select>
-          </div>
-          <div class="log_addfilter" id="log_addform_minor">
-            <label>
-              <input type="radio" name="value[minor]" value="1" checked="checked" />
-              <?php echo $lang->get('log_form_filtertype_minor_yes'); ?>
-            </label>
-            <label>
-              <input type="radio" name="value[minor]" value="0" />
-              <?php echo $lang->get('log_form_filtertype_minor_no'); ?>
-            </label>
-          </div>
-        </td>
-      </tr>
-      <tr>
-        <th colspan="2" class="subhead">
-          <input type="submit" value="<?php echo $lang->get('log_btn_add_filter'); ?>" />
-        </th>
-      </tr>
-    </table>
-    </div>
-  
-  </form>
-  
-  <!-- End filter add form -->
-  
-  <?php
-  
-  }
-  
-  // start of actual log output area
-  if ( $fmt != 'naked' )
-  {
-    echo '<div id="log-body">';
-  }
-  
-  if ( $rowcount > 0 )
-  {
-    // we have some results, show pagination + result list
-    echo '<h3 style="float: left;">' . $lang->get('log_heading_logdisplay') . '</h3>';
-    
-    echo $paginator;
-    // padding
-    echo '<div style="height: 10px; clear: both;"></div>';
-    foreach ( $dataset as $row )
-    {
-      echo LogDisplay::render_row($row) . '<br />';
-    }
-    echo $paginator;
-  }
-  else
-  {
-    // no results
-    echo '<h2 class="emptymessage">' . $lang->get('log_msg_no_results') . '</h2>';
-  }
-  
-  if ( $fmt != 'naked' )
-    echo '</div> <!-- div#log-body -->';
-  
-  $output->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $output;
+	
+	// FIXME: This doesn't currently prohibit viewing of aggregate logs that might include a page for which
+	// we don't have permission to view history. It does, however, block access if a list of pages is given
+	// and one of those doesn't allow history_view.
+	
+	// FIXME: This is a real hack. We're trying to get permissions on a random non-existent article, which
+	// effectively forces calculation to occur based on site-wide permissions.
+	$pid = '';
+	for ( $i = 0; $i < 32; $i++ )
+	{
+		$pid .= chr(mt_rand(32, 126));
+	}
+	$perms = $session->fetch_page_acl($pid, 'Article');
+	$perms_changed = false;
+	
+	require_once(ENANO_ROOT . '/includes/log.php');
+	$log = new LogDisplay();
+	$page = 1;
+	$pagesize = 50;
+	$fmt = 'full';
+	
+	if ( $params = $paths->getAllParams() )
+	{
+		if ( $params === 'AddFilter' && !empty($_POST['type']) && !empty($_POST['value']) )
+		{
+			$type = $_POST['type'];
+			if ( $type == 'within' )
+				$value = strval(intval($_POST['value']['within'])) . $_POST['value']['withinunits'];
+			else
+				$value = $_POST['value'][$type];
+			
+			if ( !ctype_digit($value) )
+				$value = str_replace('/', '.2f', sanitize_page_id($value));
+			
+			if ( $value !== '0' && (empty($value) || ( $type == 'within' && intval($value) == 0 )) )
+			{
+				$adderror = $lang->get('log_err_addfilter_field_empty');
+			}
+			else
+			{
+				$append = ( !empty($_POST['existing_filters']) ) ? "{$_POST['existing_filters']}/" : '';
+				$url = makeUrlNS('Special', "Log/{$append}{$type}={$value}");
+				
+				redirect($url, '', '', 0);
+			}
+		}
+		$params = explode('/', $params);
+		foreach ( $params as $i => $param )
+		{
+			$param = str_replace('.2f', '/', dirtify_page_id($param));
+			if ( preg_match('/^([a-z]+)!?=(.+?)$/', $param, $match) )
+			{
+				$name =& $match[1];
+				$value =& $match[2];
+				switch($name)
+				{
+					case 'resultpage':
+						$page = intval($value);
+						break;
+					case 'size':
+						$pagesize = intval($value);
+						break;
+					case 'fmt':
+						switch($value)
+						{
+							case 'barenaked':
+							case 'ajax':
+								$fmt = 'naked';
+								$output = new Output_Naked();
+								break;
+						}
+						break;
+					case 'page':
+						// tolerate slashes
+						$j = $i;
+						while ( true )
+						{
+							if ( isset($params[++$j]) )
+							{
+								if ( preg_match('/^([a-z]+)!?=(.+?)$/', $params[$j]) )
+									break;
+								
+								$value .= '/' . $params[$j];
+							}
+							else
+							{
+								break;
+							}
+						}
+						if ( get_class($perms) == 'sessionManager' )
+						{
+							unset($perms);
+							list($pid, $ns) = RenderMan::strToPageID($value);
+							$perms = $session->fetch_page_acl($pid, $ns);
+							if ( !$perms->get_permissions('history_view') )
+							{
+								die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('log_err_access_denied') . '</p>');
+							}
+						}
+						// no break here on purpose
+					default:
+						try
+						{
+							$log->add_criterion($name, $value);
+						}
+						catch ( Exception $e )
+						{
+						}
+						break;
+				}
+			}
+		}
+	}
+	if ( !$perms->get_permissions('history_view') )
+	{
+		die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('log_err_access_denied') . '</p>');
+	}
+	
+	$page--;
+	$rowcount = $log->get_row_count();
+	$paramsbit = rtrim(preg_replace('|/?resultpage=([0-9]+)/?|', '/', $paths->getAllParams()), '/');
+	$paramsbit = ( !empty($paramsbit) ) ? "/$paramsbit" : '';
+	$result_url = makeUrlNS('Special', 'Log' . $paramsbit . '/resultpage=%s', false, true);
+	$paginator = generate_paginator($page, ceil($rowcount / $pagesize), $result_url);
+	
+	$dataset = $log->get_data($page * $pagesize, $pagesize);
+	
+	$output->header();
+	
+	// breadcrumbs
+	if ( $fmt != 'naked' )
+	{
+		echo '<div class="breadcrumbs" style="font-weight: normal;" id="log-breadcrumbs">';
+		echo speciallog_generate_breadcrumbs($log->get_criteria());
+		echo '</div>';
+	
+	// form
+	?>
+	
+	<!-- Begin filter add form -->
+	
+	<form action="<?php echo makeUrlNS('Special', 'Log/AddFilter', false, true); ?>" method="post" enctype="multipart/form-data">
+		<?php
+		// serialize parameters
+		$params_pre = rtrim(preg_replace('#/?resultpage=[0-9]+/?#', '/', $paths->getAllParams()), '/');
+		echo '<input type="hidden" name="existing_filters" value="' . htmlspecialchars($params_pre) . '" />';
+		?>
+		<script type="text/javascript">//<![CDATA[
+			addOnloadHook(function()
+				{
+					load_component('jquery');
+					$('#log_addfilter_select').change(function()
+						{
+							var value = $(this).val();
+							$('.log_addfilter').hide();
+							$('#log_addform_' + value).show();
+						});
+					$('#log_addform_' + $('#log_addfilter_select').val()).show();
+				});
+		// ]]>
+		</script>
+		<?php
+		if ( isset($adderror) )
+		{
+			echo '<div class="error-box">' . $adderror . '</div>';
+		}
+		?>
+		<div class="tblholder">
+		<table border="0" cellspacing="1" cellpadding="4">
+			<tr>
+				<th colspan="2">
+					<?php echo $lang->get('log_heading_addfilter'); ?>
+				</th>
+			</tr>
+			<tr>
+			<td class="row1" style="width: 50%; text-align: right;">
+					<select name="type" id="log_addfilter_select">
+						<option value="user"><?php echo $lang->get('log_form_filtertype_user'); ?></option>
+						<option value="page"><?php echo $lang->get('log_form_filtertype_page'); ?></option>
+						<option value="within"><?php echo $lang->get('log_form_filtertype_within'); ?></option>
+						<option value="action"><?php echo $lang->get('log_form_filtertype_action'); ?></option>
+						<option value="minor"><?php echo $lang->get('log_form_filtertype_minor'); ?></option>
+					</select>
+				</td>
+				<td class="row1" style="width: 50%; text-align: left;">
+					<div class="log_addfilter" id="log_addform_user">
+						<input type="text" class="autofill username" name="value[user]" size="40" />
+					</div>
+					<div class="log_addfilter" id="log_addform_page">
+						<input type="text" class="autofill page" name="value[page]" size="40" />
+					</div>
+					<div class="log_addfilter" id="log_addform_within">
+						<input type="text" name="value[within]" size="7" />
+						<select name="value[withinunits]">
+							<option value="d"><?php echo $lang->get('etc_unit_days'); ?></option>
+							<option value="w"><?php echo $lang->get('etc_unit_weeks'); ?></option>
+							<option value="m"><?php echo $lang->get('etc_unit_months'); ?></option>
+							<option value="y"><?php echo $lang->get('etc_unit_years'); ?></option>
+						</select>
+					</div>
+					<div class="log_addfilter" id="log_addform_action">
+						<select name="value[action]">
+							<option value="rename"><?php echo $lang->get('log_formaction_rename'); ?></option>
+							<option value="create"><?php echo $lang->get('log_formaction_create'); ?></option>
+							<option value="delete"><?php echo $lang->get('log_formaction_delete'); ?></option>
+							<option value="protect"><?php echo $lang->get('log_action_protect'); ?></option>
+							<option value="edit"><?php echo $lang->get('log_action_edit'); ?></option>
+							<option value="edit"><?php echo $lang->get('log_formaction_reupload'); ?></option>
+							<option value="edit"><?php echo $lang->get('log_formaction_votereset'); ?></option>
+						</select>
+					</div>
+					<div class="log_addfilter" id="log_addform_minor">
+						<label>
+							<input type="radio" name="value[minor]" value="1" checked="checked" />
+							<?php echo $lang->get('log_form_filtertype_minor_yes'); ?>
+						</label>
+						<label>
+							<input type="radio" name="value[minor]" value="0" />
+							<?php echo $lang->get('log_form_filtertype_minor_no'); ?>
+						</label>
+					</div>
+				</td>
+			</tr>
+			<tr>
+				<th colspan="2" class="subhead">
+					<input type="submit" value="<?php echo $lang->get('log_btn_add_filter'); ?>" />
+				</th>
+			</tr>
+		</table>
+		</div>
+	
+	</form>
+	
+	<!-- End filter add form -->
+	
+	<?php
+	
+	}
+	
+	// start of actual log output area
+	if ( $fmt != 'naked' )
+	{
+		echo '<div id="log-body">';
+	}
+	
+	if ( $rowcount > 0 )
+	{
+		// we have some results, show pagination + result list
+		echo '<h3 style="float: left;">' . $lang->get('log_heading_logdisplay') . '</h3>';
+		
+		echo $paginator;
+		// padding
+		echo '<div style="height: 10px; clear: both;"></div>';
+		foreach ( $dataset as $row )
+		{
+			echo LogDisplay::render_row($row) . '<br />';
+		}
+		echo $paginator;
+	}
+	else
+	{
+		// no results
+		echo '<h2 class="emptymessage">' . $lang->get('log_msg_no_results') . '</h2>';
+	}
+	
+	if ( $fmt != 'naked' )
+		echo '</div> <!-- div#log-body -->';
+	
+	$output->footer();
 }
 
 function speciallog_generate_breadcrumbs($criteria)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  if ( count($criteria) == 0 )
-  {
-    return $lang->get('log_msg_no_filters');
-  }
-  
-  $html = array();
-  foreach ( $criteria as $criterion )
-  {
-    list($type, $value) = $criterion;
-    switch($type)
-    {
-      case 'user':
-        $rank_info = $session->get_user_rank($value);
-        $user_link = '<a href="' . makeUrlNS('User', $value, false, true) . '" style="' . $rank_info['rank_style'] . '" title="' . htmlspecialchars($lang->get($rank_info['rank_title'])) . '">';
-        $user_link .= htmlspecialchars(str_replace('_', ' ', $value)) . '</a>';
-        
-        $crumb = $lang->get('log_breadcrumb_author', array('user' => $user_link));
-        break;
-      case 'page':
-        list($pid, $ns) = RenderMan::strToPageID($value);
-        $ns = namespace_factory($pid, $ns);
-        $exist = $ns->exists() ? '' : ' class="wikilink-nonexistent"';
-        $crumb = $lang->get('log_breadcrumb_page', array('page' => '<a' . $exist . ' href="' . makeUrl($value, false, true) . '">' . htmlspecialchars($ns->title) . '</a>'));
-        break;
-      case 'action':
-        $action = ( $lang->get("log_formaction_{$value}") === "log_formaction_{$value}" ) ? $lang->get("log_action_{$value}") : $lang->get("log_formaction_{$value}");
-        $crumb = $lang->get('log_breadcrumb_action', array('action' => htmlspecialchars($action)));
-        break;
-      case 'minor':
-        $crumb = $value == '1' ? $lang->get('log_form_filtertype_minor_yes') : $lang->get('log_form_filtertype_minor_no');
-        break;
-      case 'within':
-        $value = intval($value);
-        if ( $value % 31536000 == 0 )
-        {
-          $n = $value / 31536000;
-          $value = "$n " . $lang->get( $n > 1 ? 'etc_unit_years' : 'etc_unit_year' );
-        }
-        else if ( $value % 2592000 == 0 )
-        {
-          $n = $value / 2592000;
-          $value = "$n " . $lang->get( $n > 1 ? 'etc_unit_months' : 'etc_unit_month' );
-        }
-        else if ( $value % 604800 == 0 )
-        {
-          $n = $value / 604800;
-          $value = "$n " . $lang->get( $n > 1 ? 'etc_unit_weeks' : 'etc_unit_week' );
-        }
-        else if ( $value % 86400 == 0 )
-        {
-          $n = $value / 86400;
-          $value = "$n " . $lang->get( $n > 1 ? 'etc_unit_days' : 'etc_unit_day' );
-        }
-        else
-        {
-          $value = "$value " . $lang->get( $value > 1 ? 'etc_unit_seconds' : 'etc_unit_second' );
-        }
-        $crumb = $lang->get('log_breadcrumb_within', array('time' => $value));
-        break;
-    }
-    $html[] = $crumb . ' ' . speciallog_crumb_remove_link($criterion);
-  }
-  return implode(' &raquo; ', $html);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	if ( count($criteria) == 0 )
+	{
+		return $lang->get('log_msg_no_filters');
+	}
+	
+	$html = array();
+	foreach ( $criteria as $criterion )
+	{
+		list($type, $value) = $criterion;
+		switch($type)
+		{
+			case 'user':
+				$rank_info = $session->get_user_rank($value);
+				$user_link = '<a href="' . makeUrlNS('User', $value, false, true) . '" style="' . $rank_info['rank_style'] . '" title="' . htmlspecialchars($lang->get($rank_info['rank_title'])) . '">';
+				$user_link .= htmlspecialchars(str_replace('_', ' ', $value)) . '</a>';
+				
+				$crumb = $lang->get('log_breadcrumb_author', array('user' => $user_link));
+				break;
+			case 'page':
+				list($pid, $ns) = RenderMan::strToPageID($value);
+				$ns = namespace_factory($pid, $ns);
+				$exist = $ns->exists() ? '' : ' class="wikilink-nonexistent"';
+				$crumb = $lang->get('log_breadcrumb_page', array('page' => '<a' . $exist . ' href="' . makeUrl($value, false, true) . '">' . htmlspecialchars($ns->title) . '</a>'));
+				break;
+			case 'action':
+				$action = ( $lang->get("log_formaction_{$value}") === "log_formaction_{$value}" ) ? $lang->get("log_action_{$value}") : $lang->get("log_formaction_{$value}");
+				$crumb = $lang->get('log_breadcrumb_action', array('action' => htmlspecialchars($action)));
+				break;
+			case 'minor':
+				$crumb = $value == '1' ? $lang->get('log_form_filtertype_minor_yes') : $lang->get('log_form_filtertype_minor_no');
+				break;
+			case 'within':
+				$value = intval($value);
+				if ( $value % 31536000 == 0 )
+				{
+					$n = $value / 31536000;
+					$value = "$n " . $lang->get( $n > 1 ? 'etc_unit_years' : 'etc_unit_year' );
+				}
+				else if ( $value % 2592000 == 0 )
+				{
+					$n = $value / 2592000;
+					$value = "$n " . $lang->get( $n > 1 ? 'etc_unit_months' : 'etc_unit_month' );
+				}
+				else if ( $value % 604800 == 0 )
+				{
+					$n = $value / 604800;
+					$value = "$n " . $lang->get( $n > 1 ? 'etc_unit_weeks' : 'etc_unit_week' );
+				}
+				else if ( $value % 86400 == 0 )
+				{
+					$n = $value / 86400;
+					$value = "$n " . $lang->get( $n > 1 ? 'etc_unit_days' : 'etc_unit_day' );
+				}
+				else
+				{
+					$value = "$value " . $lang->get( $value > 1 ? 'etc_unit_seconds' : 'etc_unit_second' );
+				}
+				$crumb = $lang->get('log_breadcrumb_within', array('time' => $value));
+				break;
+		}
+		$html[] = $crumb . ' ' . speciallog_crumb_remove_link($criterion);
+	}
+	return implode(' &raquo; ', $html);
 }
 
 function speciallog_crumb_remove_link($criterion)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  list($type, $value) = $criterion;
-  
-  $params = explode('/', dirtify_page_id($paths->getAllParams()));
-  foreach ( $params as $i => $param )
-  {
-    if ( $param === "$type=$value" )
-    {
-      unset($params[$i]);
-      break;
-    }
-    else if ( $type === 'within' )
-    {
-      list($ptype, $pvalue) = explode('=', $param);
-      if ( $ptype !== 'within' )
-        continue;
-      
-      $lastchar = substr($pvalue, -1);
-      $amt = intval($pvalue);
-      switch($lastchar)
-      {
-        case 'd':
-          $amt = $amt * 86400;
-          break;
-        case 'w':
-          $amt = $amt * 604800;
-          break;
-        case 'm':
-          $amt = $amt * 2592000;
-          break;
-        case 'y':
-          $amt = $amt * 31536000;
-          break;
-      }
-      if ( $amt === $value )
-      {
-        unset($params[$i]);
-        break;
-      }
-    }
-  }
-  if ( count($params) > 0 )
-  {
-    $params = implode('/', $params);
-    $url = makeUrlNS('Special', "Log/$params", false, true);
-  }
-  else
-  {
-    $url = makeUrlNS('Special', "Log", false, true);
-  }
-  
-  return '<sup><a href="' . $url . '">(x)</a></sup>';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	list($type, $value) = $criterion;
+	
+	$params = explode('/', dirtify_page_id($paths->getAllParams()));
+	foreach ( $params as $i => $param )
+	{
+		if ( $param === "$type=$value" )
+		{
+			unset($params[$i]);
+			break;
+		}
+		else if ( $type === 'within' )
+		{
+			list($ptype, $pvalue) = explode('=', $param);
+			if ( $ptype !== 'within' )
+				continue;
+			
+			$lastchar = substr($pvalue, -1);
+			$amt = intval($pvalue);
+			switch($lastchar)
+			{
+				case 'd':
+					$amt = $amt * 86400;
+					break;
+				case 'w':
+					$amt = $amt * 604800;
+					break;
+				case 'm':
+					$amt = $amt * 2592000;
+					break;
+				case 'y':
+					$amt = $amt * 31536000;
+					break;
+			}
+			if ( $amt === $value )
+			{
+				unset($params[$i]);
+				break;
+			}
+		}
+	}
+	if ( count($params) > 0 )
+	{
+		$params = implode('/', $params);
+		$url = makeUrlNS('Special', "Log/$params", false, true);
+	}
+	else
+	{
+		$url = makeUrlNS('Special', "Log", false, true);
+	}
+	
+	return '<sup><a href="' . $url . '">(x)</a></sup>';
 }
--- a/plugins/SpecialPageFuncs.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialPageFuncs.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_specialpagefuncs_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_specialpagefuncs_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_specialpagefuncs_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_specialpagefuncs_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -27,679 +27,679 @@
 
 function SpecialPageFuncs_paths_init()
 {
-  register_special_page('CreatePage', 'specialpage_create_page');
-  register_special_page('AllPages', 'specialpage_all_pages');
-  register_special_page('SpecialPages', 'specialpage_special_pages');
-  register_special_page('About_Enano', 'specialpage_about_enano');
-  register_special_page('GNU_General_Public_License', 'specialpage_gnu_gpl');
-  register_special_page('TagCloud', 'specialpage_tag_cloud');
-  register_special_page('Autofill', 'specialpage_autofill', false);
+	register_special_page('CreatePage', 'specialpage_create_page');
+	register_special_page('AllPages', 'specialpage_all_pages');
+	register_special_page('SpecialPages', 'specialpage_special_pages');
+	register_special_page('About_Enano', 'specialpage_about_enano');
+	register_special_page('GNU_General_Public_License', 'specialpage_gnu_gpl');
+	register_special_page('TagCloud', 'specialpage_tag_cloud');
+	register_special_page('Autofill', 'specialpage_autofill', false);
 }
 
 // function names are IMPORTANT!!! The name pattern is: page_<namespace ID>_<page URLname, without namespace>
 
 function page_Special_CreatePage()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $whitelist_ns = array('Article', 'User', 'Help', 'Template', 'Category', 'Project');
-  if ( $session->user_level >= USER_LEVEL_ADMIN )
-  {
-    $whitelist_ns[] = 'System';
-  }
-  $code = $plugins->setHook('page_create_ns_whitelist');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  $errors = array();
-  
-  switch ( isset($_POST['page_title']) )
-  {
-    case true:
-      // "Create page" was clicked
-      
-      //
-      // VALIDATION CODE
-      //
-      
-      // Check namespace
-      $namespace = ( isset($_POST['namespace']) ) ? $_POST['namespace'] : 'Article';
-      if ( !in_array($namespace, $whitelist_ns) )
-      {
-        $errors[] = $lang->get('pagetools_create_err_invalid_namespace');
-      }
-      
-      // Check title and figure out urlname
-      $title = $_POST['page_title'];
-      $urlname = $_POST['page_title'];
-      if ( @$_POST['custom_url'] === 'yes' && isset($_POST['urlname']) )
-      {
-        $urlname = $_POST['urlname'];
-      }
-      $urlname = sanitize_page_id($urlname);
-      if ( $urlname == '.00' || empty($urlname) )
-      {
-        $errors[] = $lang->get('pagetools_create_err_invalid_urlname');
-      }
-      
-      // Validate page existence
-      $pathskey = $paths->nslist[$namespace] . $urlname;
-      if ( isPage($pathskey) )
-      {
-        $errors[] = $lang->get('pagetools_create_err_already_exists');
-      }
-      
-      // Validate permissions
-      $perms = $session->fetch_page_acl($urlname, $namespace);
-      if ( !$perms->get_permissions('create_page') )
-      {
-        $errors[] = $lang->get('pagetools_create_err_no_permission');
-      }
-      
-      // Run hooks
-      $code = $plugins->setHook('page_create_request');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      
-      // Create the page
-      if ( count($errors) < 1 )
-      {
-        $page = new PageProcessor($urlname, $namespace);
-        $page->create_page($title);
-        if ( $error = $page->pop_error() )
-        {
-          do
-          {
-            $errors[] = $error;
-          }
-          while ( $error = $page->pop_error() );
-        }
-        else
-        {
-          redirect(makeUrlNS($namespace, $urlname) . '#do:edit', '', '', 0);
-          return true;
-        }
-      }
-      
-      break;
-  }
-  
-  $template->header();
-  
-  echo $lang->get('pagetools_create_blurb');
-  
-  if ( count($errors) > 0 )
-  {
-    echo '<div class="error-box">' . implode("<br />\n        ", $errors) . '</div>';
-  }
-  
-  ?>
-  <enano:no-opt>
-  <script type="text/javascript">
-    window.cpGenPreviewUrl = function()
-    {
-      if ( typeof(load_component) != 'function' )
-        return false;
-      
-      var frm = document.forms['create_form'];
-      var radio_custom = frm.getElementsByTagName('input')[2];
-      var use_custom_url = radio_custom.checked;
-      if ( use_custom_url )
-      {
-        var title_src = frm.urlname.value;
-      }
-      else
-      {
-        var title_src = frm.page_title.value;
-      }
-      var url = window.location.protocol + '//' + window.location.hostname + contentPath + namespace_list[frm.namespace.value] + sanitize_page_id(title_src);
-      document.getElementById('createpage_url_preview').firstChild.nodeValue = url;
-    }
-  </script>
-  </enano:no-opt>
-  <?php
-  
-  echo '<form action="' . makeUrlNS('Special', 'CreatePage') . '" method="post" name="create_form">';
-  
-  echo '<p>';
-    echo $lang->get('pagetools_create_field_title');
-    echo ' <input onkeyup="cpGenPreviewUrl();" type="text" name="page_title" size="40" tabindex="1" />';
-    echo '</p>';
-    
-  echo '<p>';
-    echo $lang->get('pagetools_create_field_namespace');
-    echo ' <select onchange="cpGenPreviewUrl();" name="namespace" tabindex="2">';
-    foreach ( $paths->nslist as $ns => $ns_prefix )
-    {
-      if ( !in_array($ns, $whitelist_ns) )
-        continue;
-      $lang_string = 'onpage_lbl_page_' . strtolower($ns);
-      $str = $lang->get($lang_string);
-      if ( $str == $lang_string )
-        $str = $ns;
-      
-      echo '<option value="' . $ns . '">' . ucwords($str) . '</option>';
-    }
-    echo '</select>';
-    echo '</p>';
-    
-  echo '<fieldset enano:expand="closed">';
-  echo '<legend>' . $lang->get('pagetools_create_group_advanced') . '</legend>';
-  
-  echo '<p>';
-    echo '<label><input tabindex="3" type="radio" name="custom_url" value="no" checked="checked" onclick="cpGenPreviewUrl(); document.getElementById(\'createpage_custom_url\').style.display = \'none\';" /> ' . $lang->get('pagetools_create_field_url_auto') . '</label>';
-    echo '</p>';
-  
-  echo '<p>';
-    echo '<label><input tabindex="3" type="radio" name="custom_url" value="yes" onclick="cpGenPreviewUrl(); document.getElementById(\'createpage_custom_url\').style.display = \'block\';" /> ' . $lang->get('pagetools_create_field_url_manual') . '</label>';
-    echo '</p>';
-  
-  echo '<p id="createpage_custom_url" style="display: none; margin-left: 2em;">';
-    echo $lang->get('pagetools_create_field_url');
-    echo ' <input onkeyup="cpGenPreviewUrl();" tabindex="4" type="text" name="urlname" value="" size="40" />';
-    echo '</p>';
-    
-  echo '<p>';
-    echo $lang->get('pagetools_create_field_preview') . ' <tt id="createpage_url_preview"> </tt><br />';
-    echo '<small>' . $lang->get('pagetools_create_field_preview_hint') . '</small>';
-    echo '</p>';
-  
-  echo '</fieldset>';
-  
-  echo '<p>';
-    echo '<input tabindex="5" type="submit" value="' . $lang->get('pagetools_create_btn_create') . '" />';
-    echo '</p>';
-    
-  echo '</form>';
-  
-  echo '<script type="text/javascript">addOnloadHook(cpGenPreviewUrl); addOnloadHook(function(){load_component(\'expander\')});</script>';
-  
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$whitelist_ns = array('Article', 'User', 'Help', 'Template', 'Category', 'Project');
+	if ( $session->user_level >= USER_LEVEL_ADMIN )
+	{
+		$whitelist_ns[] = 'System';
+	}
+	$code = $plugins->setHook('page_create_ns_whitelist');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	$errors = array();
+	
+	switch ( isset($_POST['page_title']) )
+	{
+		case true:
+			// "Create page" was clicked
+			
+			//
+			// VALIDATION CODE
+			//
+			
+			// Check namespace
+			$namespace = ( isset($_POST['namespace']) ) ? $_POST['namespace'] : 'Article';
+			if ( !in_array($namespace, $whitelist_ns) )
+			{
+				$errors[] = $lang->get('pagetools_create_err_invalid_namespace');
+			}
+			
+			// Check title and figure out urlname
+			$title = $_POST['page_title'];
+			$urlname = $_POST['page_title'];
+			if ( @$_POST['custom_url'] === 'yes' && isset($_POST['urlname']) )
+			{
+				$urlname = $_POST['urlname'];
+			}
+			$urlname = sanitize_page_id($urlname);
+			if ( $urlname == '.00' || empty($urlname) )
+			{
+				$errors[] = $lang->get('pagetools_create_err_invalid_urlname');
+			}
+			
+			// Validate page existence
+			$pathskey = $paths->nslist[$namespace] . $urlname;
+			if ( isPage($pathskey) )
+			{
+				$errors[] = $lang->get('pagetools_create_err_already_exists');
+			}
+			
+			// Validate permissions
+			$perms = $session->fetch_page_acl($urlname, $namespace);
+			if ( !$perms->get_permissions('create_page') )
+			{
+				$errors[] = $lang->get('pagetools_create_err_no_permission');
+			}
+			
+			// Run hooks
+			$code = $plugins->setHook('page_create_request');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			
+			// Create the page
+			if ( count($errors) < 1 )
+			{
+				$page = new PageProcessor($urlname, $namespace);
+				$page->create_page($title);
+				if ( $error = $page->pop_error() )
+				{
+					do
+					{
+						$errors[] = $error;
+					}
+					while ( $error = $page->pop_error() );
+				}
+				else
+				{
+					redirect(makeUrlNS($namespace, $urlname) . '#do:edit', '', '', 0);
+					return true;
+				}
+			}
+			
+			break;
+	}
+	
+	$template->header();
+	
+	echo $lang->get('pagetools_create_blurb');
+	
+	if ( count($errors) > 0 )
+	{
+		echo '<div class="error-box">' . implode("<br />\n        ", $errors) . '</div>';
+	}
+	
+	?>
+	<enano:no-opt>
+	<script type="text/javascript">
+		window.cpGenPreviewUrl = function()
+		{
+			if ( typeof(load_component) != 'function' )
+				return false;
+			
+			var frm = document.forms['create_form'];
+			var radio_custom = frm.getElementsByTagName('input')[2];
+			var use_custom_url = radio_custom.checked;
+			if ( use_custom_url )
+			{
+				var title_src = frm.urlname.value;
+			}
+			else
+			{
+				var title_src = frm.page_title.value;
+			}
+			var url = window.location.protocol + '//' + window.location.hostname + contentPath + namespace_list[frm.namespace.value] + sanitize_page_id(title_src);
+			document.getElementById('createpage_url_preview').firstChild.nodeValue = url;
+		}
+	</script>
+	</enano:no-opt>
+	<?php
+	
+	echo '<form action="' . makeUrlNS('Special', 'CreatePage') . '" method="post" name="create_form">';
+	
+	echo '<p>';
+		echo $lang->get('pagetools_create_field_title');
+		echo ' <input onkeyup="cpGenPreviewUrl();" type="text" name="page_title" size="40" tabindex="1" />';
+		echo '</p>';
+		
+	echo '<p>';
+		echo $lang->get('pagetools_create_field_namespace');
+		echo ' <select onchange="cpGenPreviewUrl();" name="namespace" tabindex="2">';
+		foreach ( $paths->nslist as $ns => $ns_prefix )
+		{
+			if ( !in_array($ns, $whitelist_ns) )
+				continue;
+			$lang_string = 'onpage_lbl_page_' . strtolower($ns);
+			$str = $lang->get($lang_string);
+			if ( $str == $lang_string )
+				$str = $ns;
+			
+			echo '<option value="' . $ns . '">' . ucwords($str) . '</option>';
+		}
+		echo '</select>';
+		echo '</p>';
+		
+	echo '<fieldset enano:expand="closed">';
+	echo '<legend>' . $lang->get('pagetools_create_group_advanced') . '</legend>';
+	
+	echo '<p>';
+		echo '<label><input tabindex="3" type="radio" name="custom_url" value="no" checked="checked" onclick="cpGenPreviewUrl(); document.getElementById(\'createpage_custom_url\').style.display = \'none\';" /> ' . $lang->get('pagetools_create_field_url_auto') . '</label>';
+		echo '</p>';
+	
+	echo '<p>';
+		echo '<label><input tabindex="3" type="radio" name="custom_url" value="yes" onclick="cpGenPreviewUrl(); document.getElementById(\'createpage_custom_url\').style.display = \'block\';" /> ' . $lang->get('pagetools_create_field_url_manual') . '</label>';
+		echo '</p>';
+	
+	echo '<p id="createpage_custom_url" style="display: none; margin-left: 2em;">';
+		echo $lang->get('pagetools_create_field_url');
+		echo ' <input onkeyup="cpGenPreviewUrl();" tabindex="4" type="text" name="urlname" value="" size="40" />';
+		echo '</p>';
+		
+	echo '<p>';
+		echo $lang->get('pagetools_create_field_preview') . ' <tt id="createpage_url_preview"> </tt><br />';
+		echo '<small>' . $lang->get('pagetools_create_field_preview_hint') . '</small>';
+		echo '</p>';
+	
+	echo '</fieldset>';
+	
+	echo '<p>';
+		echo '<input tabindex="5" type="submit" value="' . $lang->get('pagetools_create_btn_create') . '" />';
+		echo '</p>';
+		
+	echo '</form>';
+	
+	echo '<script type="text/javascript">addOnloadHook(cpGenPreviewUrl); addOnloadHook(function(){load_component(\'expander\')});</script>';
+	
+	$template->footer();
 }
 
 function PagelistingFormatter($id, $row)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  static $rowtracker = 0;
-  static $tdclass = 'row2';
-  static $per_row = 2;
-  static $first = true;
-  $return = '';
-  if ( $id === false && $row === false )
-  {
-    $rowtracker = 0;
-    $first = true;
-    return false;
-  }
-  $rowtracker++;
-  if ( $rowtracker == $per_row || $first )
-  {
-    $rowtracker = 0;
-    $tdclass = ( $tdclass == 'row2' ) ? 'row1' : 'row2';
-  }
-  if ( $rowtracker == 0 && !$first )
-    $return .= "</tr>\n<tr>";
-  
-  $first = false;
-  
-  preg_match('/^ns=(' . implode('|', array_keys($paths->nslist)) . ');pid=(.*?)$/i', $id, $match);
-  $namespace =& $match[1];
-  $page_id   =& $match[2];
-  $page_id   = sanitize_page_id($page_id);
-  
-  $url = makeUrlNS($namespace, $page_id);
-  $url = htmlspecialchars($url);
-  
-  $link = '<a href="' . $url . '">' . htmlspecialchars($row['name']) . '</a>';
-  $td = '<td class="' . $tdclass . '" style="width: 50%;">' . $link . '</td>';
-  
-  $return .= $td;
-  
-  return $return;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	static $rowtracker = 0;
+	static $tdclass = 'row2';
+	static $per_row = 2;
+	static $first = true;
+	$return = '';
+	if ( $id === false && $row === false )
+	{
+		$rowtracker = 0;
+		$first = true;
+		return false;
+	}
+	$rowtracker++;
+	if ( $rowtracker == $per_row || $first )
+	{
+		$rowtracker = 0;
+		$tdclass = ( $tdclass == 'row2' ) ? 'row1' : 'row2';
+	}
+	if ( $rowtracker == 0 && !$first )
+		$return .= "</tr>\n<tr>";
+	
+	$first = false;
+	
+	preg_match('/^ns=(' . implode('|', array_keys($paths->nslist)) . ');pid=(.*?)$/i', $id, $match);
+	$namespace =& $match[1];
+	$page_id   =& $match[2];
+	$page_id   = sanitize_page_id($page_id);
+	
+	$url = makeUrlNS($namespace, $page_id);
+	$url = htmlspecialchars($url);
+	
+	$link = '<a href="' . $url . '">' . htmlspecialchars($row['name']) . '</a>';
+	$td = '<td class="' . $tdclass . '" style="width: 50%;">' . $link . '</td>';
+	
+	$return .= $td;
+	
+	return $return;
 }
 
 function page_Special_AllPages() 
 {
-  // This should be an easy one
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $template->header();
-  echo '<p>' . $lang->get('pagetools_allpages_blurb') . '</p>';
-  
-  $q = $db->sql_query('SELECT COUNT(urlname) FROM '.table_prefix.'pages WHERE visible!=0;');
-  if ( !$q )
-    $db->_die();
-  $row = $db->fetchrow_num();
-  $count = $row[0];
-  $sz =& $count;
-  
-  switch($count % 4)
-  {
-    case 0:
-    case 2:
-      // even number of results; do nothing
-      $last_cell = '';
-      break;
-    case 1:
-      // odd number of results and odd number of rows, use row1
-      $last_cell = '<td class="row1"></td>';
-      break;
-    case 3:
-      // odd number of results and even number of rows, use row2
-      $last_cell = '<td class="row2"></td>';
-      break;
-  }
-  
-  $db->free_result();
-  
-  // This query needs to be generated based on the DBMS
-  $concat_column = ENANO_DBLAYER == 'MYSQL'
-                     ? 'CONCAT("ns=",namespace,";pid=",urlname)'
-                     : "'ns=' || namespace || ';pid=' || urlname";
-  
-  $q = $db->sql_unbuffered_query("SELECT $concat_column AS identifier, name FROM " . table_prefix . "pages WHERE visible != 0 ORDER BY name ASC;");
-  if ( !$q )
-    $db->_die();
-  
-  $offset = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0;
-  
-  // reset formatter
-  PagelistingFormatter(false, false);
-  
-  $result = paginate(
-      $q,                  // result resource
-      '{identifier}',      // formatting template
-      $count,              // # of results
-      makeUrlNS('Special', 'AllPages', 'offset=%s'), // result URL
-      $offset,             // start offset
-      40,                  // results per page
-      array( 'identifier' => 'PagelistingFormatter' ), // hooks
-      '<div class="tblholder">
-         <table border="0" cellspacing="1" cellpadding="4">
-           <tr>',          // print at start
-      '    ' . $last_cell . '</tr>
-         </table>
-       </div>'             // print at end
-       );
-  
-  echo $result;
-  
-  $template->footer();
+	// This should be an easy one
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$template->header();
+	echo '<p>' . $lang->get('pagetools_allpages_blurb') . '</p>';
+	
+	$q = $db->sql_query('SELECT COUNT(urlname) FROM '.table_prefix.'pages WHERE visible!=0;');
+	if ( !$q )
+		$db->_die();
+	$row = $db->fetchrow_num();
+	$count = $row[0];
+	$sz =& $count;
+	
+	switch($count % 4)
+	{
+		case 0:
+		case 2:
+			// even number of results; do nothing
+			$last_cell = '';
+			break;
+		case 1:
+			// odd number of results and odd number of rows, use row1
+			$last_cell = '<td class="row1"></td>';
+			break;
+		case 3:
+			// odd number of results and even number of rows, use row2
+			$last_cell = '<td class="row2"></td>';
+			break;
+	}
+	
+	$db->free_result();
+	
+	// This query needs to be generated based on the DBMS
+	$concat_column = ENANO_DBLAYER == 'MYSQL'
+ 										? 'CONCAT("ns=",namespace,";pid=",urlname)'
+ 										: "'ns=' || namespace || ';pid=' || urlname";
+	
+	$q = $db->sql_unbuffered_query("SELECT $concat_column AS identifier, name FROM " . table_prefix . "pages WHERE visible != 0 ORDER BY name ASC;");
+	if ( !$q )
+		$db->_die();
+	
+	$offset = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0;
+	
+	// reset formatter
+	PagelistingFormatter(false, false);
+	
+	$result = paginate(
+			$q,                  // result resource
+			'{identifier}',      // formatting template
+			$count,              // # of results
+			makeUrlNS('Special', 'AllPages', 'offset=%s'), // result URL
+			$offset,             // start offset
+			40,                  // results per page
+			array( 'identifier' => 'PagelistingFormatter' ), // hooks
+			'<div class="tblholder">
+ 				<table border="0" cellspacing="1" cellpadding="4">
+ 					<tr>',          // print at start
+			'    ' . $last_cell . '</tr>
+ 				</table>
+ 			</div>'             // print at end
+ 			);
+	
+	echo $result;
+	
+	$template->footer();
 }
 
 function page_Special_SpecialPages()
 {
-  // This should be an easy one
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $template->header();
-  echo '<p>' . $lang->get('pagetools_specialpages_blurb') . '</p><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr>';
-  $cclass = 'row1';
-  $i = -1;
-  foreach ( $paths->pages as $cdata )
-  {
-    if ( $cdata['namespace'] != 'Special' )
-      continue;
-    
-    $i++;
-    if ( $i % 2 == 0 && $i > 0 )
-    {
-      echo '</tr><tr>';
-      $cclass = ( $cclass == 'row1' ) ? 'row3' : 'row1';
-    }
-    echo '<td style="width: 50%;" class="' . $cclass . '">';
-    echo '<a href="' . makeUrl($cdata['urlname']) . '">';
-    echo htmlspecialchars($cdata['name']);
-    echo '</a>';
-    echo '</td>';
-  }
-  // close up the table if necessary
-  if ( $i % 2 > 0 )
-  {
-    echo "<td class=\"$cclass\"></td>";
-  }
-  echo '</table></div>';
-  $template->footer();
+	// This should be an easy one
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$template->header();
+	echo '<p>' . $lang->get('pagetools_specialpages_blurb') . '</p><div class="tblholder"><table border="0" width="100%" cellspacing="1" cellpadding="4"><tr>';
+	$cclass = 'row1';
+	$i = -1;
+	foreach ( $paths->pages as $cdata )
+	{
+		if ( $cdata['namespace'] != 'Special' )
+			continue;
+		
+		$i++;
+		if ( $i % 2 == 0 && $i > 0 )
+		{
+			echo '</tr><tr>';
+			$cclass = ( $cclass == 'row1' ) ? 'row3' : 'row1';
+		}
+		echo '<td style="width: 50%;" class="' . $cclass . '">';
+		echo '<a href="' . makeUrl($cdata['urlname']) . '">';
+		echo htmlspecialchars($cdata['name']);
+		echo '</a>';
+		echo '</td>';
+	}
+	// close up the table if necessary
+	if ( $i % 2 > 0 )
+	{
+		echo "<td class=\"$cclass\"></td>";
+	}
+	echo '</table></div>';
+	$template->footer();
 }
 
 function page_Special_About_Enano()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $platform = 'Unknown';
-  $uname = @file_get_contents('/proc/sys/kernel/ostype');
-  if($uname == "Linux\n")
-    $platform = 'Linux';
-  else if(@file_exists('/hurd/pfinet')) // I have a little experience with GNU/Hurd :-) http://hurdvm.enanocms.org/
-    $platform = 'GNU/Hurd';
-  else if(strtolower(PHP_OS) == 'winnt')
-    $platform = 'Windows NT';
-  else if(strtolower(PHP_OS) == 'win32')
-    $platform = 'Windows 9x/DOS';
-  else if(@file_exists('/System/Library/CoreServices/SystemVersion.plist'))
-    $platform = 'Mac OS X';
-  else if(@file_exists('/bin/bash'))
-    $platform = 'Other GNU';
-  else if(@is_dir('/bin'))
-    $platform = 'Other POSIX';
-  $template->header();
-  ?>
-  <br />
-  <div class="tblholder">
-    <table border="0" cellspacing="1" cellpadding="4">
-      <tr><th colspan="2" style="text-align: left;"><?php echo $lang->get('meta_enano_about_th'); ?></th></tr>
-      <tr><td colspan="2" class="row3">
-        <?php
-        echo $lang->get('meta_enano_about_poweredby', array(
-            'year'     => date('Y')
-          ));
-        $subst = array(
-            'gpl_link' => makeUrlNS('Special', 'GNU_General_Public_License')
-          );
-        echo $lang->get('meta_enano_about_gpl', $subst);
-        if ( $lang->lang_code != 'eng' ):
-        // Do not remove this block of code. Doing so is a violation of the GPL. (A copy of the GPL in other languages
-        // must be accompanied by a copy of the English GPL.)
-        ?>
-        <h3>(English)</h3>
-        <p>
-          This website is powered by <a href="http://enanocms.org/">Enano</a>, the lightweight and open source CMS that everyone can use.
-          Enano is copyright &copy; 2006-<?php echo date('Y'); ?> Dan Fuhry. For legal information, along with a list of libraries that
-          Enano uses, please see <a href="http://enanocms.org/Legal_information">Legal Information</a>.
-        </p>
-        <p>
-          The developers and maintainers of Enano strongly believe that software should not only be free to use, but free to be modified,
-          distributed, and used to create derivative works. To help achieve this goal, we use licensing terms that require you to pass on
-          the freedoms we give you when you share Enano. For more information about Free Software, check out the
-          <a href="http://en.wikipedia.org/wiki/Free_Software" onclick="window.open(this.href); return false;">Wikipedia page</a> or
-          the <a href="http://www.fsf.org/" onclick="window.open(this.href); return false;">Free Software Foundation's</a> homepage.
-        </p>
-        <p>
-          This program is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License
-          as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
-        </p>
-        <p>
-          This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
-          warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
-        </p>
-        <p>
-          You should have received <a href="<?php echo makeUrlNS('Special', 'GNU_General_Public_License'); ?>">a copy of
-          the GNU General Public License</a> along with this program; if not, write to:
-        </p>
-        <p style="margin-left 2em;">
-          Free Software Foundation, Inc.,<br />
-          51 Franklin Street, Fifth Floor<br />
-          Boston, MA 02110-1301, USA
-        </p>
-        <p>
-          Alternatively, you can <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">read it online</a>.
-        </p>
-        <?php
-        endif;
-        ?>
-      </td></tr>
-      <tr>
-        <td class="row2" colspan="2">
-          <table border="0" style="margin: 0 auto; background: none; width: 100%;" cellpadding="5">
-            <tr>
-              <td style="text-align: center;">
-                <?php echo $template->fading_button; ?>
-              </td>
-              <td style="text-align: center;">
-                <a href="http://www.php.net/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
-                  <img alt="Written in PHP" src="<?php echo scriptPath; ?>/images/about-powered-php.png" style="border-width: 0px;" width="88" height="31" />
-                </a>
-              </td>
-              <td style="text-align: center;">
-                <?php
-                switch(ENANO_DBLAYER)
-                {
-                  case 'MYSQL':
-                    ?>
-                    <a href="http://www.mysql.com/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
-                      <img alt="Database engine powered by MySQL" src="<?php echo scriptPath; ?>/images/about-powered-mysql.png" style="border-width: 0px;" width="88" height="31" />
-                    </a>
-                    <?php
-                    break;
-                  case 'PGSQL':
-                    ?>
-                    <a href="http://www.postgresql.org/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
-                      <img alt="Database engine powered by PostgreSQL" src="<?php echo scriptPath; ?>/images/about-powered-pgsql.png" style="border-width: 0px;" width="90" height="30" />
-                    </a>
-                    <?php
-                    break;
-                }
-                ?>
-              </td>
-            </tr>
-          </table>
-        </td>
-      </tr>
-      <tr><td style="width: 100px;" class="row1"><?php echo $lang->get('meta_enano_about_lbl_enanoversion'); ?></td><td class="row1"><?php echo enano_version(true) . ' (' . enano_codename() . ')'; ?></td></tr>
-      <tr><td style="width: 100px;" class="row2"><?php echo $lang->get('meta_enano_about_lbl_webserver'); ?></td><td class="row2"><?php if(isset($_SERVER['SERVER_SOFTWARE'])) echo $_SERVER['SERVER_SOFTWARE']; else echo 'Unable to determine web server software.'; ?></td></tr>
-      <tr><td style="width: 100px;" class="row1"><?php echo $lang->get('meta_enano_about_lbl_serverplatform'); ?></td><td class="row1"><?php echo $platform; ?></td></tr>
-      <tr><td style="width: 100px;" class="row2"><?php echo $lang->get('meta_enano_about_lbl_phpversion'); ?></td><td class="row2"><?php echo PHP_VERSION; ?></td></tr>
-      <?php
-      switch(ENANO_DBLAYER)
-      {
-        case 'MYSQL':
-          ?>
-          <tr><td style="width: 100px;" class="row1"><?php echo $lang->get('meta_enano_about_lbl_mysqlversion'); ?></td><td class="row1"><?php echo mysql_get_server_info($db->_conn); ?></td></tr>
-          <?php
-          break;
-        case 'PGSQL':
-          $pg_serverdata = pg_version($db->_conn);
-          $pg_version = $pg_serverdata['server'];
-          ?>
-          <tr><td style="width: 100px;" class="row1"><?php echo $lang->get('meta_enano_about_lbl_pgsqlversion'); ?></td><td class="row1"><?php echo $pg_version; ?></td></tr>
-          <?php
-          break;
-      }
-      ?>
-    </table>
-  </div>
-  <?php
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$platform = 'Unknown';
+	$uname = @file_get_contents('/proc/sys/kernel/ostype');
+	if($uname == "Linux\n")
+		$platform = 'Linux';
+	else if(@file_exists('/hurd/pfinet')) // I have a little experience with GNU/Hurd :-) http://hurdvm.enanocms.org/
+		$platform = 'GNU/Hurd';
+	else if(strtolower(PHP_OS) == 'winnt')
+		$platform = 'Windows NT';
+	else if(strtolower(PHP_OS) == 'win32')
+		$platform = 'Windows 9x/DOS';
+	else if(@file_exists('/System/Library/CoreServices/SystemVersion.plist'))
+		$platform = 'Mac OS X';
+	else if(@file_exists('/bin/bash'))
+		$platform = 'Other GNU';
+	else if(@is_dir('/bin'))
+		$platform = 'Other POSIX';
+	$template->header();
+	?>
+	<br />
+	<div class="tblholder">
+		<table border="0" cellspacing="1" cellpadding="4">
+			<tr><th colspan="2" style="text-align: left;"><?php echo $lang->get('meta_enano_about_th'); ?></th></tr>
+			<tr><td colspan="2" class="row3">
+				<?php
+				echo $lang->get('meta_enano_about_poweredby', array(
+						'year'     => date('Y')
+					));
+				$subst = array(
+						'gpl_link' => makeUrlNS('Special', 'GNU_General_Public_License')
+					);
+				echo $lang->get('meta_enano_about_gpl', $subst);
+				if ( $lang->lang_code != 'eng' ):
+				// Do not remove this block of code. Doing so is a violation of the GPL. (A copy of the GPL in other languages
+				// must be accompanied by a copy of the English GPL.)
+				?>
+				<h3>(English)</h3>
+				<p>
+					This website is powered by <a href="http://enanocms.org/">Enano</a>, the lightweight and open source CMS that everyone can use.
+					Enano is copyright &copy; 2006-<?php echo date('Y'); ?> Dan Fuhry. For legal information, along with a list of libraries that
+					Enano uses, please see <a href="http://enanocms.org/Legal_information">Legal Information</a>.
+				</p>
+				<p>
+					The developers and maintainers of Enano strongly believe that software should not only be free to use, but free to be modified,
+					distributed, and used to create derivative works. To help achieve this goal, we use licensing terms that require you to pass on
+					the freedoms we give you when you share Enano. For more information about Free Software, check out the
+					<a href="http://en.wikipedia.org/wiki/Free_Software" onclick="window.open(this.href); return false;">Wikipedia page</a> or
+					the <a href="http://www.fsf.org/" onclick="window.open(this.href); return false;">Free Software Foundation's</a> homepage.
+				</p>
+				<p>
+					This program is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License
+					as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+				</p>
+				<p>
+					This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+					warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+				</p>
+				<p>
+					You should have received <a href="<?php echo makeUrlNS('Special', 'GNU_General_Public_License'); ?>">a copy of
+					the GNU General Public License</a> along with this program; if not, write to:
+				</p>
+				<p style="margin-left 2em;">
+					Free Software Foundation, Inc.,<br />
+					51 Franklin Street, Fifth Floor<br />
+					Boston, MA 02110-1301, USA
+				</p>
+				<p>
+					Alternatively, you can <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">read it online</a>.
+				</p>
+				<?php
+				endif;
+				?>
+			</td></tr>
+			<tr>
+				<td class="row2" colspan="2">
+					<table border="0" style="margin: 0 auto; background: none; width: 100%;" cellpadding="5">
+						<tr>
+							<td style="text-align: center;">
+								<?php echo $template->fading_button; ?>
+							</td>
+							<td style="text-align: center;">
+								<a href="http://www.php.net/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
+									<img alt="Written in PHP" src="<?php echo scriptPath; ?>/images/about-powered-php.png" style="border-width: 0px;" width="88" height="31" />
+								</a>
+							</td>
+							<td style="text-align: center;">
+								<?php
+								switch(ENANO_DBLAYER)
+								{
+									case 'MYSQL':
+										?>
+										<a href="http://www.mysql.com/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
+											<img alt="Database engine powered by MySQL" src="<?php echo scriptPath; ?>/images/about-powered-mysql.png" style="border-width: 0px;" width="88" height="31" />
+										</a>
+										<?php
+										break;
+									case 'PGSQL':
+										?>
+										<a href="http://www.postgresql.org/" onclick="window.open(this.href); return false;" style="background: none; padding: 0;">
+											<img alt="Database engine powered by PostgreSQL" src="<?php echo scriptPath; ?>/images/about-powered-pgsql.png" style="border-width: 0px;" width="90" height="30" />
+										</a>
+										<?php
+										break;
+								}
+								?>
+							</td>
+						</tr>
+					</table>
+				</td>
+			</tr>
+			<tr><td style="width: 100px;" class="row1"><?php echo $lang->get('meta_enano_about_lbl_enanoversion'); ?></td><td class="row1"><?php echo enano_version(true) . ' (' . enano_codename() . ')'; ?></td></tr>
+			<tr><td style="width: 100px;" class="row2"><?php echo $lang->get('meta_enano_about_lbl_webserver'); ?></td><td class="row2"><?php if(isset($_SERVER['SERVER_SOFTWARE'])) echo $_SERVER['SERVER_SOFTWARE']; else echo 'Unable to determine web server software.'; ?></td></tr>
+			<tr><td style="width: 100px;" class="row1"><?php echo $lang->get('meta_enano_about_lbl_serverplatform'); ?></td><td class="row1"><?php echo $platform; ?></td></tr>
+			<tr><td style="width: 100px;" class="row2"><?php echo $lang->get('meta_enano_about_lbl_phpversion'); ?></td><td class="row2"><?php echo PHP_VERSION; ?></td></tr>
+			<?php
+			switch(ENANO_DBLAYER)
+			{
+				case 'MYSQL':
+					?>
+					<tr><td style="width: 100px;" class="row1"><?php echo $lang->get('meta_enano_about_lbl_mysqlversion'); ?></td><td class="row1"><?php echo mysql_get_server_info($db->_conn); ?></td></tr>
+					<?php
+					break;
+				case 'PGSQL':
+					$pg_serverdata = pg_version($db->_conn);
+					$pg_version = $pg_serverdata['server'];
+					?>
+					<tr><td style="width: 100px;" class="row1"><?php echo $lang->get('meta_enano_about_lbl_pgsqlversion'); ?></td><td class="row1"><?php echo $pg_version; ?></td></tr>
+					<?php
+					break;
+			}
+			?>
+		</table>
+	</div>
+	<?php
+	$template->footer();
 }
 
 function page_Special_GNU_General_Public_License()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $template->header();
-  if(file_exists(ENANO_ROOT . '/GPL'))
-  {
-    echo '<p>' . $lang->get('pagetools_gpl_blurb', array('about_url' => makeUrlNS('Special', 'About_Enano'))) . '</p>';
-    
-    if ( $lang->lang_code != 'eng' ):
-    // Do not remove this block of code. Doing so is a violation of the GPL. (A copy of the GPL in other languages
-    // must be accompanied by a copy of the English GPL.)
-    echo '<p>The following text represents the license that the <a href="'.makeUrlNS('Special', 'About_Enano').'">Enano</a> content management system is under. To make it easier to read, the text has been wiki-formatted; in no other way has it been changed.</p>';
-    endif;
-    
-    if ( file_exists(ENANO_ROOT . "/GPL_{$lang->lang_code}") )
-    {
-      echo '<h2>' . $lang->get('pagetools_gpl_title_native') . '</h2>';
-      echo '<p><a href="#gpl_english">' . $lang->get('pagetools_gpl_link_to_english') . ' / View the license in English' . '</a></p>';
-      echo RenderMan::render( file_get_contents ( ENANO_ROOT . "/GPL_{$lang->lang_code}" ) );
-      echo '<h2>' . $lang->get('pagetools_gpl_title_english') . ' / English version<a name="gpl_english" id="gpl_english"></a></h2>';
-    }
-    
-    echo RenderMan::render( file_get_contents ( ENANO_ROOT . '/GPL' ) );
-  }
-  else
-  {
-    echo '<p>' . $lang->get('pagetools_gpl_err_file_missing') . '</p>';
-    if ( $lang->lang_code != 'eng')
-      // Also print out English version
-      // Do not remove the following line of code; doing so would be a violation of the GPL.
-      echo '<p>It appears that the file "GPL" is missing from your Enano installation. You may find a wiki-formatted copy of the GPL at: <a href="http://enanocms.org/GPL">http://enanocms.org/GPL</a>. In the mean time, you may wish to contact the site administration and ask them to replace the GPL file.</p>';
-  }
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$template->header();
+	if(file_exists(ENANO_ROOT . '/GPL'))
+	{
+		echo '<p>' . $lang->get('pagetools_gpl_blurb', array('about_url' => makeUrlNS('Special', 'About_Enano'))) . '</p>';
+		
+		if ( $lang->lang_code != 'eng' ):
+		// Do not remove this block of code. Doing so is a violation of the GPL. (A copy of the GPL in other languages
+		// must be accompanied by a copy of the English GPL.)
+		echo '<p>The following text represents the license that the <a href="'.makeUrlNS('Special', 'About_Enano').'">Enano</a> content management system is under. To make it easier to read, the text has been wiki-formatted; in no other way has it been changed.</p>';
+		endif;
+		
+		if ( file_exists(ENANO_ROOT . "/GPL_{$lang->lang_code}") )
+		{
+			echo '<h2>' . $lang->get('pagetools_gpl_title_native') . '</h2>';
+			echo '<p><a href="#gpl_english">' . $lang->get('pagetools_gpl_link_to_english') . ' / View the license in English' . '</a></p>';
+			echo RenderMan::render( file_get_contents ( ENANO_ROOT . "/GPL_{$lang->lang_code}" ) );
+			echo '<h2>' . $lang->get('pagetools_gpl_title_english') . ' / English version<a name="gpl_english" id="gpl_english"></a></h2>';
+		}
+		
+		echo RenderMan::render( file_get_contents ( ENANO_ROOT . '/GPL' ) );
+	}
+	else
+	{
+		echo '<p>' . $lang->get('pagetools_gpl_err_file_missing') . '</p>';
+		if ( $lang->lang_code != 'eng')
+			// Also print out English version
+			// Do not remove the following line of code; doing so would be a violation of the GPL.
+			echo '<p>It appears that the file "GPL" is missing from your Enano installation. You may find a wiki-formatted copy of the GPL at: <a href="http://enanocms.org/GPL">http://enanocms.org/GPL</a>. In the mean time, you may wish to contact the site administration and ask them to replace the GPL file.</p>';
+	}
+	$template->footer();
 }
 
 function page_Special_TagCloud()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $template->header();
-  
-  if ( $tag = $paths->getParam(0) )
-  {
-    $tag = sanitize_tag($tag);
-    $q = $db->sql_query('SELECT page_id, namespace FROM '.table_prefix.'tags WHERE tag_name=\'' . $db->escape($tag) . '\';');
-    if ( !$q )
-      $db->_die();
-    if ( $row = $db->fetchrow() )
-    {
-      echo '<div class="tblholder">
-              <table border="0" cellspacing="1" cellpadding="4">';
-      echo '<tr><th colspan="2">' . $lang->get('pagetools_tagcloud_pagelist_th', array('tag' => htmlspecialchars($tag))) . '</th></tr>';
-      echo '<tr>';
-      $i = 0;
-      $td_class = 'row1';
-      do
-      {
-        if ( $i % 2 == 0 && $i > 1 )
-        {
-          $td_class = ( $td_class == 'row2' ) ? 'row1' : 'row2';
-          echo '</tr><tr>';
-        }
-        $i++;
-        $title = get_page_title_ns($row['page_id'], $row['namespace']);
-        if ( $row['namespace'] != 'Article' && isset($paths->nslist[$row['namespace']]) )
-          $title = $paths->nslist[$row['namespace']] . $title;
-        $url = makeUrlNS($row['namespace'], $row['page_id']);
-        $class = ( isPage( $paths->nslist[$row['namespace']] . $row['page_id'] ) ) ? '' : ' class="wikilink-nonexistent"';
-        $link = '<a href="' . htmlspecialchars($url) . '"' . $class . '>' . htmlspecialchars($title) . '</a>';
-        echo "<td class=\"$td_class\" style=\"width: 50%;\">$link</td>";
-        // " workaround for jEdit highlighting bug
-      }
-      while ( $row = $db->fetchrow() );
-      while ( $i % 2 > 0 )
-      {
-        $i++;
-        echo "<td class=\"$td_class\" style=\"width: 50%;\"></td>";
-      }
-      // " workaround for jEdit highlighting bug
-      echo '<tr>
-              <th colspan="2" class="subhead"><a href="' . makeUrlNS('Special', 'TagCloud') . '">&laquo; ' . $lang->get('pagetools_tagcloud_btn_return') . '</a></th>
-            </tr>';
-      echo '</table>';
-      echo '</div>';
-    }
-  }
-  else
-  {
-    $cloud = new TagCloud();
-    
-    $q = $db->sql_query('SELECT tag_name FROM '.table_prefix.'tags;');
-    if ( !$q )
-      $db->_die();
-    if ( $db->numrows() < 1 )
-    {
-      echo '<p>' . $lang->get('pagetools_tagcloud_msg_no_tags') . '</p>';
-    }
-    else
-    {
-      echo '<h3>' . $lang->get('pagetools_tagcloud_blurb') . '</h3>';
-      while ( $row = $db->fetchrow() )
-      {
-        $cloud->add_word($row['tag_name']);
-      }
-      echo $cloud->make_html('normal');
-      echo '<p>' . $lang->get('pagetools_tagcloud_instructions') . '</p>';
-    }
-  }
-  
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$template->header();
+	
+	if ( $tag = $paths->getParam(0) )
+	{
+		$tag = sanitize_tag($tag);
+		$q = $db->sql_query('SELECT page_id, namespace FROM '.table_prefix.'tags WHERE tag_name=\'' . $db->escape($tag) . '\';');
+		if ( !$q )
+			$db->_die();
+		if ( $row = $db->fetchrow() )
+		{
+			echo '<div class="tblholder">
+							<table border="0" cellspacing="1" cellpadding="4">';
+			echo '<tr><th colspan="2">' . $lang->get('pagetools_tagcloud_pagelist_th', array('tag' => htmlspecialchars($tag))) . '</th></tr>';
+			echo '<tr>';
+			$i = 0;
+			$td_class = 'row1';
+			do
+			{
+				if ( $i % 2 == 0 && $i > 1 )
+				{
+					$td_class = ( $td_class == 'row2' ) ? 'row1' : 'row2';
+					echo '</tr><tr>';
+				}
+				$i++;
+				$title = get_page_title_ns($row['page_id'], $row['namespace']);
+				if ( $row['namespace'] != 'Article' && isset($paths->nslist[$row['namespace']]) )
+					$title = $paths->nslist[$row['namespace']] . $title;
+				$url = makeUrlNS($row['namespace'], $row['page_id']);
+				$class = ( isPage( $paths->nslist[$row['namespace']] . $row['page_id'] ) ) ? '' : ' class="wikilink-nonexistent"';
+				$link = '<a href="' . htmlspecialchars($url) . '"' . $class . '>' . htmlspecialchars($title) . '</a>';
+				echo "<td class=\"$td_class\" style=\"width: 50%;\">$link</td>";
+				// " workaround for jEdit highlighting bug
+			}
+			while ( $row = $db->fetchrow() );
+			while ( $i % 2 > 0 )
+			{
+				$i++;
+				echo "<td class=\"$td_class\" style=\"width: 50%;\"></td>";
+			}
+			// " workaround for jEdit highlighting bug
+			echo '<tr>
+							<th colspan="2" class="subhead"><a href="' . makeUrlNS('Special', 'TagCloud') . '">&laquo; ' . $lang->get('pagetools_tagcloud_btn_return') . '</a></th>
+						</tr>';
+			echo '</table>';
+			echo '</div>';
+		}
+	}
+	else
+	{
+		$cloud = new TagCloud();
+		
+		$q = $db->sql_query('SELECT tag_name FROM '.table_prefix.'tags;');
+		if ( !$q )
+			$db->_die();
+		if ( $db->numrows() < 1 )
+		{
+			echo '<p>' . $lang->get('pagetools_tagcloud_msg_no_tags') . '</p>';
+		}
+		else
+		{
+			echo '<h3>' . $lang->get('pagetools_tagcloud_blurb') . '</h3>';
+			while ( $row = $db->fetchrow() )
+			{
+				$cloud->add_word($row['tag_name']);
+			}
+			echo $cloud->make_html('normal');
+			echo '<p>' . $lang->get('pagetools_tagcloud_instructions') . '</p>';
+		}
+	}
+	
+	$template->footer();
 }
 
 function page_Special_Autofill()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  require_once(ENANO_ROOT . '/includes/search.php');
-  
-  header('Content-type: text/javascript');
-  
-  $dataset = array();
-  if ( isset($_GET['type']) )
-  {
-    switch($_GET['type'])
-    {
-      case 'username':
-        if ( isset($_GET['userinput']) && strlen($_GET['userinput']) >= 3 )
-        {
-          $search = '%' . escape_string_like($_GET['userinput']) . '%';
-          $min_id = ( isset($_GET['allow_anon']) && $_GET['allow_anon'] == '1' ) ? '1' : '2';
-          $q = $db->sql_query('SELECT username FROM ' . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) LIKE '$search' AND user_id >= $min_id");
-          if ( !$q )
-            $db->die_json();
-          
-          while ( $row = $db->fetchrow($q) )
-          {
-            $key = array(
-              'name' => $row['username'],
-              'name_highlight' => highlight_term($_GET['userinput'], $row['username'], '<b>', '</b>')
-            );
-            $key = array_merge($key, $session->get_user_rank($row['username']));
-            $key['rank_title'] = $lang->get($key['rank_title']);
-            $key[0] = $row['username'];
-            $dataset[] = $key;
-            // $dataset[] = array($row['username'], $row['username']);
-            // echo "{$row['username']}|{$row['username']}\n";
-          }
-        }
-        // return;
-        break;
-      case 'page':
-        if ( isset($_GET['userinput']) && strlen($_GET['userinput']) >= 3 )
-        {
-          $search = '%' . escape_string_like($_GET['userinput']) . '%';
-          $q = $db->sql_query('SELECT urlname, namespace, name FROM ' . table_prefix . "pages\n"
-                            . "  WHERE (\n"
-                            . "       " . ENANO_SQLFUNC_LOWERCASE . "(urlname) LIKE '$search'\n"
-                            . "    OR " . ENANO_SQLFUNC_LOWERCASE . "(name)    LIKE '$search'\n"
-                            . "  );");
-          if ( !$q )
-            $db->die_json();
-          
-          while ( $row = $db->fetchrow() )
-          {
-            $pathskey = ( isset($paths->nslist[$row['namespace']]) ? $paths->nslist[$row['namespace']] : $row['namespace'] . substr($paths->nslist['Special'], -1) ) . $row['urlname'];
-            
-            $key = array(
-              0 => $pathskey,
-              'pid_highlight'  => highlight_term($_GET['userinput'], dirtify_page_id($pathskey), '<b>', '</b>'),
-              'name_highlight' => highlight_term($_GET['userinput'], $row['name'], '<b>', '</b>')
-            );
-            $dataset[] = $key;
-          }
-        }
-        break;
-      default:
-        $code = $plugins->setHook('autofill_json_request');
-        foreach ( $code as $cmd )
-        {
-          eval($cmd);
-        }
-        break;
-    }
-  }
-  
-  echo enano_json_encode($dataset);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	require_once(ENANO_ROOT . '/includes/search.php');
+	
+	header('Content-type: text/javascript');
+	
+	$dataset = array();
+	if ( isset($_GET['type']) )
+	{
+		switch($_GET['type'])
+		{
+			case 'username':
+				if ( isset($_GET['userinput']) && strlen($_GET['userinput']) >= 3 )
+				{
+					$search = '%' . escape_string_like($_GET['userinput']) . '%';
+					$min_id = ( isset($_GET['allow_anon']) && $_GET['allow_anon'] == '1' ) ? '1' : '2';
+					$q = $db->sql_query('SELECT username FROM ' . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) LIKE '$search' AND user_id >= $min_id");
+					if ( !$q )
+						$db->die_json();
+					
+					while ( $row = $db->fetchrow($q) )
+					{
+						$key = array(
+							'name' => $row['username'],
+							'name_highlight' => highlight_term($_GET['userinput'], $row['username'], '<b>', '</b>')
+						);
+						$key = array_merge($key, $session->get_user_rank($row['username']));
+						$key['rank_title'] = $lang->get($key['rank_title']);
+						$key[0] = $row['username'];
+						$dataset[] = $key;
+						// $dataset[] = array($row['username'], $row['username']);
+						// echo "{$row['username']}|{$row['username']}\n";
+					}
+				}
+				// return;
+				break;
+			case 'page':
+				if ( isset($_GET['userinput']) && strlen($_GET['userinput']) >= 3 )
+				{
+					$search = '%' . escape_string_like($_GET['userinput']) . '%';
+					$q = $db->sql_query('SELECT urlname, namespace, name FROM ' . table_prefix . "pages\n"
+														. "  WHERE (\n"
+														. "       " . ENANO_SQLFUNC_LOWERCASE . "(urlname) LIKE '$search'\n"
+														. "    OR " . ENANO_SQLFUNC_LOWERCASE . "(name)    LIKE '$search'\n"
+														. "  );");
+					if ( !$q )
+						$db->die_json();
+					
+					while ( $row = $db->fetchrow() )
+					{
+						$pathskey = ( isset($paths->nslist[$row['namespace']]) ? $paths->nslist[$row['namespace']] : $row['namespace'] . substr($paths->nslist['Special'], -1) ) . $row['urlname'];
+						
+						$key = array(
+							0 => $pathskey,
+							'pid_highlight'  => highlight_term($_GET['userinput'], dirtify_page_id($pathskey), '<b>', '</b>'),
+							'name_highlight' => highlight_term($_GET['userinput'], $row['name'], '<b>', '</b>')
+						);
+						$dataset[] = $key;
+					}
+				}
+				break;
+			default:
+				$code = $plugins->setHook('autofill_json_request');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				break;
+		}
+	}
+	
+	echo enano_json_encode($dataset);
 }
 
 ?>
--- a/plugins/SpecialSearch.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialSearch.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_specialsearch_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_specialsearch_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_specialsearch_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_specialsearch_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -25,282 +25,282 @@
 
 function SpecialSearch_paths_init()
 {
-  register_special_page('SearchRebuild', 'specialpage_search_rebuild');
-  register_special_page('Search', 'specialpage_search');
+	register_special_page('SearchRebuild', 'specialpage_search_rebuild');
+	register_special_page('Search', 'specialpage_search');
 }
 
 function page_Special_SearchRebuild()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if ( !$session->get_permissions('mod_misc') )
-  {
-    die_friendly('Unauthorized', '<p>You need to be an administrator to rebuild the search index</p>');
-  }
-  $template->header();
-  @set_time_limit(0);
-  echo '<p>';
-  if($paths->rebuild_search_index(true))
-    echo '</p><p>Index rebuilt!</p>';
-  else
-    echo '<p>Index was not rebuilt due to an error.';
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	if ( !$session->get_permissions('mod_misc') )
+	{
+		die_friendly('Unauthorized', '<p>You need to be an administrator to rebuild the search index</p>');
+	}
+	$template->header();
+	@set_time_limit(0);
+	echo '<p>';
+	if($paths->rebuild_search_index(true))
+		echo '</p><p>Index rebuilt!</p>';
+	else
+		echo '<p>Index was not rebuilt due to an error.';
+	$template->footer();
 }
 
 function page_Special_Search()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $aggressive_optimize_html;
-  global $lang;
-  
-  require_once(ENANO_ROOT.'/includes/search.php');
-  
-  $aggressive_optimize_html = false;
-  
-  if ( !$q = $paths->getParam(0) )
-    $q = ( isset($_GET['q']) ) ? $_GET['q'] : '';
-  
-  if(isset($_GET['words_any']))
-  {
-    $q = '';
-    if(!empty($_GET['words_any']))
-    {
-      $q .= $_GET['words_any'] . ' ';
-    }
-    if(!empty($_GET['exact_phrase']))
-    {
-      $q .= '"' . $_GET['exact_phrase'] . '" ';
-    }
-    if(!empty($_GET['exclude_words']))
-    {
-      $not = explode(' ', $_GET['exclude_words']);
-      foreach ( $not as $i => $foo )
-      {
-        $not[$i] = '-' . $not[$i];
-      }
-      $q .= implode(' ', $not) . ' ';
-    }
-    if(!empty($_GET['require_words']))
-    {
-      $req = explode(' ', $_GET['require_words']);
-      foreach ( $req as $i => $foo )
-      {
-        $req[$i] = '+' . $req[$i];
-      }
-      $q .= implode(' ', $req) . ' ';
-    }
-  }
-  $q = trim($q);
-  
-  $template->header();
-  
-  $qin = ( isset($q) ) ? str_replace('"', '\"', htmlspecialchars($q)) : '';
-  $search_form = '<form action="' . makeUrlNS('Special', 'Search') . '">
-  <input type="text" tabindex="1" name="q" size="50" value="' . $qin . '" />&nbsp;<input tabindex="2" type="submit" value="' . $lang->get('search_btn_search') . '" />&nbsp;<a href="' . makeUrlNS('Special', 'Search') . '">' . $lang->get('search_btn_advanced_search') . '</a>
-  ' . ( $session->auth_level > USER_LEVEL_MEMBER ? '<input type="hidden" name="auth" value="' . $session->sid_super . '" />' : '' ) . '
-  </form>';
-  
-  if ( !empty($q) )
-  {
-    $search_start = microtime_float();
-    
-    $results = perform_search($q, $warn, ( isset($_GET['match_case']) ), $word_list);
-    $warn = array_unique($warn);
-    
-    if ( file_exists( ENANO_ROOT . '/themes/' . $template->theme . '/search-result.tpl' ) )
-    {
-      $parser = $template->makeParser('search-result.tpl');
-    }
-    else
-    {
-      $tpl_code = <<<LONGSTRING
-      
-      <!-- Start search result -->
-      
-      <div class="search-result">
-        <p>
-         <h3><a href="{RESULT_URL}"><span class="search-result-annotation">{PAGE_NOTE}</span>{PAGE_TITLE}</a></h3>
-          {PAGE_TEXT}
-          <span class="search-result-url">{PAGE_URL}</span> - 
-          <!-- BEGINNOT special_page --><span class="search-result-info">{PAGE_LENGTH} {PAGE_LENGTH_UNIT}</span> -<!-- END special_page --> 
-          <span class="search-result-info">{lang:search_lbl_relevance} {RELEVANCE_SCORE}%</span>
-        </p>
-      </div>
-      
-      <!-- Finish search result -->
-      
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $aggressive_optimize_html;
+	global $lang;
+	
+	require_once(ENANO_ROOT.'/includes/search.php');
+	
+	$aggressive_optimize_html = false;
+	
+	if ( !$q = $paths->getParam(0) )
+		$q = ( isset($_GET['q']) ) ? $_GET['q'] : '';
+	
+	if(isset($_GET['words_any']))
+	{
+		$q = '';
+		if(!empty($_GET['words_any']))
+		{
+			$q .= $_GET['words_any'] . ' ';
+		}
+		if(!empty($_GET['exact_phrase']))
+		{
+			$q .= '"' . $_GET['exact_phrase'] . '" ';
+		}
+		if(!empty($_GET['exclude_words']))
+		{
+			$not = explode(' ', $_GET['exclude_words']);
+			foreach ( $not as $i => $foo )
+			{
+				$not[$i] = '-' . $not[$i];
+			}
+			$q .= implode(' ', $not) . ' ';
+		}
+		if(!empty($_GET['require_words']))
+		{
+			$req = explode(' ', $_GET['require_words']);
+			foreach ( $req as $i => $foo )
+			{
+				$req[$i] = '+' . $req[$i];
+			}
+			$q .= implode(' ', $req) . ' ';
+		}
+	}
+	$q = trim($q);
+	
+	$template->header();
+	
+	$qin = ( isset($q) ) ? str_replace('"', '\"', htmlspecialchars($q)) : '';
+	$search_form = '<form action="' . makeUrlNS('Special', 'Search') . '">
+	<input type="text" tabindex="1" name="q" size="50" value="' . $qin . '" />&nbsp;<input tabindex="2" type="submit" value="' . $lang->get('search_btn_search') . '" />&nbsp;<a href="' . makeUrlNS('Special', 'Search') . '">' . $lang->get('search_btn_advanced_search') . '</a>
+	' . ( $session->auth_level > USER_LEVEL_MEMBER ? '<input type="hidden" name="auth" value="' . $session->sid_super . '" />' : '' ) . '
+	</form>';
+	
+	if ( !empty($q) )
+	{
+		$search_start = microtime_float();
+		
+		$results = perform_search($q, $warn, ( isset($_GET['match_case']) ), $word_list);
+		$warn = array_unique($warn);
+		
+		if ( file_exists( ENANO_ROOT . '/themes/' . $template->theme . '/search-result.tpl' ) )
+		{
+			$parser = $template->makeParser('search-result.tpl');
+		}
+		else
+		{
+			$tpl_code = <<<LONGSTRING
+			
+			<!-- Start search result -->
+			
+			<div class="search-result">
+				<p>
+ 				<h3><a href="{RESULT_URL}"><span class="search-result-annotation">{PAGE_NOTE}</span>{PAGE_TITLE}</a></h3>
+					{PAGE_TEXT}
+					<span class="search-result-url">{PAGE_URL}</span> - 
+					<!-- BEGINNOT special_page --><span class="search-result-info">{PAGE_LENGTH} {PAGE_LENGTH_UNIT}</span> -<!-- END special_page --> 
+					<span class="search-result-info">{lang:search_lbl_relevance} {RELEVANCE_SCORE}%</span>
+				</p>
+			</div>
+			
+			<!-- Finish search result -->
+			
 LONGSTRING;
-      $parser = $template->makeParserText($tpl_code);
-    }
-    foreach ( $results as $i => $_ )
-    {
-      $result =& $results[$i];
-      $result['page_text'] = str_replace(array('<highlight>', '</highlight>'), array('<span class="search-term">', '</span>'), $result['page_text']);
-      if ( !empty($result['page_text']) )
-        $result['page_text'] .= '<br />';
-      $result['page_name'] = str_replace(array('<highlight>', '</highlight>'), array('<span class="title-search-term">', '</span>'), $result['page_name']);
-      $result['url_highlight'] = str_replace(array('<highlight>', '</highlight>'), array('<span class="url-search-term">', '</span>'), $result['url_highlight']);
-      if ( $result['page_length'] >= 1048576 )
-      {
-        $result['page_length'] = round($result['page_length'] / 1048576, 1);
-        $length_unit = $lang->get('etc_unit_megabytes_short');
-      }
-      else if ( $result['page_length'] >= 1024 )
-      {
-        $result['page_length'] = round($result['page_length'] / 1024, 1);
-        $length_unit = $lang->get('etc_unit_kilobytes_short');
-      }
-      else
-      {
-        $length_unit = $lang->get('etc_unit_bytes');
-      }
-      //$url = makeUrlComplete($result['namespace'], $result['page_id']);
-      //$url = preg_replace('/\?.+$/', '', $url);
-      $url = $result['url_highlight'];
-      
-      $parser->assign_vars(array(
-         'PAGE_TITLE' => $result['page_name'],
-         'PAGE_TEXT' => $result['page_text'],
-         'PAGE_LENGTH' => $result['page_length'],
-         'RELEVANCE_SCORE' => $result['score'],
-         'RESULT_URL' => makeUrlNS($result['namespace'], $result['page_id'], false, true) . ( isset($result['url_append']) ? $result['url_append'] : '' ),
-         'PAGE_LENGTH_UNIT' => $length_unit,
-         'PAGE_URL' => $url,
-         'PAGE_NOTE' => ( isset($result['page_note']) ? $result['page_note'] . ' ' : '' )
-        ));
-      $has_content = ( $result['namespace'] == 'Special' || !empty($result['zero_length']) );
-      
-      $code = $plugins->setHook('search_global_results');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      
-      $parser->assign_bool(array(
-          'special_page' => $has_content
-        ));
-      $result = $parser->run();
-    }
-    unset($result);
-    
-    $per_page = 10;
-    $start = ( isset($_GET['start']) ? intval($_GET['start']) : 0 );
-    // for plugin compatibility:
-    $offset =& $start;
-    $start_string = $start + 1;
-    $per_string = $start_string + $per_page - 1;
-    $num_results = count($results);
-    if ( $per_string > $num_results )
-      $per_string = $num_results;
-    
-    $search_time = microtime_float() - $search_start;
-    $search_time = round($search_time, 3);
-    
-    $q_trim = ( strlen($q) > 30 ) ? substr($q, 0, 27) . '...' : $q;
-    $q_trim = htmlspecialchars($q_trim);
-    
-    $result_detail = $lang->get('search_msg_result_detail', array(
-        'start_string' => $start_string,
-        'per_string' => $per_string,
-        'q_trim' => $q_trim,
-        'num_results' => $num_results,
-        'search_time' => $search_time
-      ));
-    $result_string = ( count($results) > 0 ) ? $result_detail : $lang->get('search_msg_no_results');
-    
-    echo '<div class="search-hibar">
-            <div style="float: right;">
-              ' . $result_string . '
-            </div>
-            <b>' . $lang->get('search_lbl_site_search') . '</b>
-          </div>
-          <div class="search-lobar">
-            ' . $search_form . '
-          </div>';
-          
-    if ( count($warn) > 0 )
-    {
-      echo '<div class="warning-box" style="margin: 10px 0 0 0;">';
-      echo '<b>' . $lang->get('search_err_query_title') . '</b><br />
-            ' . $lang->get('search_err_query_body');
-      echo '<ul><li>' . implode('</li><li>', $warn) . '</li></ul>';
-      echo '</div>';
-    }
-  
-    if ( count($results) > 0 )
-    {
-      $html = paginate_array(
-          $results,
-          count($results),
-          makeUrlNS('Special', 'Search', 'q=' . str_replace('%', '%%', htmlspecialchars(urlencode($q))) . '&start=%s'),
-          $start,
-          $per_page
-        );
-      echo $html;
-    }
-    else
-    {
-      // No results for the search
-      echo '<h3 style="font-weight: normal;">' . $lang->get('search_body_no_results_title', array('query' => htmlspecialchars($q))) . '</h3>';
-      echo $lang->get('search_body_no_results_body', array(
-          'query' => htmlspecialchars($q),
-          'create_url' => makeUrl($q),
-          'special_url' => makeUrlNS('Special', 'SpecialPages'),
-        ));
-    }
-    $code = $plugins->setHook('search_results');
-    foreach ( $code as $cmd )
-    {
-      eval($cmd);
-    }
-  }
-  else
-  {
-    ?>
-    <form action="<?php echo makeUrl($paths->page); ?>" method="get">
-      <?php if ( urlSeparator == '&' ): ?>
-        <input type="hidden" name="title" value="<?php echo $paths->nslist['Special'] . 'Search'; ?>" />
-      <?php 
-      echo ( $session->auth_level > USER_LEVEL_MEMBER ? '<input type="hidden" name="auth" value="' . $session->sid_super . '" />' : '' );
-      endif; ?>
-      <div class="tblholder">
-        <table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
-          <tr><th colspan="2"><?php echo $lang->get('search_th_advanced_search'); ?></th></tr>
-          <tr>
-            <td class="row1"><?php echo $lang->get('search_lbl_field_any'); ?></td>
-            <td class="row1"><input type="text" name="words_any" size="40" /></td>
-          </tr>
-          <tr>
-            <td class="row2"><?php echo $lang->get('search_lbl_field_exact'); ?></td>
-            <td class="row2"><input type="text" name="exact_phrase" size="40" /></td>
-          </tr>
-          <tr>
-            <td class="row1"><?php echo $lang->get('search_lbl_field_none'); ?></td>
-            <td class="row1"><input type="text" name="exclude_words" size="40" /></td>
-          </tr>
-          <tr>
-            <td class="row2"><?php echo $lang->get('search_lbl_field_all'); ?></td>
-            <td class="row2"><input type="text" name="require_words" size="40" /></td>
-          </tr>
-          <tr>
-            <td class="row1">
-              <label for="chk_case"><?php echo $lang->get('search_lbl_field_casesensitive'); ?></label>
-            </td>
-            <td class="row1">
-              <input type="checkbox" name="match_case" id="chk_case" />
-            </td>
-          </tr>
-          <tr>
-            <th colspan="2" class="subhead">
-              <input type="submit" name="do_search" value="<?php echo $lang->get('search_btn_search'); ?>" />
-            </td>
-          </tr>
-        </table>
-      </div>
-    </form>
-    <?php
-  }
-  
-  $template->footer();
+			$parser = $template->makeParserText($tpl_code);
+		}
+		foreach ( $results as $i => $_ )
+		{
+			$result =& $results[$i];
+			$result['page_text'] = str_replace(array('<highlight>', '</highlight>'), array('<span class="search-term">', '</span>'), $result['page_text']);
+			if ( !empty($result['page_text']) )
+				$result['page_text'] .= '<br />';
+			$result['page_name'] = str_replace(array('<highlight>', '</highlight>'), array('<span class="title-search-term">', '</span>'), $result['page_name']);
+			$result['url_highlight'] = str_replace(array('<highlight>', '</highlight>'), array('<span class="url-search-term">', '</span>'), $result['url_highlight']);
+			if ( $result['page_length'] >= 1048576 )
+			{
+				$result['page_length'] = round($result['page_length'] / 1048576, 1);
+				$length_unit = $lang->get('etc_unit_megabytes_short');
+			}
+			else if ( $result['page_length'] >= 1024 )
+			{
+				$result['page_length'] = round($result['page_length'] / 1024, 1);
+				$length_unit = $lang->get('etc_unit_kilobytes_short');
+			}
+			else
+			{
+				$length_unit = $lang->get('etc_unit_bytes');
+			}
+			//$url = makeUrlComplete($result['namespace'], $result['page_id']);
+			//$url = preg_replace('/\?.+$/', '', $url);
+			$url = $result['url_highlight'];
+			
+			$parser->assign_vars(array(
+ 				'PAGE_TITLE' => $result['page_name'],
+ 				'PAGE_TEXT' => $result['page_text'],
+ 				'PAGE_LENGTH' => $result['page_length'],
+ 				'RELEVANCE_SCORE' => $result['score'],
+ 				'RESULT_URL' => makeUrlNS($result['namespace'], $result['page_id'], false, true) . ( isset($result['url_append']) ? $result['url_append'] : '' ),
+ 				'PAGE_LENGTH_UNIT' => $length_unit,
+ 				'PAGE_URL' => $url,
+ 				'PAGE_NOTE' => ( isset($result['page_note']) ? $result['page_note'] . ' ' : '' )
+				));
+			$has_content = ( $result['namespace'] == 'Special' || !empty($result['zero_length']) );
+			
+			$code = $plugins->setHook('search_global_results');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			
+			$parser->assign_bool(array(
+					'special_page' => $has_content
+				));
+			$result = $parser->run();
+		}
+		unset($result);
+		
+		$per_page = 10;
+		$start = ( isset($_GET['start']) ? intval($_GET['start']) : 0 );
+		// for plugin compatibility:
+		$offset =& $start;
+		$start_string = $start + 1;
+		$per_string = $start_string + $per_page - 1;
+		$num_results = count($results);
+		if ( $per_string > $num_results )
+			$per_string = $num_results;
+		
+		$search_time = microtime_float() - $search_start;
+		$search_time = round($search_time, 3);
+		
+		$q_trim = ( strlen($q) > 30 ) ? substr($q, 0, 27) . '...' : $q;
+		$q_trim = htmlspecialchars($q_trim);
+		
+		$result_detail = $lang->get('search_msg_result_detail', array(
+				'start_string' => $start_string,
+				'per_string' => $per_string,
+				'q_trim' => $q_trim,
+				'num_results' => $num_results,
+				'search_time' => $search_time
+			));
+		$result_string = ( count($results) > 0 ) ? $result_detail : $lang->get('search_msg_no_results');
+		
+		echo '<div class="search-hibar">
+						<div style="float: right;">
+							' . $result_string . '
+						</div>
+						<b>' . $lang->get('search_lbl_site_search') . '</b>
+					</div>
+					<div class="search-lobar">
+						' . $search_form . '
+					</div>';
+					
+		if ( count($warn) > 0 )
+		{
+			echo '<div class="warning-box" style="margin: 10px 0 0 0;">';
+			echo '<b>' . $lang->get('search_err_query_title') . '</b><br />
+						' . $lang->get('search_err_query_body');
+			echo '<ul><li>' . implode('</li><li>', $warn) . '</li></ul>';
+			echo '</div>';
+		}
+	
+		if ( count($results) > 0 )
+		{
+			$html = paginate_array(
+					$results,
+					count($results),
+					makeUrlNS('Special', 'Search', 'q=' . str_replace('%', '%%', htmlspecialchars(urlencode($q))) . '&start=%s'),
+					$start,
+					$per_page
+				);
+			echo $html;
+		}
+		else
+		{
+			// No results for the search
+			echo '<h3 style="font-weight: normal;">' . $lang->get('search_body_no_results_title', array('query' => htmlspecialchars($q))) . '</h3>';
+			echo $lang->get('search_body_no_results_body', array(
+					'query' => htmlspecialchars($q),
+					'create_url' => makeUrl($q),
+					'special_url' => makeUrlNS('Special', 'SpecialPages'),
+				));
+		}
+		$code = $plugins->setHook('search_results');
+		foreach ( $code as $cmd )
+		{
+			eval($cmd);
+		}
+	}
+	else
+	{
+		?>
+		<form action="<?php echo makeUrl($paths->page); ?>" method="get">
+			<?php if ( urlSeparator == '&' ): ?>
+				<input type="hidden" name="title" value="<?php echo $paths->nslist['Special'] . 'Search'; ?>" />
+			<?php 
+			echo ( $session->auth_level > USER_LEVEL_MEMBER ? '<input type="hidden" name="auth" value="' . $session->sid_super . '" />' : '' );
+			endif; ?>
+			<div class="tblholder">
+				<table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
+					<tr><th colspan="2"><?php echo $lang->get('search_th_advanced_search'); ?></th></tr>
+					<tr>
+						<td class="row1"><?php echo $lang->get('search_lbl_field_any'); ?></td>
+						<td class="row1"><input type="text" name="words_any" size="40" /></td>
+					</tr>
+					<tr>
+						<td class="row2"><?php echo $lang->get('search_lbl_field_exact'); ?></td>
+						<td class="row2"><input type="text" name="exact_phrase" size="40" /></td>
+					</tr>
+					<tr>
+						<td class="row1"><?php echo $lang->get('search_lbl_field_none'); ?></td>
+						<td class="row1"><input type="text" name="exclude_words" size="40" /></td>
+					</tr>
+					<tr>
+						<td class="row2"><?php echo $lang->get('search_lbl_field_all'); ?></td>
+						<td class="row2"><input type="text" name="require_words" size="40" /></td>
+					</tr>
+					<tr>
+						<td class="row1">
+							<label for="chk_case"><?php echo $lang->get('search_lbl_field_casesensitive'); ?></label>
+						</td>
+						<td class="row1">
+							<input type="checkbox" name="match_case" id="chk_case" />
+						</td>
+					</tr>
+					<tr>
+						<th colspan="2" class="subhead">
+							<input type="submit" name="do_search" value="<?php echo $lang->get('search_btn_search'); ?>" />
+						</td>
+					</tr>
+				</table>
+			</div>
+		</form>
+		<?php
+	}
+	
+	$template->footer();
 }
 
 ?>
--- a/plugins/SpecialUpdownload.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialUpdownload.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_specialupdownload_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_specialupdownload_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_specialupdownload_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_specialupdownload_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -28,295 +28,295 @@
 
 function SpecialUpDownload_paths_init()
 {
-  register_special_page('UploadFile', 'specialpage_upload_file');
-  register_special_page('DownloadFile', 'specialpage_download_file');
+	register_special_page('UploadFile', 'specialpage_upload_file');
+	register_special_page('DownloadFile', 'specialpage_download_file');
 }
-  
+	
 function page_Special_UploadFile()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $cache;
-  global $mime_types;
-  if(getConfig('enable_uploads')!='1') { die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('upload_err_disabled_site') . '</p>'); }
-  if ( !$session->get_permissions('upload_files') )
-  {
-    die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('upload_err_disabled_acl') . '</p>');
-  }
-  if(isset($_POST['doit']))
-  {
-    if(isset($_FILES['data']))
-    {
-      $file =& $_FILES['data'];
-    }
-    else
-    {
-      $file = false;
-    }
-    if ( !is_array($file) )
-    {
-      die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_cant_get_file_meta') . '</p>');
-    }
-    if ( $file['size'] == 0 || $file['size'] > (int)getConfig('max_file_size', '256000') )
-    {
-      die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_too_big_or_small') . '</p>');
-    }
-    
-    $types = fetch_allowed_extensions();
-    $ext = strtolower(substr($file['name'], strrpos($file['name'], '.')+1, strlen($file['name'])));
-    if ( !isset($types[$ext]) || ( isset($types[$ext]) && !$types[$ext] ) )
-    {
-      die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_banned_ext', array('ext' => htmlspecialchars($ext))) . '</p>');
-    }
-    $type = $mime_types[$ext];
-    //$type = explode(';', $type); $type = $type[0];
-    //if(!in_array($type, $allowed_mime_types)) die_friendly('Upload failed', '<p>The file type "'.$type.'" is not allowed.</p>');
-    if($_POST['rename'] != '')
-    {
-      $filename = $_POST['rename'];
-    }
-    else
-    {
-      $filename = $file['name'];
-    }
-    $bad_chars = Array(':', '\\', '/', '<', '>', '|', '*', '?', '"', '#', '+');
-    foreach($bad_chars as $ch)
-    {
-      if(strstr($filename, $ch) || preg_match('/^([ ]+)$/is', $filename))
-      {
-        die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_banned_chars') . '</p>');
-      }
-    }
-    
-    $ns = namespace_factory($filename, 'File');
-    $cdata = $ns->get_cdata();
-    $is_protected = $cdata['really_protected'];
-    
-    if ( isPage($paths->get_pathskey($filename, 'File')) && !isset ( $_POST['update'] ) )
-    {
-      $upload_link = makeUrlNS('Special', 'UploadFile/'.$filename);
-      die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_already_exists', array('upload_link' => $upload_link)) . '</p>');
-    }
-    else if ( isset($_POST['update']) && $is_protected )
-    {
-      die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_replace_protected') . '</p>');
-    }
-    
-    $utime = time();
-           
-    $filename = $db->escape(sanitize_page_id($filename));
-    $ext = substr($filename, strrpos($filename, '.'), strlen($filename));
-    $flen = filesize($file['tmp_name']);
-    
-    $perms = $session->fetch_page_acl($filename, 'File');
-    $comments = ( isset($_POST['update']) ) ? $db->escape($_POST['comments']) : $db->escape(RenderMan::preprocess_text($_POST['comments'], false, false, true, $perms));
-    $chartag = sha1(microtime());
-    $urln = str_replace(' ', '_', $filename);
-    
-    $key = md5($filename . '_' . ( function_exists('md5_file') ? md5_file($file['tmp_name']) : file_get_contents($file['tmp_name'])));
-    $targetname = ENANO_ROOT . '/files/' . $key . $ext;
-    
-    if(!@move_uploaded_file($file['tmp_name'], $targetname))
-    {
-      die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_move_failed') . '</p>');
-    }
-    
-    if(getConfig('file_history') != '1')
-    {
-      if(!$db->sql_query('DELETE FROM  '.table_prefix.'files WHERE filename=\''.$filename.'\' LIMIT 1;')) $db->_die('The old file data could not be deleted.');
-    }
-    if(!$db->sql_query('INSERT INTO '.table_prefix.'files(time_id,page_id,filename,size,mimetype,file_extension,file_key) VALUES('.$utime.', \''.$urln.'\', \''.$filename.'\', '.$flen.', \''.$type.'\', \''.$ext.'\', \''.$key.'\')')) $db->_die('The file data entry could not be inserted.');
-    if(!isset($_POST['update']))
-    {
-      if(!$db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,author_uid,page_id,namespace) VALUES('.$utime.', \''.enano_date(ED_DATE | ED_TIME).'\', \'page\', \'create\', \''.$session->username.'\',' . $session->user_id . ', \''.$filename.'\', \''.'File'.'\');')) $db->_die('The page log could not be updated.');
-      if(!$db->sql_query('INSERT INTO '.table_prefix.'pages(name,urlname,namespace,protected,delvotes,delvote_ips) VALUES(\''.$filename.'\', \''.$urln.'\', \'File\', 0, 0, \'\')')) $db->_die('The page listing entry could not be inserted.');
-      if(!$db->sql_query('INSERT INTO '.table_prefix.'page_text(page_id,namespace,page_text,char_tag) VALUES(\''.$urln.'\', \'File\', \''.$comments.'\', \''.$chartag.'\')')) $db->_die('The page text entry could not be inserted.');
-    }
-    else
-    {
-      if(!$db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,author_uid,page_id,namespace,edit_summary) VALUES('.$utime.', \''.enano_date(ED_DATE | ED_TIME).'\', \'page\', \'reupload\', \''.$session->username.'\',' . $session->user_id . ', \''.$filename.'\', \''.'File'.'\', \''.$comments.'\');')) $db->_die('The page log could not be updated.');
-    }
-    $cache->purge('page_meta');
-    die_friendly($lang->get('upload_success_title'), '<p>' . $lang->get('upload_success_body', array('file_link' => makeUrlNS('File', $filename))) . '</p>');
-  }
-  else
-  {
-    $template->header();
-    $fn = $paths->getParam(0);
-    if ( $fn && !$session->get_permissions('upload_new_version') )
-    {
-      die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('upload_err_replace_denied') . '<p>');
-    }
-    ?>
-    <p><?php echo $lang->get('upload_intro'); ?></p>
-    <p><?php 
-      // Get the max file size, and format it in a way that is user-friendly
-      
-      $fs = getConfig('max_file_size', '256000');
-      $fs = (int)$fs;
-      if($fs >= 1048576)
-      {
-        $fs = round($fs / 1048576, 1);
-        $unitized = $fs . ' ' . $lang->get('etc_unit_megabytes_short');
-      }
-      elseif($fs >= 1024)
-      {
-        $fs = round($fs / 1024, 1);
-        $unitized = $fs . ' ' . $lang->get('etc_unit_kilobytes_short');
-      }
-      
-      echo $lang->get('upload_max_filesize', array(
-          'size' => $unitized
-        ));
-    ?></p>
-    <form action="<?php echo makeUrl($paths->page); ?>" method="post" enctype="multipart/form-data">
-      <table border="0" cellspacing="1" cellpadding="4">
-        <tr><td><?php echo $lang->get('upload_field_file'); ?></td><td><input name="data" type="file" size="40" /></td></tr>
-        <tr><td><?php echo $lang->get('upload_field_renameto'); ?></td><td><input name="rename" type="text" size="40"<?php if($fn) echo ' value="'.$fn.'" readonly="readonly"'; ?> /></td></tr>
-        <?php
-        if(!$fn) echo '<tr><td>' . $lang->get('upload_field_comments') . '</td><td><textarea name="comments" rows="20" cols="60"></textarea></td></tr>';
-        else echo '<tr><td>' . $lang->get('upload_field_reason') . '</td><td><input name="comments" size="50" /></td></tr>';
-        ?>
-        <tr><td colspan="2" style="text-align: center">
-          <?php
-          if($fn)
-            echo '<input type="hidden" name="update" value="true" />';
-          ?>
-          <input type="submit" name="doit" value="<?php echo $lang->get('upload_btn_upload'); ?>" />
-        </td></tr>
-      </table>
-    </form>
-    <?php
-    $template->footer();
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $cache;
+	global $mime_types;
+	if(getConfig('enable_uploads')!='1') { die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('upload_err_disabled_site') . '</p>'); }
+	if ( !$session->get_permissions('upload_files') )
+	{
+		die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('upload_err_disabled_acl') . '</p>');
+	}
+	if(isset($_POST['doit']))
+	{
+		if(isset($_FILES['data']))
+		{
+			$file =& $_FILES['data'];
+		}
+		else
+		{
+			$file = false;
+		}
+		if ( !is_array($file) )
+		{
+			die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_cant_get_file_meta') . '</p>');
+		}
+		if ( $file['size'] == 0 || $file['size'] > (int)getConfig('max_file_size', '256000') )
+		{
+			die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_too_big_or_small') . '</p>');
+		}
+		
+		$types = fetch_allowed_extensions();
+		$ext = strtolower(substr($file['name'], strrpos($file['name'], '.')+1, strlen($file['name'])));
+		if ( !isset($types[$ext]) || ( isset($types[$ext]) && !$types[$ext] ) )
+		{
+			die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_banned_ext', array('ext' => htmlspecialchars($ext))) . '</p>');
+		}
+		$type = $mime_types[$ext];
+		//$type = explode(';', $type); $type = $type[0];
+		//if(!in_array($type, $allowed_mime_types)) die_friendly('Upload failed', '<p>The file type "'.$type.'" is not allowed.</p>');
+		if($_POST['rename'] != '')
+		{
+			$filename = $_POST['rename'];
+		}
+		else
+		{
+			$filename = $file['name'];
+		}
+		$bad_chars = Array(':', '\\', '/', '<', '>', '|', '*', '?', '"', '#', '+');
+		foreach($bad_chars as $ch)
+		{
+			if(strstr($filename, $ch) || preg_match('/^([ ]+)$/is', $filename))
+			{
+				die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_banned_chars') . '</p>');
+			}
+		}
+		
+		$ns = namespace_factory($filename, 'File');
+		$cdata = $ns->get_cdata();
+		$is_protected = $cdata['really_protected'];
+		
+		if ( isPage($paths->get_pathskey($filename, 'File')) && !isset ( $_POST['update'] ) )
+		{
+			$upload_link = makeUrlNS('Special', 'UploadFile/'.$filename);
+			die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_already_exists', array('upload_link' => $upload_link)) . '</p>');
+		}
+		else if ( isset($_POST['update']) && $is_protected )
+		{
+			die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_replace_protected') . '</p>');
+		}
+		
+		$utime = time();
+ 					
+		$filename = $db->escape(sanitize_page_id($filename));
+		$ext = substr($filename, strrpos($filename, '.'), strlen($filename));
+		$flen = filesize($file['tmp_name']);
+		
+		$perms = $session->fetch_page_acl($filename, 'File');
+		$comments = ( isset($_POST['update']) ) ? $db->escape($_POST['comments']) : $db->escape(RenderMan::preprocess_text($_POST['comments'], false, false, true, $perms));
+		$chartag = sha1(microtime());
+		$urln = str_replace(' ', '_', $filename);
+		
+		$key = md5($filename . '_' . ( function_exists('md5_file') ? md5_file($file['tmp_name']) : file_get_contents($file['tmp_name'])));
+		$targetname = ENANO_ROOT . '/files/' . $key . $ext;
+		
+		if(!@move_uploaded_file($file['tmp_name'], $targetname))
+		{
+			die_friendly($lang->get('upload_err_title'), '<p>' . $lang->get('upload_err_move_failed') . '</p>');
+		}
+		
+		if(getConfig('file_history') != '1')
+		{
+			if(!$db->sql_query('DELETE FROM  '.table_prefix.'files WHERE filename=\''.$filename.'\' LIMIT 1;')) $db->_die('The old file data could not be deleted.');
+		}
+		if(!$db->sql_query('INSERT INTO '.table_prefix.'files(time_id,page_id,filename,size,mimetype,file_extension,file_key) VALUES('.$utime.', \''.$urln.'\', \''.$filename.'\', '.$flen.', \''.$type.'\', \''.$ext.'\', \''.$key.'\')')) $db->_die('The file data entry could not be inserted.');
+		if(!isset($_POST['update']))
+		{
+			if(!$db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,author_uid,page_id,namespace) VALUES('.$utime.', \''.enano_date(ED_DATE | ED_TIME).'\', \'page\', \'create\', \''.$session->username.'\',' . $session->user_id . ', \''.$filename.'\', \''.'File'.'\');')) $db->_die('The page log could not be updated.');
+			if(!$db->sql_query('INSERT INTO '.table_prefix.'pages(name,urlname,namespace,protected,delvotes,delvote_ips) VALUES(\''.$filename.'\', \''.$urln.'\', \'File\', 0, 0, \'\')')) $db->_die('The page listing entry could not be inserted.');
+			if(!$db->sql_query('INSERT INTO '.table_prefix.'page_text(page_id,namespace,page_text,char_tag) VALUES(\''.$urln.'\', \'File\', \''.$comments.'\', \''.$chartag.'\')')) $db->_die('The page text entry could not be inserted.');
+		}
+		else
+		{
+			if(!$db->sql_query('INSERT INTO '.table_prefix.'logs(time_id,date_string,log_type,action,author,author_uid,page_id,namespace,edit_summary) VALUES('.$utime.', \''.enano_date(ED_DATE | ED_TIME).'\', \'page\', \'reupload\', \''.$session->username.'\',' . $session->user_id . ', \''.$filename.'\', \''.'File'.'\', \''.$comments.'\');')) $db->_die('The page log could not be updated.');
+		}
+		$cache->purge('page_meta');
+		die_friendly($lang->get('upload_success_title'), '<p>' . $lang->get('upload_success_body', array('file_link' => makeUrlNS('File', $filename))) . '</p>');
+	}
+	else
+	{
+		$template->header();
+		$fn = $paths->getParam(0);
+		if ( $fn && !$session->get_permissions('upload_new_version') )
+		{
+			die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('upload_err_replace_denied') . '<p>');
+		}
+		?>
+		<p><?php echo $lang->get('upload_intro'); ?></p>
+		<p><?php 
+			// Get the max file size, and format it in a way that is user-friendly
+			
+			$fs = getConfig('max_file_size', '256000');
+			$fs = (int)$fs;
+			if($fs >= 1048576)
+			{
+				$fs = round($fs / 1048576, 1);
+				$unitized = $fs . ' ' . $lang->get('etc_unit_megabytes_short');
+			}
+			elseif($fs >= 1024)
+			{
+				$fs = round($fs / 1024, 1);
+				$unitized = $fs . ' ' . $lang->get('etc_unit_kilobytes_short');
+			}
+			
+			echo $lang->get('upload_max_filesize', array(
+					'size' => $unitized
+				));
+		?></p>
+		<form action="<?php echo makeUrl($paths->page); ?>" method="post" enctype="multipart/form-data">
+			<table border="0" cellspacing="1" cellpadding="4">
+				<tr><td><?php echo $lang->get('upload_field_file'); ?></td><td><input name="data" type="file" size="40" /></td></tr>
+				<tr><td><?php echo $lang->get('upload_field_renameto'); ?></td><td><input name="rename" type="text" size="40"<?php if($fn) echo ' value="'.$fn.'" readonly="readonly"'; ?> /></td></tr>
+				<?php
+				if(!$fn) echo '<tr><td>' . $lang->get('upload_field_comments') . '</td><td><textarea name="comments" rows="20" cols="60"></textarea></td></tr>';
+				else echo '<tr><td>' . $lang->get('upload_field_reason') . '</td><td><input name="comments" size="50" /></td></tr>';
+				?>
+				<tr><td colspan="2" style="text-align: center">
+					<?php
+					if($fn)
+						echo '<input type="hidden" name="update" value="true" />';
+					?>
+					<input type="submit" name="doit" value="<?php echo $lang->get('upload_btn_upload'); ?>" />
+				</td></tr>
+			</table>
+		</form>
+		<?php
+		$template->footer();
+	}
 }                                                     
 
 function page_Special_DownloadFile()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $do_gzip;
-  $filename = $paths->getParam(0);
-  $timeid = $paths->getParam(1);
-  if ( $timeid && ctype_digit((string)$timeid) )
-  {
-    $tid = ' AND time_id='.$timeid;
-  }
-  else
-  {
-    $tid = '';
-  }
-  $filename = $db->escape(sanitize_page_id($filename));
-  
-  $q = $db->sql_query('SELECT page_id,size,mimetype,time_id,file_extension,file_key FROM '.table_prefix.'files WHERE filename=\''.$filename.'\''.$tid.' ORDER BY time_id DESC;');
-  if ( !$q )
-  {
-    $db->_die('The file data could not be selected.');
-  }
-  if ( $db->numrows() < 1 )
-  {
-    header('HTTP/1.1 404 Not Found');
-    die_friendly($lang->get('upload_err_not_found_title'), '<p>' . $lang->get('upload_err_not_found_body', array('filename' => htmlspecialchars($filename))) . '</p>');
-  }
-  $row = $db->fetchrow();
-  $db->free_result();
-  
-  // Check permissions
-  $perms = $session->fetch_page_acl($row['page_id'], 'File');
-  if ( !$perms->get_permissions('read') )
-  {
-    die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
-  }
-  
-  $fname = ENANO_ROOT . '/files/' . $row['file_key'] . $row['file_extension'];
-  if ( !file_exists($fname) )
-  {
-    $fname = ENANO_ROOT . '/files/' . $row['file_key'] . '_' . $row['time_id'] . $row['file_extension'];
-  }
-  if ( !file_exists($fname) )
-  {
-    die("Uploaded file $fname not found.");
-  }
-  
-  if ( isset($_GET['preview']) && substr($row['mimetype'], 0, 6) == 'image/' )
-  {
-    // Determine appropriate width and height
-    $width  = ( isset($_GET['width'])  ) ? intval($_GET['width'] ) : 320;
-    $height = ( isset($_GET['height']) ) ? intval($_GET['height']) : 320;
-    
-    // 1.1.7: allow different format output
-    $extension = $row['file_extension'];
-    if ( isset($_GET['fmt']) && in_array($_GET['fmt'], array('png', 'jpg')) )
-      $extension = ".{$_GET['fmt']}";
-    
-    $cache_filename = ENANO_ROOT . "/cache/{$filename}-{$row['time_id']}-{$width}x{$height}$extension";
-    if ( file_exists($cache_filename) )
-    {
-      $fname = $cache_filename;
-    }
-    else
-    {
-      $allow_scale = false;
-      $orig_fname = $fname;
-      // is caching enabled?
-      if ( getConfig('cache_thumbs') == '1' )
-      {
-        $fname = $cache_filename;
-        if ( is_writeable(dirname($fname)) )
-        {
-          $allow_scale = true;
-        }
-      }
-      else
-      {
-        // Get a temporary file
-        // In this case, the file will not be cached and will be scaled each time it's requested
-        $temp_dir = sys_get_temp_dir();
-        // if tempnam() cannot use the specified directory name, it will fall back on the system default
-        $tempname = tempnam($temp_dir, $filename);
-        if ( $tempname && is_writeable($tempname) )
-        {
-          $allow_scale = true;
-        }
-      }
-      if ( $allow_scale )
-      {
-        $result = scale_image($orig_fname, $fname, $width, $height);
-        if ( !$result )
-          $fname = $orig_fname;
-      }
-      else
-      {
-        $fname = $orig_fname;
-      }
-    }
-  }
-  $handle = @fopen($fname, 'r');
-  if ( !$handle )
-    die('Can\'t open output file for reading');
-  
-  $len = filesize($fname);
-  header('Content-type: '.$row['mimetype']);
-  if ( isset($_GET['download']) )
-  {
-    header('Content-disposition: attachment, filename="' . $filename . '";');
-  }
-  if ( !@$GLOBALS['do_gzip'] )
-    header('Content-length: ' . $len);
-  
-  header('Last-Modified: '.enano_date('r', $row['time_id']));
-  
-  // using this method limits RAM consumption
-  while ( !feof($handle) )
-  {
-    echo fread($handle, 512000);
-  }
-  fclose($handle);
-  
-  gzip_output();
-  
-  exit;
-  
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $do_gzip;
+	$filename = $paths->getParam(0);
+	$timeid = $paths->getParam(1);
+	if ( $timeid && ctype_digit((string)$timeid) )
+	{
+		$tid = ' AND time_id='.$timeid;
+	}
+	else
+	{
+		$tid = '';
+	}
+	$filename = $db->escape(sanitize_page_id($filename));
+	
+	$q = $db->sql_query('SELECT page_id,size,mimetype,time_id,file_extension,file_key FROM '.table_prefix.'files WHERE filename=\''.$filename.'\''.$tid.' ORDER BY time_id DESC;');
+	if ( !$q )
+	{
+		$db->_die('The file data could not be selected.');
+	}
+	if ( $db->numrows() < 1 )
+	{
+		header('HTTP/1.1 404 Not Found');
+		die_friendly($lang->get('upload_err_not_found_title'), '<p>' . $lang->get('upload_err_not_found_body', array('filename' => htmlspecialchars($filename))) . '</p>');
+	}
+	$row = $db->fetchrow();
+	$db->free_result();
+	
+	// Check permissions
+	$perms = $session->fetch_page_acl($row['page_id'], 'File');
+	if ( !$perms->get_permissions('read') )
+	{
+		die_friendly($lang->get('etc_access_denied_short'), '<p>' . $lang->get('etc_access_denied') . '</p>');
+	}
+	
+	$fname = ENANO_ROOT . '/files/' . $row['file_key'] . $row['file_extension'];
+	if ( !file_exists($fname) )
+	{
+		$fname = ENANO_ROOT . '/files/' . $row['file_key'] . '_' . $row['time_id'] . $row['file_extension'];
+	}
+	if ( !file_exists($fname) )
+	{
+		die("Uploaded file $fname not found.");
+	}
+	
+	if ( isset($_GET['preview']) && substr($row['mimetype'], 0, 6) == 'image/' )
+	{
+		// Determine appropriate width and height
+		$width  = ( isset($_GET['width'])  ) ? intval($_GET['width'] ) : 320;
+		$height = ( isset($_GET['height']) ) ? intval($_GET['height']) : 320;
+		
+		// 1.1.7: allow different format output
+		$extension = $row['file_extension'];
+		if ( isset($_GET['fmt']) && in_array($_GET['fmt'], array('png', 'jpg')) )
+			$extension = ".{$_GET['fmt']}";
+		
+		$cache_filename = ENANO_ROOT . "/cache/{$filename}-{$row['time_id']}-{$width}x{$height}$extension";
+		if ( file_exists($cache_filename) )
+		{
+			$fname = $cache_filename;
+		}
+		else
+		{
+			$allow_scale = false;
+			$orig_fname = $fname;
+			// is caching enabled?
+			if ( getConfig('cache_thumbs') == '1' )
+			{
+				$fname = $cache_filename;
+				if ( is_writeable(dirname($fname)) )
+				{
+					$allow_scale = true;
+				}
+			}
+			else
+			{
+				// Get a temporary file
+				// In this case, the file will not be cached and will be scaled each time it's requested
+				$temp_dir = sys_get_temp_dir();
+				// if tempnam() cannot use the specified directory name, it will fall back on the system default
+				$tempname = tempnam($temp_dir, $filename);
+				if ( $tempname && is_writeable($tempname) )
+				{
+					$allow_scale = true;
+				}
+			}
+			if ( $allow_scale )
+			{
+				$result = scale_image($orig_fname, $fname, $width, $height);
+				if ( !$result )
+					$fname = $orig_fname;
+			}
+			else
+			{
+				$fname = $orig_fname;
+			}
+		}
+	}
+	$handle = @fopen($fname, 'r');
+	if ( !$handle )
+		die('Can\'t open output file for reading');
+	
+	$len = filesize($fname);
+	header('Content-type: '.$row['mimetype']);
+	if ( isset($_GET['download']) )
+	{
+		header('Content-disposition: attachment, filename="' . $filename . '";');
+	}
+	if ( !@$GLOBALS['do_gzip'] )
+		header('Content-length: ' . $len);
+	
+	header('Last-Modified: '.enano_date('r', $row['time_id']));
+	
+	// using this method limits RAM consumption
+	while ( !feof($handle) )
+	{
+		echo fread($handle, 512000);
+	}
+	fclose($handle);
+	
+	gzip_output();
+	
+	exit;
+	
 }
 
 ?>
\ No newline at end of file
--- a/plugins/SpecialUserFuncs.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialUserFuncs.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_specialuserfuncs_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_specialuserfuncs_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_specialuserfuncs_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_specialuserfuncs_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -27,18 +27,18 @@
 
 function SpecialUserFuncs_paths_init()
 {
-  register_special_page('Login', 'specialpage_log_in');
-  register_special_page('Logout', 'specialpage_log_out');
-  register_special_page('Register', 'specialpage_register');
-  register_special_page('Preferences', 'specialpage_preferences');
-  register_special_page('Contributions', 'specialpage_contributions');
-  register_special_page('ChangeStyle', 'specialpage_change_theme');
-  register_special_page('ActivateAccount', 'specialpage_activate_account');
-  register_special_page('Captcha', 'specialpage_captcha');
-  register_special_page('PasswordReset', 'specialpage_password_reset');
-  register_special_page('Memberlist', 'specialpage_member_list');
-  register_special_page('LangExportJSON', 'specialpage_language_export', false);
-  register_special_page('Avatar', 'specialpage_avatar', false);
+	register_special_page('Login', 'specialpage_log_in');
+	register_special_page('Logout', 'specialpage_log_out');
+	register_special_page('Register', 'specialpage_register');
+	register_special_page('Preferences', 'specialpage_preferences');
+	register_special_page('Contributions', 'specialpage_contributions');
+	register_special_page('ChangeStyle', 'specialpage_change_theme');
+	register_special_page('ActivateAccount', 'specialpage_activate_account');
+	register_special_page('Captcha', 'specialpage_captcha');
+	register_special_page('PasswordReset', 'specialpage_password_reset');
+	register_special_page('Memberlist', 'specialpage_member_list');
+	register_special_page('LangExportJSON', 'specialpage_language_export', false);
+	register_special_page('Avatar', 'specialpage_avatar', false);
 }
 
 // function names are IMPORTANT!!! The name pattern is: page_<namespace ID>_<page URLname, without namespace>
@@ -47,405 +47,405 @@
 
 function page_Special_Login()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $login_result;
-  global $lang, $output;
-  
-  // Determine which level we're going up to
-  $level = ( isset($_GET['level']) && in_array($_GET['level'], array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ) ) ? intval($_GET['level']) : USER_LEVEL_MEMBER;
-  if ( isset($_POST['login']) )
-  {
-    if ( in_array($_POST['level'], array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ) )
-    {
-      $level = intval($_POST['level']);
-    }
-  }
-  // Don't allow going from guest straight to elevated
-  // FIXME do we want to allow this with a CSRF check?
-  if ( $level > USER_LEVEL_MEMBER && !$session->user_logged_in )
-  {
-    $level = USER_LEVEL_MEMBER;
-  }
-  
-  // If we're already at or above this level, redirect to the target page or, if no target
-  // specified, back to the main page.
-  if ( $level <= USER_LEVEL_MEMBER && $session->user_logged_in )
-  {
-    if ( $target = $paths->getAllParams() )
-    {
-      redirect(makeUrl($target), '', '', 0);
-    }
-    $paths->main_page();
-  }
-  
-  // Lockout aliasing
-  $lockout =& $login_result['lockout'];
-  
-  $output->header();
-  echo '<form action="' . makeUrl($paths->nslist['Special'].'Login') . '" method="post" name="loginform" onsubmit="try { return runEncryption(); } catch(e) { console.error(e); };">';
-  
-  if ( $p = $paths->getAllParams() )
-  {
-    echo '<input type="hidden" name="return_to" value="' . htmlspecialchars($p) . '" />';
-  }
-  else if ( isset($_POST['login']) && isset($_POST['return_to']) )
-  {
-    echo '<input type="hidden" name="return_to" value="' . htmlspecialchars($_POST['return_to']) . '" />';
-  }
-  
-  // determine what the "remember me" checkbox should say
-  $session_time = intval(getConfig('session_remember_time', '30'));
-  if ( $session_time === 0 )
-  {
-    // sessions are infinite
-    $text_remember = $lang->get('user_login_check_remember_infinite');
-  }
-  else
-  {
-    // is the number of days evenly divisible by 7? if so, use weeks
-    if ( $session_time % 7 == 0 )
-    {
-      $session_time = $session_time / 7;
-      $unit = 'week';
-    }
-    else
-    {
-      $unit = 'day';
-    }
-    // if it's not equal to 1, pluralize it
-    if ( $session_time != 1 )
-    {
-      $unit .= $lang->get('meta_plural');
-    }
-    $text_remember = $lang->get('user_login_check_remember', array(
-        'session_length' => $session_time,
-        'length_units' => $lang->get("etc_unit_$unit")
-      ));
-  }
-  
-  if ( $error_text = login_get_error($login_result) )
-  {
-    echo '<div class="error-box-mini">' . htmlspecialchars($error_text) . '</div>';
-  }
-  
-  //
-  // START FORM
-  //
-  ?>
-    <div class="tblholder">
-      <table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
-        <tr>
-          <th colspan="3">
-            <!-- Table header: "Please enter..." -->
-            <?php echo ( $level > USER_LEVEL_MEMBER ) ? $lang->get('user_login_message_short_elev') : $lang->get('user_login_message_short'); ?>
-          </th>
-        </tr>
-        <tr>
-          <td colspan="3" class="row1">
-            <!-- Introduction text -->
-            <?php
-            if ( $level <= USER_LEVEL_MEMBER )
-              echo '<p>' . $lang->get('user_login_body', array('reg_link' => makeUrlNS('Special', 'Register'))) . '</p>';
-            else
-              echo '<p>' . $lang->get('user_login_body_elev') . '</p>';
-            ?>
-          </td>
-        </tr>
-        <tr>
-          <!-- Username field -->
-          <td class="row2">
-            <?php echo $lang->get('user_login_field_username'); ?>:
-          </td>
-          <td class="row1">
-            <input name="username" size="25" type="text" value="<?php echo $session->user_logged_in ? htmlspecialchars($session->username) : ''; ?>" />
-          </td>
-          <?php if ( $level <= USER_LEVEL_MEMBER ): ?>
-          <!-- Forgot password / create account links -->
-          <td rowspan="<?php echo ( ( $lockout['active'] && $lockout['policy'] == 'captcha' ) ) ? '4' : '2'; ?>" class="row3">
-            <small><?php echo $lang->get('user_login_forgotpass_blurb', array('forgotpass_link' => makeUrlNS('Special', 'PasswordReset'))); ?><br />
-            <?php echo $lang->get('user_login_createaccount_blurb', array('reg_link' => makeUrlNS('Special', 'Register'))); ?></small>
-          </td>
-          <?php endif; ?>
-        </tr>
-        <tr>
-          <!-- Password field -->
-          <td class="row2">
-            <?php echo $lang->get('user_login_field_password'); ?>:
-          </td><td class="row1"><input name="password" size="25" type="password" /></td>
-         </tr>
-         
-         <?php
-         // CAPTCHA?
-         if ( $lockout['active'] && $lockout['policy'] == 'captcha' )
-         {
-           ?>
-           <!-- CAPTCHA -->
-           <tr>
-             <td class="row2" rowspan="2">
-               <?php echo $lang->get('user_login_field_captcha'); ?>:
-               <br />
-             </td>
-             <td class="row1">
-               <input type="hidden" name="captcha_hash" value="<?php echo $lockout['captcha']; ?>" />
-               <input name="captcha_code" size="25" type="text" tabindex="<?php echo ( $level <= USER_LEVEL_MEMBER ) ? '3' : '4'; ?>" />
-             </td>
-           </tr>
-           <tr>
-             <td class="row3">
-               <img src="<?php echo makeUrlNS('Special', 'Captcha/' . $lockout['captcha']) ?>" onclick="this.src=this.src+'/a';" style="cursor: pointer;" />
-             </td>
-           </tr>
-           <?php
-         }
-         
-         // Run hooks
-         $code = $plugins->setHook('login_form_html');
-         foreach ( $code as $cmd )
-         {
-           eval($cmd);
-         }
-         
-         // level-2 only: "Remember me" switch
-         if ( $level <= USER_LEVEL_MEMBER )
-         {
-           ?>
-           <tr>
-             <td class="row2">
-               <?php echo $lang->get('user_login_field_remember'); ?>
-             </td>
-             <td class="row1" colspan="2">
-               <label>
-                 <input type="checkbox" name="remember" tabindex="3" />
-                 <?php echo $text_remember; ?>
-               </label>
-             </td>
-           </tr>
-           
-         <!-- Crypto notice -->
-           <?php
-         }
-         
-         // lol DeMorgan'd
-         $crypto_disable = ( isset($_GET['use_crypt']) && $_GET['use_crypt'] == '0' );
-         
-         // Crypto disable: crypto on, normal login
-         if ( $level <= USER_LEVEL_MEMBER && !$crypto_disable )
-         {
-           echo '<tr>
-             <td class="row3" colspan="3">';
-             
-           $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
-           $nocrypt_link = makeUrlNS('Special', "Login$returnpage_link", "level=$level&use_crypt=0", true);
-           echo '<p><b>' . $lang->get('user_login_nocrypt_title') . '</b> ' . $lang->get('user_login_nocrypt_body', array('nocrypt_link' => $nocrypt_link)) . '</p>';
-           echo '<p>' . $lang->get('user_login_nocrypt_countrylist') . '</p>';
-           
-           echo '  </td>
-           </tr>';
-         }
-         // Crypto disable: crypto OFF, normal login
-         else if ( $level <= USER_LEVEL_MEMBER && $crypto_disable )
-         {
-           echo '<tr>
-             <td class="row3" colspan="3">';
-             
-           $returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
-           $usecrypt_link = makeUrlNS('Special', "Login$returnpage_link", "level=$level&use_crypt=1", true);
-           echo '<p><b>' . $lang->get('user_login_usecrypt_title') . '</b> ' . $lang->get('user_login_usecrypt_body', array('usecrypt_link' => $usecrypt_link)) . '</p>';
-           echo '<p>' . $lang->get('user_login_usecrypt_countrylist') . '</p>';
-           
-           echo '  </td>
-           </tr>';
-         }
-         // Crypto disable: crypto on, ELEV login
-         else if ( $level > USER_LEVEL_MEMBER && $GLOBALS['dh_supported'] )
-         {
-           echo '<tr>';
-           echo '<td class="row3" colspan="3">';
-           echo '<p>' . $lang->get('user_login_dh_notice') . '</p>';
-           echo '</td>';
-           echo '</tr>';
-         }
-         ?>
-         
-         <!-- Submit button -->
-         <tr>
-           <th colspan="3" style="text-align: center" class="subhead">
-             <input type="hidden" name="login" value="true" />
-             <input type="submit" value="<?php echo $lang->get('user_login_btn_log_in'); ?>" />
-           </th>
-         </tr>
-      </table>
-    </div>
-    
-      <input type="hidden" name="level" value="<?php echo (string)$level; ?>" />
-      <?php if ( $level <= USER_LEVEL_MEMBER ): ?>
-      <script type="text/javascript">
-        document.forms.loginform.username.focus();
-      </script>
-      <?php else: ?>
-      <script type="text/javascript">
-        document.forms.loginform.pass.focus();
-      </script>
-      <?php endif; ?>
-      <?php
-      echo $session->generate_aes_form();
-      
-      // Any additional parameters that need to be passed back?
-      if ( $p = $paths->getAllParams() )
-      {
-        // ... only if we have a return_to destination.
-        $get_fwd = $_GET;
-        unset($get_fwd['do']);
-        if ( isset($get_fwd['target_do']) )
-        {
-          $get_fwd['do'] = $get_fwd['target_do'];
-          unset($get_fwd['target_do']);
-        }
-        if ( isset($get_fwd['level']) )
-          unset($get_fwd['level']);
-        if ( isset($get_fwd['title']) )
-          unset($get_fwd['title']);
-        
-        if ( !empty($get_fwd) )
-        {
-          $get_string = htmlspecialchars(enano_json_encode($get_fwd));
-          echo '<input type="hidden" name="get_fwd" value="' . $get_string . '" />';
-        }
-      }
-      else if ( isset($_POST['get_fwd']) )
-      {
-        echo '<input type="hidden" name="get_fwd" value="' . htmlspecialchars($_POST['get_fwd']) . '" />';
-      }
-      ?>
-    </form>
-    <?php
-      if ( !$crypto_disable )
-        echo $session->aes_javascript('loginform', 'password');
-    ?>
-  <?php
-  $output->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $login_result;
+	global $lang, $output;
+	
+	// Determine which level we're going up to
+	$level = ( isset($_GET['level']) && in_array($_GET['level'], array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ) ) ? intval($_GET['level']) : USER_LEVEL_MEMBER;
+	if ( isset($_POST['login']) )
+	{
+		if ( in_array($_POST['level'], array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ) )
+		{
+			$level = intval($_POST['level']);
+		}
+	}
+	// Don't allow going from guest straight to elevated
+	// FIXME do we want to allow this with a CSRF check?
+	if ( $level > USER_LEVEL_MEMBER && !$session->user_logged_in )
+	{
+		$level = USER_LEVEL_MEMBER;
+	}
+	
+	// If we're already at or above this level, redirect to the target page or, if no target
+	// specified, back to the main page.
+	if ( $level <= USER_LEVEL_MEMBER && $session->user_logged_in )
+	{
+		if ( $target = $paths->getAllParams() )
+		{
+			redirect(makeUrl($target), '', '', 0);
+		}
+		$paths->main_page();
+	}
+	
+	// Lockout aliasing
+	$lockout =& $login_result['lockout'];
+	
+	$output->header();
+	echo '<form action="' . makeUrl($paths->nslist['Special'].'Login') . '" method="post" name="loginform" onsubmit="try { return runEncryption(); } catch(e) { console.error(e); };">';
+	
+	if ( $p = $paths->getAllParams() )
+	{
+		echo '<input type="hidden" name="return_to" value="' . htmlspecialchars($p) . '" />';
+	}
+	else if ( isset($_POST['login']) && isset($_POST['return_to']) )
+	{
+		echo '<input type="hidden" name="return_to" value="' . htmlspecialchars($_POST['return_to']) . '" />';
+	}
+	
+	// determine what the "remember me" checkbox should say
+	$session_time = intval(getConfig('session_remember_time', '30'));
+	if ( $session_time === 0 )
+	{
+		// sessions are infinite
+		$text_remember = $lang->get('user_login_check_remember_infinite');
+	}
+	else
+	{
+		// is the number of days evenly divisible by 7? if so, use weeks
+		if ( $session_time % 7 == 0 )
+		{
+			$session_time = $session_time / 7;
+			$unit = 'week';
+		}
+		else
+		{
+			$unit = 'day';
+		}
+		// if it's not equal to 1, pluralize it
+		if ( $session_time != 1 )
+		{
+			$unit .= $lang->get('meta_plural');
+		}
+		$text_remember = $lang->get('user_login_check_remember', array(
+				'session_length' => $session_time,
+				'length_units' => $lang->get("etc_unit_$unit")
+			));
+	}
+	
+	if ( $error_text = login_get_error($login_result) )
+	{
+		echo '<div class="error-box-mini">' . htmlspecialchars($error_text) . '</div>';
+	}
+	
+	//
+	// START FORM
+	//
+	?>
+		<div class="tblholder">
+			<table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
+				<tr>
+					<th colspan="3">
+						<!-- Table header: "Please enter..." -->
+						<?php echo ( $level > USER_LEVEL_MEMBER ) ? $lang->get('user_login_message_short_elev') : $lang->get('user_login_message_short'); ?>
+					</th>
+				</tr>
+				<tr>
+					<td colspan="3" class="row1">
+						<!-- Introduction text -->
+						<?php
+						if ( $level <= USER_LEVEL_MEMBER )
+							echo '<p>' . $lang->get('user_login_body', array('reg_link' => makeUrlNS('Special', 'Register'))) . '</p>';
+						else
+							echo '<p>' . $lang->get('user_login_body_elev') . '</p>';
+						?>
+					</td>
+				</tr>
+				<tr>
+					<!-- Username field -->
+					<td class="row2">
+						<?php echo $lang->get('user_login_field_username'); ?>:
+					</td>
+					<td class="row1">
+						<input name="username" size="25" type="text" value="<?php echo $session->user_logged_in ? htmlspecialchars($session->username) : ''; ?>" />
+					</td>
+					<?php if ( $level <= USER_LEVEL_MEMBER ): ?>
+					<!-- Forgot password / create account links -->
+					<td rowspan="<?php echo ( ( $lockout['active'] && $lockout['policy'] == 'captcha' ) ) ? '4' : '2'; ?>" class="row3">
+						<small><?php echo $lang->get('user_login_forgotpass_blurb', array('forgotpass_link' => makeUrlNS('Special', 'PasswordReset'))); ?><br />
+						<?php echo $lang->get('user_login_createaccount_blurb', array('reg_link' => makeUrlNS('Special', 'Register'))); ?></small>
+					</td>
+					<?php endif; ?>
+				</tr>
+				<tr>
+					<!-- Password field -->
+					<td class="row2">
+						<?php echo $lang->get('user_login_field_password'); ?>:
+					</td><td class="row1"><input name="password" size="25" type="password" /></td>
+ 				</tr>
+ 				
+ 				<?php
+ 				// CAPTCHA?
+ 				if ( $lockout['active'] && $lockout['policy'] == 'captcha' )
+ 				{
+ 					?>
+ 					<!-- CAPTCHA -->
+ 					<tr>
+ 						<td class="row2" rowspan="2">
+ 							<?php echo $lang->get('user_login_field_captcha'); ?>:
+ 							<br />
+ 						</td>
+ 						<td class="row1">
+ 							<input type="hidden" name="captcha_hash" value="<?php echo $lockout['captcha']; ?>" />
+ 							<input name="captcha_code" size="25" type="text" tabindex="<?php echo ( $level <= USER_LEVEL_MEMBER ) ? '3' : '4'; ?>" />
+ 						</td>
+ 					</tr>
+ 					<tr>
+ 						<td class="row3">
+ 							<img src="<?php echo makeUrlNS('Special', 'Captcha/' . $lockout['captcha']) ?>" onclick="this.src=this.src+'/a';" style="cursor: pointer;" />
+ 						</td>
+ 					</tr>
+ 					<?php
+ 				}
+ 				
+ 				// Run hooks
+ 				$code = $plugins->setHook('login_form_html');
+ 				foreach ( $code as $cmd )
+ 				{
+ 					eval($cmd);
+ 				}
+ 				
+ 				// level-2 only: "Remember me" switch
+ 				if ( $level <= USER_LEVEL_MEMBER )
+ 				{
+ 					?>
+ 					<tr>
+ 						<td class="row2">
+ 							<?php echo $lang->get('user_login_field_remember'); ?>
+ 						</td>
+ 						<td class="row1" colspan="2">
+ 							<label>
+ 								<input type="checkbox" name="remember" tabindex="3" />
+ 								<?php echo $text_remember; ?>
+ 							</label>
+ 						</td>
+ 					</tr>
+ 					
+ 				<!-- Crypto notice -->
+ 					<?php
+ 				}
+ 				
+ 				// lol DeMorgan'd
+ 				$crypto_disable = ( isset($_GET['use_crypt']) && $_GET['use_crypt'] == '0' );
+ 				
+ 				// Crypto disable: crypto on, normal login
+ 				if ( $level <= USER_LEVEL_MEMBER && !$crypto_disable )
+ 				{
+ 					echo '<tr>
+ 						<td class="row3" colspan="3">';
+ 						
+ 					$returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
+ 					$nocrypt_link = makeUrlNS('Special', "Login$returnpage_link", "level=$level&use_crypt=0", true);
+ 					echo '<p><b>' . $lang->get('user_login_nocrypt_title') . '</b> ' . $lang->get('user_login_nocrypt_body', array('nocrypt_link' => $nocrypt_link)) . '</p>';
+ 					echo '<p>' . $lang->get('user_login_nocrypt_countrylist') . '</p>';
+ 					
+ 					echo '  </td>
+ 					</tr>';
+ 				}
+ 				// Crypto disable: crypto OFF, normal login
+ 				else if ( $level <= USER_LEVEL_MEMBER && $crypto_disable )
+ 				{
+ 					echo '<tr>
+ 						<td class="row3" colspan="3">';
+ 						
+ 					$returnpage_link = ( $return = $paths->getAllParams() ) ? '/' . $return : '';
+ 					$usecrypt_link = makeUrlNS('Special', "Login$returnpage_link", "level=$level&use_crypt=1", true);
+ 					echo '<p><b>' . $lang->get('user_login_usecrypt_title') . '</b> ' . $lang->get('user_login_usecrypt_body', array('usecrypt_link' => $usecrypt_link)) . '</p>';
+ 					echo '<p>' . $lang->get('user_login_usecrypt_countrylist') . '</p>';
+ 					
+ 					echo '  </td>
+ 					</tr>';
+ 				}
+ 				// Crypto disable: crypto on, ELEV login
+ 				else if ( $level > USER_LEVEL_MEMBER && $GLOBALS['dh_supported'] )
+ 				{
+ 					echo '<tr>';
+ 					echo '<td class="row3" colspan="3">';
+ 					echo '<p>' . $lang->get('user_login_dh_notice') . '</p>';
+ 					echo '</td>';
+ 					echo '</tr>';
+ 				}
+ 				?>
+ 				
+ 				<!-- Submit button -->
+ 				<tr>
+ 					<th colspan="3" style="text-align: center" class="subhead">
+ 						<input type="hidden" name="login" value="true" />
+ 						<input type="submit" value="<?php echo $lang->get('user_login_btn_log_in'); ?>" />
+ 					</th>
+ 				</tr>
+			</table>
+		</div>
+		
+			<input type="hidden" name="level" value="<?php echo (string)$level; ?>" />
+			<?php if ( $level <= USER_LEVEL_MEMBER ): ?>
+			<script type="text/javascript">
+				document.forms.loginform.username.focus();
+			</script>
+			<?php else: ?>
+			<script type="text/javascript">
+				document.forms.loginform.pass.focus();
+			</script>
+			<?php endif; ?>
+			<?php
+			echo $session->generate_aes_form();
+			
+			// Any additional parameters that need to be passed back?
+			if ( $p = $paths->getAllParams() )
+			{
+				// ... only if we have a return_to destination.
+				$get_fwd = $_GET;
+				unset($get_fwd['do']);
+				if ( isset($get_fwd['target_do']) )
+				{
+					$get_fwd['do'] = $get_fwd['target_do'];
+					unset($get_fwd['target_do']);
+				}
+				if ( isset($get_fwd['level']) )
+					unset($get_fwd['level']);
+				if ( isset($get_fwd['title']) )
+					unset($get_fwd['title']);
+				
+				if ( !empty($get_fwd) )
+				{
+					$get_string = htmlspecialchars(enano_json_encode($get_fwd));
+					echo '<input type="hidden" name="get_fwd" value="' . $get_string . '" />';
+				}
+			}
+			else if ( isset($_POST['get_fwd']) )
+			{
+				echo '<input type="hidden" name="get_fwd" value="' . htmlspecialchars($_POST['get_fwd']) . '" />';
+			}
+			?>
+		</form>
+		<?php
+			if ( !$crypto_disable )
+				echo $session->aes_javascript('loginform', 'password');
+		?>
+	<?php
+	$output->footer();
 }
 
 function page_Special_Login_preloader() // adding _preloader to the end of the function name calls the function before $session and $paths setup routines are called
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $login_result;
-  global $lang;
-  
-  // Are we calling the JSON interface?
-  $paths->fullpage = $GLOBALS['urlname'];
-  if ( $paths->getParam(0) === 'action.json' )
-  {
-    if ( !isset($_POST['r']) )
-      die('No request.');
-    
-    $request = $_POST['r'];
-    try
-    {
-      $request = enano_json_decode($request);
-    }
-    catch ( Exception $e )
-    {
-      die(enano_json_encode(array(
-          'mode' => 'error',
-          'error' => 'ERR_JSON_PARSE_FAILED'
-        )));
-    }
-    
-    echo enano_json_encode($session->process_login_request($request));
-    
-    $db->close();
-    exit;
-  }
-  
-  // No. Process incoming results from the HTML version.
-  if ( isset($_POST['login']) )
-  {
-    $_POST['password'] = $session->get_aes_post();
-    
-    $result = $session->process_login_request(array(
-        'mode' => 'login_pt',
-        'userinfo' => $_POST,
-        'level' => $_POST['level'],
-        'captcha_hash' => isset($_POST['captcha_hash']) ? $_POST['captcha_hash'] : false,
-        'captcha_code' => isset($_POST['captcha_code']) ? $_POST['captcha_code'] : false
-      ));
-    
-    if ( $result['mode'] === 'login_success' )
-    {
-      //
-      // LOGIN SUCCESS.
-      // Redirect as necessary.
-      //
-      
-      // Load our preferences
-      $session->start();
-      
-      // Decode get_add
-      $get_add = false;
-      if ( isset($_POST['get_fwd']) )
-      {
-        try
-        {
-          $get_fwd = enano_json_decode($_POST['get_fwd']);
-          $get_add = '';
-          foreach ( $get_fwd as $key => $value )
-          {
-            $get_add .= "&{$key}=" . urlencode($value);
-          }
-          $get_add = ltrim($get_add, '&');
-        }
-        catch ( Exception $e )
-        {
-        }
-      }
-      
-      // Going to a user-specified page?
-      if ( isset($_POST['return_to']) )
-      {
-        // yea
-        $name = get_page_title($_POST['return_to']);
-        $subst = array(
-            'username' => $session->username,
-            'redir_target' => $name
-          );
-        redirect( makeUrl($_POST['return_to'], $get_add), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
-      }
-      else
-      {
-        // No, redirect them to the main page
-        $subst = array(
-            'username' => $session->username,
-            'redir_target' => $lang->get('user_login_success_body_mainpage')
-          );
-        redirect( makeUrl(get_main_page(), $get_add), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
-      }
-    }
-    else if ( $result['mode'] === 'login_success_reset' )
-    {
-      // They logged in with a temporary password; send them to the reset form
-      redirect($result['redirect_url'], '', '', 0);
-    }
-    // Otherwise, the result is likely an error.
-    $login_result = $result;
-  }
-  else
-  {
-    $login_result = $session->process_login_request(array(
-        'mode' => 'getkey'
-      ));
-  }
-  
-  // This is a bit of a hack. The login form generates AES and DiffieHellman keys on its
-  // own, so we need to clean up the ones from the login request API.
-  if ( !empty($login_result['crypto']) )
-  {
-    $session->process_login_request(array(
-        'mode' => 'clean_key',
-        'key_aes' => $login_result['crypto']['aes_key'],
-        'key_dh' => $login_result['crypto']['dh_public_key'],
-      ));
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $login_result;
+	global $lang;
+	
+	// Are we calling the JSON interface?
+	$paths->fullpage = $GLOBALS['urlname'];
+	if ( $paths->getParam(0) === 'action.json' )
+	{
+		if ( !isset($_POST['r']) )
+			die('No request.');
+		
+		$request = $_POST['r'];
+		try
+		{
+			$request = enano_json_decode($request);
+		}
+		catch ( Exception $e )
+		{
+			die(enano_json_encode(array(
+					'mode' => 'error',
+					'error' => 'ERR_JSON_PARSE_FAILED'
+				)));
+		}
+		
+		echo enano_json_encode($session->process_login_request($request));
+		
+		$db->close();
+		exit;
+	}
+	
+	// No. Process incoming results from the HTML version.
+	if ( isset($_POST['login']) )
+	{
+		$_POST['password'] = $session->get_aes_post();
+		
+		$result = $session->process_login_request(array(
+				'mode' => 'login_pt',
+				'userinfo' => $_POST,
+				'level' => $_POST['level'],
+				'captcha_hash' => isset($_POST['captcha_hash']) ? $_POST['captcha_hash'] : false,
+				'captcha_code' => isset($_POST['captcha_code']) ? $_POST['captcha_code'] : false
+			));
+		
+		if ( $result['mode'] === 'login_success' )
+		{
+			//
+			// LOGIN SUCCESS.
+			// Redirect as necessary.
+			//
+			
+			// Load our preferences
+			$session->start();
+			
+			// Decode get_add
+			$get_add = false;
+			if ( isset($_POST['get_fwd']) )
+			{
+				try
+				{
+					$get_fwd = enano_json_decode($_POST['get_fwd']);
+					$get_add = '';
+					foreach ( $get_fwd as $key => $value )
+					{
+						$get_add .= "&{$key}=" . urlencode($value);
+					}
+					$get_add = ltrim($get_add, '&');
+				}
+				catch ( Exception $e )
+				{
+				}
+			}
+			
+			// Going to a user-specified page?
+			if ( isset($_POST['return_to']) )
+			{
+				// yea
+				$name = get_page_title($_POST['return_to']);
+				$subst = array(
+						'username' => $session->username,
+						'redir_target' => $name
+					);
+				redirect( makeUrl($_POST['return_to'], $get_add), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
+			}
+			else
+			{
+				// No, redirect them to the main page
+				$subst = array(
+						'username' => $session->username,
+						'redir_target' => $lang->get('user_login_success_body_mainpage')
+					);
+				redirect( makeUrl(get_main_page(), $get_add), $lang->get('user_login_success_title'), $lang->get('user_login_success_body', $subst) );
+			}
+		}
+		else if ( $result['mode'] === 'login_success_reset' )
+		{
+			// They logged in with a temporary password; send them to the reset form
+			redirect($result['redirect_url'], '', '', 0);
+		}
+		// Otherwise, the result is likely an error.
+		$login_result = $result;
+	}
+	else
+	{
+		$login_result = $session->process_login_request(array(
+				'mode' => 'getkey'
+			));
+	}
+	
+	// This is a bit of a hack. The login form generates AES and DiffieHellman keys on its
+	// own, so we need to clean up the ones from the login request API.
+	if ( !empty($login_result['crypto']) )
+	{
+		$session->process_login_request(array(
+				'mode' => 'clean_key',
+				'key_aes' => $login_result['crypto']['aes_key'],
+				'key_dh' => $login_result['crypto']['dh_public_key'],
+			));
+	}
 }
 
 /**
@@ -456,1296 +456,1296 @@
 
 function login_get_error($response)
 {
-  global $lang;
-  
-  if ( !empty($response['lockout']) )
-  {
-    // set this pluralality thing
-    $response['lockout']['plural'] = $response['lockout']['time_rem'] == 1 ? '' : $lang->get('meta_plural');
-  }
-  
-  if ( $response['mode'] == 'initial' )
-  {
-    // Just showing the box for the first time. If there's an error now, it's based on a preexisting lockout.
-    if ( $response['lockout']['active'] )
-    {
-      return $lang->get('user_err_locked_out_initial_' . $response['lockout']['policy'], $response['lockout']);
-    }
-    return false;
-  }
-  else
-  {
-    // An attempt was made.
-    switch($response['mode'])
-    {
-      case 'login_failure':
-        // Generic login user error.
-        $error = '';
-        if ( ($x = $lang->get($response['error'])) != $response['error'] )
-          $error = $x;
-        else
-          $error = $lang->get('user_err_' . $response['error']);
-        if ( $response['lockout']['active'] && $response['lockout']['policy'] == 'lockout' )
-        {
-          // Lockout enforcement was just activated.
-          return $lang->get('user_err_locked_out_initial_' . $response['lockout']['policy'], $response['lockout']);
-        }
-        else if ( $response['lockout']['policy'] != 'disable' && !$response['lockout']['active'] && $response['lockout']['fails'] > 0 )
-        {
-          // Lockout is in a warning state.
-          $error .= ' ' . $lang->get('user_err_invalid_credentials_' . $response['lockout']['policy'], $response['lockout']);
-        }
-        return $error;
-        break;
-      case 'api_error':
-        // Error in the API.
-        return $lang->get('user_err_login_generic_title') + ': ' + $lang->get('user_' . strtolower($response['error']));
-        break;
-    }
-  }
-  
-  return is_string($response['error']) ? $response['error'] : false;
+	global $lang;
+	
+	if ( !empty($response['lockout']) )
+	{
+		// set this pluralality thing
+		$response['lockout']['plural'] = $response['lockout']['time_rem'] == 1 ? '' : $lang->get('meta_plural');
+	}
+	
+	if ( $response['mode'] == 'initial' )
+	{
+		// Just showing the box for the first time. If there's an error now, it's based on a preexisting lockout.
+		if ( $response['lockout']['active'] )
+		{
+			return $lang->get('user_err_locked_out_initial_' . $response['lockout']['policy'], $response['lockout']);
+		}
+		return false;
+	}
+	else
+	{
+		// An attempt was made.
+		switch($response['mode'])
+		{
+			case 'login_failure':
+				// Generic login user error.
+				$error = '';
+				if ( ($x = $lang->get($response['error'])) != $response['error'] )
+					$error = $x;
+				else
+					$error = $lang->get('user_err_' . $response['error']);
+				if ( $response['lockout']['active'] && $response['lockout']['policy'] == 'lockout' )
+				{
+					// Lockout enforcement was just activated.
+					return $lang->get('user_err_locked_out_initial_' . $response['lockout']['policy'], $response['lockout']);
+				}
+				else if ( $response['lockout']['policy'] != 'disable' && !$response['lockout']['active'] && $response['lockout']['fails'] > 0 )
+				{
+					// Lockout is in a warning state.
+					$error .= ' ' . $lang->get('user_err_invalid_credentials_' . $response['lockout']['policy'], $response['lockout']);
+				}
+				return $error;
+				break;
+			case 'api_error':
+				// Error in the API.
+				return $lang->get('user_err_login_generic_title') + ': ' + $lang->get('user_' . strtolower($response['error']));
+				break;
+		}
+	}
+	
+	return is_string($response['error']) ? $response['error'] : false;
 }
 
 function page_Special_Logout()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  if ( !$session->user_logged_in )
-    $paths->main_page();
-  
-  $token = $paths->getParam(0);
-  if ( $token !== $session->csrf_token )
-  {
-    csrf_request_confirm();
-  }
-  
-  $l = $session->logout();
-  if ( $l == 'success' )
-  {
-    $url = makeUrl(get_main_page(), false, true);
-    if ( $paths->getParam(1) )
-    {
-      $pi = explode('/', $paths->getAllParams());
-      $pi = implode('/', array_values(array_slice($pi, 1)));
-      list($pid, $ns) = RenderMan::strToPageID($pi);
-      $perms = $session->fetch_page_acl($pid, $ns);
-      if ( $perms->get_permissions('read') )
-      {
-        $url = makeUrl($pi, false, true);
-      }
-    }
-    redirect($url, $lang->get('user_logout_success_title'), $lang->get('user_logout_success_body'), 3);
-  }
-  $template->header();
-  echo '<h3>' . $lang->get('user_logout_err_title') . '</h3>';
-  echo '<p>' . $l . '</p>';
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	if ( !$session->user_logged_in )
+		$paths->main_page();
+	
+	$token = $paths->getParam(0);
+	if ( $token !== $session->csrf_token )
+	{
+		csrf_request_confirm();
+	}
+	
+	$l = $session->logout();
+	if ( $l == 'success' )
+	{
+		$url = makeUrl(get_main_page(), false, true);
+		if ( $paths->getParam(1) )
+		{
+			$pi = explode('/', $paths->getAllParams());
+			$pi = implode('/', array_values(array_slice($pi, 1)));
+			list($pid, $ns) = RenderMan::strToPageID($pi);
+			$perms = $session->fetch_page_acl($pid, $ns);
+			if ( $perms->get_permissions('read') )
+			{
+				$url = makeUrl($pi, false, true);
+			}
+		}
+		redirect($url, $lang->get('user_logout_success_title'), $lang->get('user_logout_success_body'), 3);
+	}
+	$template->header();
+	echo '<h3>' . $lang->get('user_logout_err_title') . '</h3>';
+	echo '<p>' . $l . '</p>';
+	$template->footer();
 }
 
 function page_Special_Register()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  if ( $session->user_level < USER_LEVEL_ADMIN && $session->user_logged_in )
-  {
-    $paths->main_page();
-  }
-  
-  // form field trackers
-  $username = '';
-  $email = '';
-  $realname = '';
-  
-  $terms = getConfig('register_tou');
-  
-  if(getConfig('account_activation') == 'disable' && ( ( $session->user_level >= USER_LEVEL_ADMIN && !isset($_GET['IWannaPlayToo']) ) || $session->user_level < USER_LEVEL_ADMIN || !$session->user_logged_in ))
-  {
-    $s = ($session->user_level >= USER_LEVEL_ADMIN) ? '<p>' . $lang->get('user_reg_err_disabled_body_adminblurb', array( 'reg_link' => makeUrl($paths->page, 'IWannaPlayToo&coppa=no', true) )) . '</p>' : '';
-    die_friendly($lang->get('user_reg_err_disabled_title'), '<p>' . $lang->get('user_reg_err_disabled_body') . '</p>' . $s);
-  }
-  // are we locked out from logging in? if so, also lock out registration
-  if ( getConfig('lockout_policy') === 'lockout' )
-  {
-    $ip = $db->escape($_SERVER['REMOTE_ADDR']);
-    $threshold = time() - ( 60 * intval(getConfig('lockout_duration')) );
-    $limit = intval(getConfig('lockout_threshold'));
-    $q = $db->sql_query('SELECT * FROM ' . table_prefix . "lockout WHERE timestamp >= $threshold ORDER BY timestamp DESC;");
-    if ( !$q )
-      $db->_die();
-    if ( $db->numrows() >= $limit )
-    {
-      $row = $db->fetchrow();
-      $db->free_result();
-      $time_rem = intval(getConfig('lockout_duration')) - round((time() - $row['timestamp']) / 60);
-      die_friendly($lang->get('user_reg_err_disabled_title'), '<p>' . $lang->get('user_reg_err_locked_out', array('time' => $time_rem)) . '</p>');
-    }
-    $db->free_result();
-  }
-  if(isset($_POST['submit'])) 
-  {
-    $_GET['coppa'] = ( isset($_POST['coppa']) ) ? $_POST['coppa'] : 'x';
-    
-    $captcharesult = $session->get_captcha($_POST['captchahash']);
-    $session->kill_captcha();
-    // bypass captcha if logged in (at this point, if logged in, we're admin)
-    if ( !$session->user_logged_in && strtolower($captcharesult) != strtolower($_POST['captchacode']) )
-    {
-      $s = $lang->get('user_reg_err_captcha');
-    }
-    else
-    {
-      if ( getConfig('enable_coppa') == '1' && ( !isset($_POST['coppa']) || ( isset($_POST['coppa']) && !in_array($_POST['coppa'], array('yes', 'no')) ) ) )
-      {
-        $s = 'Invalid COPPA input';
-      }
-      else if ( !$session->user_logged_in && !empty($terms) && !isset($_POST['tou_agreed']) )
-      {
-        $s = $lang->get('user_reg_err_accept_tou');
-      }
-      else
-      {
-        $coppa = ( isset($_POST['coppa']) && $_POST['coppa'] == 'yes' );
-        $s = false;
-        
-        // decrypt password
-        // as with the change pass form, we aren't going to bother checking the confirmation code because if the passwords didn't match
-        // and yet the password got encrypted, that means the user screwed with the code, and if the user screwed with the code and thus
-        // forgot his password, that's his problem.
-        
-        if ( $_POST['use_crypt'] == 'yes' )
-        {
-          $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-          $crypt_key = $session->fetch_public_key($_POST['crypt_key']);
-          if ( !$crypt_key )
-          {
-            $s = $lang->get('user_reg_err_missing_key');
-          }
-          else
-          {
-            $data = $_POST['crypt_data'];
-            $bin_key = hexdecode($crypt_key);
-            //die("Decrypting with params: key $crypt_key, data $data");
-            $password = $aes->decrypt($data, $bin_key, ENC_HEX);
-          }
-        }
-        else
-        {
-          $password = $_POST['password'];
-        }
-        
-        $error =& $s;
-        
-        /**
-         * Validation of POST data coming from registration. Put an error message in the variable $error to stop registration.
-         * @hook ucp_register_validate
-         */
-        
-        $code = $plugins->setHook('ucp_register_validate');
-        foreach ( $code as $cmd )
-        {
-          eval($cmd);
-        }
-        
-        // All things verified, create account
-        if ( !$s )
-          $s = $session->create_user($_POST['username'], $password, $_POST['email'], $_POST['real_name'], $coppa);
-      }
-    }
-    if($s == 'success' && !$coppa)
-    {
-      switch(getConfig('account_activation'))
-      {
-        case "none":
-        default:
-          $str = $lang->get('user_reg_msg_success_activ_none', array('login_link' => makeUrlNS('Special', 'Login', false, true)));
-          break;
-        case "user":
-          $str = $lang->get('user_reg_msg_success_activ_user');
-          break;
-        case "admin":
-          $str = $lang->get('user_reg_msg_success_activ_admin');
-          break;
-      }
-      die_friendly($lang->get('user_reg_msg_success_title'), '<p>' . $lang->get('user_reg_msg_success_body') . ' ' . $str . '</p>');
-    }
-    else if ( $s == 'success' && $coppa )
-    {
-      $str = $lang->get('user_reg_msg_success_activ_coppa');
-      die_friendly($lang->get('user_reg_msg_success_title'), '<p>' . $lang->get('user_reg_msg_success_body') . ' ' . $str . '</p>');
-    }
-    $username = htmlspecialchars($_POST['username']);
-    $email    = htmlspecialchars($_POST['email']);
-    $realname = htmlspecialchars($_POST['real_name']);
-  }
-  $template->header();
-  echo $lang->get('user_reg_msg_greatercontrol');
-  
-  if ( getConfig('enable_coppa') != '1' || ( isset($_GET['coppa']) && in_array($_GET['coppa'], array('yes', 'no')) ) )
-  {
-    $coppa = ( isset($_GET['coppa']) && $_GET['coppa'] == 'yes' );
-    $session->kill_captcha();
-    $captchacode = $session->make_captcha();
-    
-    $pubkey = $session->rijndael_genkey();
-    $challenge = $session->dss_rand();
-    
-    ?>
-      <h3><?php echo $lang->get('user_reg_msg_table_title'); ?></h3>
-      <form name="regform" action="<?php echo makeUrl($paths->page); ?>" method="post" onsubmit="return runEncryption();">
-        <div class="tblholder">
-          <table border="0" width="100%" cellspacing="1" cellpadding="4">
-            <tr><th colspan="3"><?php echo $lang->get('user_reg_msg_table_subtitle'); ?></th></tr>
-            
-            <?php if(isset($_POST['submit'])) echo '<tr><td colspan="3" class="row2" style="color: red;">'.$s.'</td></tr>'; ?>
-            
-            <!-- FIELD: Username -->
-            <tr>
-              <td class="row1" style="width: 50%;">
-                <?php echo $lang->get('user_reg_lbl_field_username'); ?>
-                <span id="e_username"></span>
-              </td>
-              <td class="row1" style="width: 50%;">
-                <input tabindex="1" type="text" name="username" size="30" value="<?php echo $username; ?>" onkeyup="namegood = false; validateForm(this);" onblur="checkUsername();" />
-              </td>
-              <td class="row1" style="width: 1px;">
-                <img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/checkbad.png" id="s_username" />
-              </td>
-            </tr>
-            
-            <!-- FIELD: Password -->
-            <tr>
-              <td class="row3" style="width: 50%;" rowspan="<?php echo ( getConfig('pw_strength_enable') == '1' ) ? '3' : '2'; ?>">
-                <?php echo $lang->get('user_reg_lbl_field_password'); ?>
-                <span id="e_password"></span>
-                <?php if ( getConfig('pw_strength_enable') == '1' && getConfig('pw_strength_minimum') > -10 ): ?>
-                <small><?php echo $lang->get('user_reg_msg_password_score'); ?></small>
-                <?php endif; ?>
-              </td>
-              <td class="row3" style="width: 50%;">
-                <input tabindex="2" type="password" name="password" size="15" onkeyup="<?php if ( getConfig('pw_strength_enable') == '1' ): ?>password_score_field(this); <?php endif; ?>validateForm(this);" /><?php if ( getConfig('pw_strength_enable') == '1' ): ?><span class="password-checker" style="font-weight: bold; color: #aaaaaa;"> Loading...</span><?php endif; ?>
-              </td>
-              <td rowspan="<?php echo ( getConfig('pw_strength_enable') == '1' ) ? '3' : '2'; ?>" class="row3" style="max-width: 24px;">
-                <img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/checkbad.png" id="s_password" />
-              </td>
-            </tr>
-            
-            <!-- FIELD: Password confirmation -->
-            <tr>
-              <td class="row3" style="width: 50%;">
-                <input tabindex="3" type="password" name="password_confirm" size="15" onkeyup="validateForm(this);" /> <small><?php echo $lang->get('user_reg_lbl_field_password_confirm'); ?></small>
-              </td>
-            </tr>
-            
-            <!-- FIELD: Password strength meter -->
-            
-            <?php if ( getConfig('pw_strength_enable') == '1' ): ?>
-            <tr>
-              <td class="row3" style="width: 50%;">
-                <div id="pwmeter"></div>
-              </td>
-            </tr>
-            <?php endif; ?>
-            
-            <!-- FIELD: E-mail address -->
-            <tr>
-              <td class="row1" style="width: 50%;">
-                <?php
-                  if ( $coppa )
-                  {
-                    echo $lang->get('user_reg_lbl_field_email_coppa');
-                  }
-                  else
-                  {
-                    echo $lang->get('user_reg_lbl_field_email');
-                  }
-                ?>
-                <?php
-                  if ( ( $x = getConfig('account_activation') ) == 'user' )
-                  {
-                    echo '<br /><small>' . $lang->get('user_reg_msg_email_activuser') . '</small>';
-                  }
-                ?>
-              </td>
-              <td class="row1" style="width: 50%;">
-                <input tabindex="4" type="text" name="email" size="30" value="<?php echo $email; ?>" onkeyup="validateForm(this);" />
-              </td>
-              <td class="row1" style="max-width: 24px;">
-                <img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/checkbad.png" id="s_email" />
-              </td>
-            </tr>
-            
-            <!-- FIELD: Real name -->
-            <tr>
-              <td class="row3" style="width: 50%;">
-                <?php echo $lang->get('user_reg_lbl_field_realname'); ?><br />
-                <small><?php echo $lang->get('user_reg_msg_realname_optional'); ?></small>
-              </td>
-              <td class="row3" style="width: 50%;">
-                <input tabindex="5" type="text" name="real_name" size="30" value="<?php echo $realname; ?>" />
-              </td>
-              <td class="row3" style="max-width: 24px;">
-              </td>
-            </tr>
-            
-            <?php
-            /**
-             * Allows adding fields to the user registration form. Form is built with Enano tables, 3 columns. (Rightmost can be left empty or if you're using Javascript validation an image you can update with your own Javascript code)
-             * @hook ucp_register_form
-             */
-            
-            $code = $plugins->setHook('ucp_register_form');
-            foreach ( $code as $cmd )
-            {
-              eval($cmd);
-            }
-            ?>
-            
-            <!-- FIELD: CAPTCHA image -->
-            <?php
-            if ( !$session->user_logged_in ):
-            ?>
-            <tr>
-              <td class="row1" style="width: 50%;" rowspan="2">
-                <?php echo $lang->get('user_reg_lbl_field_captcha'); ?><br />
-                <small>
-                  <?php echo $lang->get('user_reg_msg_captcha_pleaseenter', array('regen_flags' => 'href="#" onclick="regenCaptcha(); return false;"')); ?><br />
-                  <br />
-                  <?php echo $lang->get('user_reg_msg_captcha_blind'); ?>
-                </small>
-              </td>
-              <td class="row1">
-                <img id="captchaimg" alt="CAPTCHA image" src="<?php echo makeUrlNS('Special', 'Captcha/'.$captchacode); ?>" style="cursor: pointer;" onclick="regenCaptcha(); return false;" />
-              </td>
-              <td class="row1">
-                <img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/checkbad.png" id="s_captcha" />
-              </td>
-            </tr>
-            
-            <!-- FIELD: CAPTCHA input field -->
-            <tr>
-              <td class="row1" colspan="2">
-                <?php echo $lang->get('user_reg_lbl_field_captcha_code'); ?>
-                <input tabindex="6" name="captchacode" type="text" size="10" onkeyup="validateCaptcha(this);" />
-                <img id="captchaajax" width="16" height="16" src="<?php echo cdnPath; ?>/images/spacer.gif" />
-                <input type="hidden" name="captchahash" value="<?php echo $captchacode; ?>" />
-              </td>
-            </tr>
-            
-            <!-- FIELD: TOU -->
-            
-            <?php
-            if ( !empty($terms) ):
-            ?>
-            
-            <tr>
-              <td class="row1" colspan="3">
-                <?php
-                echo $lang->get('user_reg_msg_please_read_tou');
-                ?>
-              </td>
-            </tr>
-            
-            <tr>
-              <td class="row3" colspan="3">
-                <div style="border: 1px solid #000000; height: 75px; width: 60%; clip: rect(0px,auto,auto,0px); overflow: auto; background-color: #FFF; margin: 0 auto; padding: 4px;">
-                  <?php
-                  echo RenderMan::render($terms);
-                  ?>
-                </div>
-                <p style="text-align: center;">
-                  <label>
-                    <input tabindex="7" type="checkbox" name="tou_agreed" />
-                    <b><?php echo $lang->get('user_reg_lbl_field_tou'); ?></b>
-                  </label>
-                </p>
-              </td>
-            </tr>
-            
-            <?php
-            endif; // !empty($terms)
-            endif; // $session->user_logged_in
-            ?>
-            
-            <!-- FIELD: submit button -->
-            <tr>
-              <th class="subhead" colspan="3" style="text-align: center;">
-                <input tabindex="8" type="submit" name="submit" value="<?php echo $lang->get('user_reg_btn_create_account'); ?>" />
-              </td>
-            </tr>
-            
-          </table>
-        </div>
-        <?php
-          $val = ( $coppa ) ? 'yes' : 'no';
-          echo '<input type="hidden" name="coppa" value="' . $val . '" />';
-        ?>
-        <input type="hidden" name="challenge_data" value="<?php echo $challenge; ?>" />
-        <input type="hidden" name="use_crypt" value="no" />
-        <input type="hidden" name="crypt_key" value="<?php echo $pubkey; ?>" />
-        <input type="hidden" name="crypt_data" value="" />
-      <script type="text/javascript">
-        // ENCRYPTION CODE
-        function runEncryption()
-        {
-          var frm = document.forms.regform;
-          if ( frm.password.value.length < 1 )
-            return true;
-          pass1 = frm.password.value;
-          pass2 = frm.password_confirm.value;
-          if ( pass1 != pass2 )
-          {
-            alert($lang.get('user_reg_err_alert_password_nomatch'));
-            return false;
-          }
-          if ( pass1.length < 6 && pass1.length > 0 )
-          {
-            alert($lang.get('user_reg_err_alert_password_tooshort'));
-            return false;
-          }
-          if(aes_self_test())
-          {
-            frm.use_crypt.value = 'yes';
-            var cryptkey = frm.crypt_key.value;
-            frm.crypt_key.value = hex_md5(cryptkey);
-            cryptkey = hexToByteArray(cryptkey);
-            if(!cryptkey || ( ( typeof cryptkey == 'string' || typeof cryptkey == 'object' ) ) && cryptkey.length != keySizeInBits / 8 )
-            {
-              frm.submit.disabled = true;
-              len = ( typeof cryptkey == 'string' || typeof cryptkey == 'object' ) ? '\nLen: '+cryptkey.length : '';
-              alert('The key is messed up\nType: '+typeof(cryptkey)+len);
-            }
-            pass = frm.password.value;
-            pass = stringToByteArray(pass);
-            cryptstring = rijndaelEncrypt(pass, cryptkey, 'ECB');
-            if(!cryptstring)
-            {
-              return false;
-            }
-            cryptstring = byteArrayToHex(cryptstring);
-            frm.crypt_data.value = cryptstring;
-            frm.password.value = "";
-            frm.password_confirm.value = "";
-          }
-          return true;
-        }
-        </script>
-      </form>
-      <!-- Don't optimize this script, it fails when compressed -->
-      <enano:no-opt>
-        <script type="text/javascript">
-          // <![CDATA[
-          var namegood = false;
-          function validateForm(field)
-          {
-            if ( typeof(field) != 'object' )
-            {
-              field = {
-                name: '_nil',
-                value: '_nil'
-              }
-            }
-            // wait until $lang is initted
-            if ( typeof($lang) != 'object' )
-            {
-              setTimeout('validateForm();', 200);
-              return false;
-            }
-            var frm = document.forms.regform;
-            failed = false;
-            
-            // Username
-            if(!namegood && ( field.name == 'username' || field.name == '_nil' ) ) 
-            {
-              //if(frm.username.value.match(/^([A-z0-9 \!@\-\(\)]+){2,}$/ig))
-              var regex = new RegExp('^([^<>&\?]+){2,}$', 'ig');
-              if ( frm.username.value.match(regex) )
-              {
-                document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkunk.png';
-                document.getElementById('e_username').innerHTML = '&nbsp;';
-              } else {
-                failed = true;
-                document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkbad.png';
-                document.getElementById('e_username').innerHTML = '<br /><small>' + $lang.get('user_reg_err_username_invalid') + '</small>';
-              }
-            }
-            if ( document.getElementById('b_username') )
-            {
-              document.getElementById('b_username').innerHTML = '';
-              if(hex_md5(frm.real_name.value) == '5a397df72678128cf0e8147a2befd5f1')
-              {
-                document.getElementById('b_username').innerHTML = '<br /><br />Hey...I know you!<br /><img alt="" src="http://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Bill_Gates_2004_cr.jpg/220px-Bill_Gates_2004_cr.jpg" />';
-              }
-            }
-            
-            // Password
-            if ( field.name == 'password' || field.name == 'password_confirm' || field.name == '_nil' )
-            {
-              if(frm.password.value.match(/^(.+){6,}$/ig) && frm.password_confirm.value.match(/^(.+){6,}$/ig) && frm.password.value == frm.password_confirm.value )
-              {
-                document.getElementById('s_password').src='<?php echo scriptPath; ?>/images/check.png';
-                document.getElementById('e_password').innerHTML = '<br /><small>' + $lang.get('user_reg_err_password_good') + '</small>';
-              } else {
-                failed = true;
-                if(frm.password.value.length < 6)
-                {
-                  document.getElementById('e_password').innerHTML = '<br /><small>' + $lang.get('user_reg_msg_password_length') + '</small>';
-                }
-                else if(frm.password.value != frm.password_confirm.value)
-                {
-                  document.getElementById('e_password').innerHTML = '<br /><small>' + $lang.get('user_reg_msg_password_needmatch') + '</small>';
-                }
-                else
-                {
-                  document.getElementById('e_password').innerHTML = '';
-                }
-                document.getElementById('s_password').src='<?php echo scriptPath; ?>/images/checkbad.png';
-              }
-            }
-            
-            // E-mail address
-            
-            // workaround for idiot jEdit bug
-            if ( validateEmail(frm.email.value) && ( field.name == 'email' || field.name == '_nil' ) )
-            {
-              document.getElementById('s_email').src='<?php echo scriptPath; ?>/images/check.png';
-            } else {
-              failed = true;
-              document.getElementById('s_email').src='<?php echo scriptPath; ?>/images/checkbad.png';
-            }
-            if(failed)
-            {
-              frm.submit.disabled = 'disabled';
-            } else {
-              frm.submit.disabled = false;
-            }
-          }
-          function checkUsername()
-          {
-            var frm = document.forms.regform;
-            
-            if(!namegood)
-            {
-              var regex = new RegExp('^([^<>&\?]+){2,}$', 'ig');
-              if ( frm.username.value.match(regex) )
-              {
-                document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkunk.png';
-                document.getElementById('e_username').innerHTML = '&nbsp;';
-              } else {
-                document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkbad.png';
-                document.getElementById('e_username').innerHTML = '<br /><small>' + $lang.get('user_reg_err_username_invalid') + '</small>';
-                return false;
-              }
-            }
-            
-            document.getElementById('e_username').innerHTML = '<br /><small><b>' + $lang.get('user_reg_msg_username_checking') + '</b></small>';
-            ajaxGet('<?php echo scriptPath; ?>/ajax.php?title=null&_mode=checkusername&name='+escape(frm.username.value), function() {
-              if ( ajax.readyState == 4 && ajax.status == 200 )
-                if(ajax.responseText == 'good')
-                {
-                  document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/check.png';
-                  document.getElementById('e_username').innerHTML = '<br /><small><b>' + $lang.get('user_reg_msg_username_available') + '</b></small>';
-                  namegood = true;
-                } else if(ajax.responseText == 'bad') {
-                  document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkbad.png';
-                  document.getElementById('e_username').innerHTML = '<br /><small><b>' + $lang.get('user_reg_msg_username_unavailable') + '</b></small>';
-                  namegood = false;
-                } else {
-                  document.getElementById('e_username').innerHTML = ajax.responseText;
-                }
-            });
-          }
-          function regenCaptcha()
-          {
-            var frm = document.forms.regform;
-            document.getElementById('captchaimg').src = '<?php echo makeUrlNS("Special", "Captcha/$captchacode"); ?>/'+Math.floor(Math.random() * 100000);
-            frm.captchacode.value = '';
-            return false;
-          }
-          function validateCaptcha(input)
-          {
-            var frm = document.forms.regform;
-            if ( input.value.length < 7 )
-            {
-              return false;
-            }
-            var valid_field = document.getElementById('s_captcha');
-            var loader_img = document.getElementById('captchaajax');
-            loader_img.src = cdnPath + '/images/loading.gif';
-            ajaxGet(makeUrlNS('Special', 'Captcha/' + frm.captchahash.value + '/validate=' + input.value), function(ajax)
-              {
-                if ( ajax.readyState == 4 && ajax.status == 200 )
-                {
-                  var response = String(ajax.responseText + '');
-                  if ( !check_json_response(response) )
-                  {
-                    handle_invalid_json(response);
-                    return false;
-                  }
-                  response = parseJSON(response);
-                  if ( response.valid )
-                  {
-                    loader_img.src = cdnPath + '/images/spacer.gif';
-                    valid_field.src = cdnPath + '/images/check.png';
-                  }
-                  else
-                  {
-                    valid_field.src = cdnPath + '/images/checkbad.png';
-                    regenCaptcha();
-                    document.getElementById('captchaimg').onload = function()
-                    {
-                      document.getElementById('captchaajax').src = cdnPath + '/images/spacer.gif';
-                      input.focus();
-                    };
-                    input.value = '';
-                  }
-                }
-              });
-          }
-          addOnloadHook(function()
-            {
-              <?php if ( getConfig('pw_strength_enable') == '1' ): ?>
-              var frm = document.forms.regform;
-              load_component('pwstrength');
-              password_score_field(frm.password);
-              <?php endif; ?>
-              load_component('crypto');
-              validateForm();
-              setTimeout('checkUsername();', 1000);
-            });
-          // ]]>
-        </script>
-      </enano:no-opt>
-    <?php
-  }
-  else
-  {
-    $year = intval( enano_date('Y') );
-    $year = $year - 13;
-    $month = enano_date('F');
-    $day = enano_date('d');
-    
-    $yo13_date = "$month $day, $year";
-    $link_coppa_yes = makeUrlNS('Special', 'Register', 'coppa=yes', true);
-    $link_coppa_no  = makeUrlNS('Special', 'Register', 'coppa=no',  true);
-    
-    // COPPA enabled, ask age
-    echo '<div class="tblholder">';
-    echo '<table border="0" cellspacing="1" cellpadding="4">';
-    echo '<tr>
-            <td class="row1">
-              ' . $lang->get('user_reg_coppa_title') . '
-            </td>
-          </tr>
-          <tr>
-            <td class="row3">
-              <a href="' . $link_coppa_no  . '">' . $lang->get('user_reg_coppa_link_atleast13', array( 'yo13_date' => $yo13_date )) . '</a><br />
-              <a href="' . $link_coppa_yes . '">' . $lang->get('user_reg_coppa_link_not13', array( 'yo13_date' => $yo13_date )) . '</a>
-            </td>
-          </tr>';
-    echo '</table>';
-    echo '</div>';
-  }
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	if ( $session->user_level < USER_LEVEL_ADMIN && $session->user_logged_in )
+	{
+		$paths->main_page();
+	}
+	
+	// form field trackers
+	$username = '';
+	$email = '';
+	$realname = '';
+	
+	$terms = getConfig('register_tou');
+	
+	if(getConfig('account_activation') == 'disable' && ( ( $session->user_level >= USER_LEVEL_ADMIN && !isset($_GET['IWannaPlayToo']) ) || $session->user_level < USER_LEVEL_ADMIN || !$session->user_logged_in ))
+	{
+		$s = ($session->user_level >= USER_LEVEL_ADMIN) ? '<p>' . $lang->get('user_reg_err_disabled_body_adminblurb', array( 'reg_link' => makeUrl($paths->page, 'IWannaPlayToo&coppa=no', true) )) . '</p>' : '';
+		die_friendly($lang->get('user_reg_err_disabled_title'), '<p>' . $lang->get('user_reg_err_disabled_body') . '</p>' . $s);
+	}
+	// are we locked out from logging in? if so, also lock out registration
+	if ( getConfig('lockout_policy') === 'lockout' )
+	{
+		$ip = $db->escape($_SERVER['REMOTE_ADDR']);
+		$threshold = time() - ( 60 * intval(getConfig('lockout_duration')) );
+		$limit = intval(getConfig('lockout_threshold'));
+		$q = $db->sql_query('SELECT * FROM ' . table_prefix . "lockout WHERE timestamp >= $threshold ORDER BY timestamp DESC;");
+		if ( !$q )
+			$db->_die();
+		if ( $db->numrows() >= $limit )
+		{
+			$row = $db->fetchrow();
+			$db->free_result();
+			$time_rem = intval(getConfig('lockout_duration')) - round((time() - $row['timestamp']) / 60);
+			die_friendly($lang->get('user_reg_err_disabled_title'), '<p>' . $lang->get('user_reg_err_locked_out', array('time' => $time_rem)) . '</p>');
+		}
+		$db->free_result();
+	}
+	if(isset($_POST['submit'])) 
+	{
+		$_GET['coppa'] = ( isset($_POST['coppa']) ) ? $_POST['coppa'] : 'x';
+		
+		$captcharesult = $session->get_captcha($_POST['captchahash']);
+		$session->kill_captcha();
+		// bypass captcha if logged in (at this point, if logged in, we're admin)
+		if ( !$session->user_logged_in && strtolower($captcharesult) != strtolower($_POST['captchacode']) )
+		{
+			$s = $lang->get('user_reg_err_captcha');
+		}
+		else
+		{
+			if ( getConfig('enable_coppa') == '1' && ( !isset($_POST['coppa']) || ( isset($_POST['coppa']) && !in_array($_POST['coppa'], array('yes', 'no')) ) ) )
+			{
+				$s = 'Invalid COPPA input';
+			}
+			else if ( !$session->user_logged_in && !empty($terms) && !isset($_POST['tou_agreed']) )
+			{
+				$s = $lang->get('user_reg_err_accept_tou');
+			}
+			else
+			{
+				$coppa = ( isset($_POST['coppa']) && $_POST['coppa'] == 'yes' );
+				$s = false;
+				
+				// decrypt password
+				// as with the change pass form, we aren't going to bother checking the confirmation code because if the passwords didn't match
+				// and yet the password got encrypted, that means the user screwed with the code, and if the user screwed with the code and thus
+				// forgot his password, that's his problem.
+				
+				if ( $_POST['use_crypt'] == 'yes' )
+				{
+					$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+					$crypt_key = $session->fetch_public_key($_POST['crypt_key']);
+					if ( !$crypt_key )
+					{
+						$s = $lang->get('user_reg_err_missing_key');
+					}
+					else
+					{
+						$data = $_POST['crypt_data'];
+						$bin_key = hexdecode($crypt_key);
+						//die("Decrypting with params: key $crypt_key, data $data");
+						$password = $aes->decrypt($data, $bin_key, ENC_HEX);
+					}
+				}
+				else
+				{
+					$password = $_POST['password'];
+				}
+				
+				$error =& $s;
+				
+				/**
+ 				* Validation of POST data coming from registration. Put an error message in the variable $error to stop registration.
+ 				* @hook ucp_register_validate
+ 				*/
+				
+				$code = $plugins->setHook('ucp_register_validate');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				
+				// All things verified, create account
+				if ( !$s )
+					$s = $session->create_user($_POST['username'], $password, $_POST['email'], $_POST['real_name'], $coppa);
+			}
+		}
+		if($s == 'success' && !$coppa)
+		{
+			switch(getConfig('account_activation'))
+			{
+				case "none":
+				default:
+					$str = $lang->get('user_reg_msg_success_activ_none', array('login_link' => makeUrlNS('Special', 'Login', false, true)));
+					break;
+				case "user":
+					$str = $lang->get('user_reg_msg_success_activ_user');
+					break;
+				case "admin":
+					$str = $lang->get('user_reg_msg_success_activ_admin');
+					break;
+			}
+			die_friendly($lang->get('user_reg_msg_success_title'), '<p>' . $lang->get('user_reg_msg_success_body') . ' ' . $str . '</p>');
+		}
+		else if ( $s == 'success' && $coppa )
+		{
+			$str = $lang->get('user_reg_msg_success_activ_coppa');
+			die_friendly($lang->get('user_reg_msg_success_title'), '<p>' . $lang->get('user_reg_msg_success_body') . ' ' . $str . '</p>');
+		}
+		$username = htmlspecialchars($_POST['username']);
+		$email    = htmlspecialchars($_POST['email']);
+		$realname = htmlspecialchars($_POST['real_name']);
+	}
+	$template->header();
+	echo $lang->get('user_reg_msg_greatercontrol');
+	
+	if ( getConfig('enable_coppa') != '1' || ( isset($_GET['coppa']) && in_array($_GET['coppa'], array('yes', 'no')) ) )
+	{
+		$coppa = ( isset($_GET['coppa']) && $_GET['coppa'] == 'yes' );
+		$session->kill_captcha();
+		$captchacode = $session->make_captcha();
+		
+		$pubkey = $session->rijndael_genkey();
+		$challenge = $session->dss_rand();
+		
+		?>
+			<h3><?php echo $lang->get('user_reg_msg_table_title'); ?></h3>
+			<form name="regform" action="<?php echo makeUrl($paths->page); ?>" method="post" onsubmit="return runEncryption();">
+				<div class="tblholder">
+					<table border="0" width="100%" cellspacing="1" cellpadding="4">
+						<tr><th colspan="3"><?php echo $lang->get('user_reg_msg_table_subtitle'); ?></th></tr>
+						
+						<?php if(isset($_POST['submit'])) echo '<tr><td colspan="3" class="row2" style="color: red;">'.$s.'</td></tr>'; ?>
+						
+						<!-- FIELD: Username -->
+						<tr>
+							<td class="row1" style="width: 50%;">
+								<?php echo $lang->get('user_reg_lbl_field_username'); ?>
+								<span id="e_username"></span>
+							</td>
+							<td class="row1" style="width: 50%;">
+								<input tabindex="1" type="text" name="username" size="30" value="<?php echo $username; ?>" onkeyup="namegood = false; validateForm(this);" onblur="checkUsername();" />
+							</td>
+							<td class="row1" style="width: 1px;">
+								<img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/checkbad.png" id="s_username" />
+							</td>
+						</tr>
+						
+						<!-- FIELD: Password -->
+						<tr>
+							<td class="row3" style="width: 50%;" rowspan="<?php echo ( getConfig('pw_strength_enable') == '1' ) ? '3' : '2'; ?>">
+								<?php echo $lang->get('user_reg_lbl_field_password'); ?>
+								<span id="e_password"></span>
+								<?php if ( getConfig('pw_strength_enable') == '1' && getConfig('pw_strength_minimum') > -10 ): ?>
+								<small><?php echo $lang->get('user_reg_msg_password_score'); ?></small>
+								<?php endif; ?>
+							</td>
+							<td class="row3" style="width: 50%;">
+								<input tabindex="2" type="password" name="password" size="15" onkeyup="<?php if ( getConfig('pw_strength_enable') == '1' ): ?>password_score_field(this); <?php endif; ?>validateForm(this);" /><?php if ( getConfig('pw_strength_enable') == '1' ): ?><span class="password-checker" style="font-weight: bold; color: #aaaaaa;"> Loading...</span><?php endif; ?>
+							</td>
+							<td rowspan="<?php echo ( getConfig('pw_strength_enable') == '1' ) ? '3' : '2'; ?>" class="row3" style="max-width: 24px;">
+								<img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/checkbad.png" id="s_password" />
+							</td>
+						</tr>
+						
+						<!-- FIELD: Password confirmation -->
+						<tr>
+							<td class="row3" style="width: 50%;">
+								<input tabindex="3" type="password" name="password_confirm" size="15" onkeyup="validateForm(this);" /> <small><?php echo $lang->get('user_reg_lbl_field_password_confirm'); ?></small>
+							</td>
+						</tr>
+						
+						<!-- FIELD: Password strength meter -->
+						
+						<?php if ( getConfig('pw_strength_enable') == '1' ): ?>
+						<tr>
+							<td class="row3" style="width: 50%;">
+								<div id="pwmeter"></div>
+							</td>
+						</tr>
+						<?php endif; ?>
+						
+						<!-- FIELD: E-mail address -->
+						<tr>
+							<td class="row1" style="width: 50%;">
+								<?php
+									if ( $coppa )
+									{
+										echo $lang->get('user_reg_lbl_field_email_coppa');
+									}
+									else
+									{
+										echo $lang->get('user_reg_lbl_field_email');
+									}
+								?>
+								<?php
+									if ( ( $x = getConfig('account_activation') ) == 'user' )
+									{
+										echo '<br /><small>' . $lang->get('user_reg_msg_email_activuser') . '</small>';
+									}
+								?>
+							</td>
+							<td class="row1" style="width: 50%;">
+								<input tabindex="4" type="text" name="email" size="30" value="<?php echo $email; ?>" onkeyup="validateForm(this);" />
+							</td>
+							<td class="row1" style="max-width: 24px;">
+								<img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/checkbad.png" id="s_email" />
+							</td>
+						</tr>
+						
+						<!-- FIELD: Real name -->
+						<tr>
+							<td class="row3" style="width: 50%;">
+								<?php echo $lang->get('user_reg_lbl_field_realname'); ?><br />
+								<small><?php echo $lang->get('user_reg_msg_realname_optional'); ?></small>
+							</td>
+							<td class="row3" style="width: 50%;">
+								<input tabindex="5" type="text" name="real_name" size="30" value="<?php echo $realname; ?>" />
+							</td>
+							<td class="row3" style="max-width: 24px;">
+							</td>
+						</tr>
+						
+						<?php
+						/**
+ 						* Allows adding fields to the user registration form. Form is built with Enano tables, 3 columns. (Rightmost can be left empty or if you're using Javascript validation an image you can update with your own Javascript code)
+ 						* @hook ucp_register_form
+ 						*/
+						
+						$code = $plugins->setHook('ucp_register_form');
+						foreach ( $code as $cmd )
+						{
+							eval($cmd);
+						}
+						?>
+						
+						<!-- FIELD: CAPTCHA image -->
+						<?php
+						if ( !$session->user_logged_in ):
+						?>
+						<tr>
+							<td class="row1" style="width: 50%;" rowspan="2">
+								<?php echo $lang->get('user_reg_lbl_field_captcha'); ?><br />
+								<small>
+									<?php echo $lang->get('user_reg_msg_captcha_pleaseenter', array('regen_flags' => 'href="#" onclick="regenCaptcha(); return false;"')); ?><br />
+									<br />
+									<?php echo $lang->get('user_reg_msg_captcha_blind'); ?>
+								</small>
+							</td>
+							<td class="row1">
+								<img id="captchaimg" alt="CAPTCHA image" src="<?php echo makeUrlNS('Special', 'Captcha/'.$captchacode); ?>" style="cursor: pointer;" onclick="regenCaptcha(); return false;" />
+							</td>
+							<td class="row1">
+								<img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/checkbad.png" id="s_captcha" />
+							</td>
+						</tr>
+						
+						<!-- FIELD: CAPTCHA input field -->
+						<tr>
+							<td class="row1" colspan="2">
+								<?php echo $lang->get('user_reg_lbl_field_captcha_code'); ?>
+								<input tabindex="6" name="captchacode" type="text" size="10" onkeyup="validateCaptcha(this);" />
+								<img id="captchaajax" width="16" height="16" src="<?php echo cdnPath; ?>/images/spacer.gif" />
+								<input type="hidden" name="captchahash" value="<?php echo $captchacode; ?>" />
+							</td>
+						</tr>
+						
+						<!-- FIELD: TOU -->
+						
+						<?php
+						if ( !empty($terms) ):
+						?>
+						
+						<tr>
+							<td class="row1" colspan="3">
+								<?php
+								echo $lang->get('user_reg_msg_please_read_tou');
+								?>
+							</td>
+						</tr>
+						
+						<tr>
+							<td class="row3" colspan="3">
+								<div style="border: 1px solid #000000; height: 75px; width: 60%; clip: rect(0px,auto,auto,0px); overflow: auto; background-color: #FFF; margin: 0 auto; padding: 4px;">
+									<?php
+									echo RenderMan::render($terms);
+									?>
+								</div>
+								<p style="text-align: center;">
+									<label>
+										<input tabindex="7" type="checkbox" name="tou_agreed" />
+										<b><?php echo $lang->get('user_reg_lbl_field_tou'); ?></b>
+									</label>
+								</p>
+							</td>
+						</tr>
+						
+						<?php
+						endif; // !empty($terms)
+						endif; // $session->user_logged_in
+						?>
+						
+						<!-- FIELD: submit button -->
+						<tr>
+							<th class="subhead" colspan="3" style="text-align: center;">
+								<input tabindex="8" type="submit" name="submit" value="<?php echo $lang->get('user_reg_btn_create_account'); ?>" />
+							</td>
+						</tr>
+						
+					</table>
+				</div>
+				<?php
+					$val = ( $coppa ) ? 'yes' : 'no';
+					echo '<input type="hidden" name="coppa" value="' . $val . '" />';
+				?>
+				<input type="hidden" name="challenge_data" value="<?php echo $challenge; ?>" />
+				<input type="hidden" name="use_crypt" value="no" />
+				<input type="hidden" name="crypt_key" value="<?php echo $pubkey; ?>" />
+				<input type="hidden" name="crypt_data" value="" />
+			<script type="text/javascript">
+				// ENCRYPTION CODE
+				function runEncryption()
+				{
+					var frm = document.forms.regform;
+					if ( frm.password.value.length < 1 )
+						return true;
+					pass1 = frm.password.value;
+					pass2 = frm.password_confirm.value;
+					if ( pass1 != pass2 )
+					{
+						alert($lang.get('user_reg_err_alert_password_nomatch'));
+						return false;
+					}
+					if ( pass1.length < 6 && pass1.length > 0 )
+					{
+						alert($lang.get('user_reg_err_alert_password_tooshort'));
+						return false;
+					}
+					if(aes_self_test())
+					{
+						frm.use_crypt.value = 'yes';
+						var cryptkey = frm.crypt_key.value;
+						frm.crypt_key.value = hex_md5(cryptkey);
+						cryptkey = hexToByteArray(cryptkey);
+						if(!cryptkey || ( ( typeof cryptkey == 'string' || typeof cryptkey == 'object' ) ) && cryptkey.length != keySizeInBits / 8 )
+						{
+							frm.submit.disabled = true;
+							len = ( typeof cryptkey == 'string' || typeof cryptkey == 'object' ) ? '\nLen: '+cryptkey.length : '';
+							alert('The key is messed up\nType: '+typeof(cryptkey)+len);
+						}
+						pass = frm.password.value;
+						pass = stringToByteArray(pass);
+						cryptstring = rijndaelEncrypt(pass, cryptkey, 'ECB');
+						if(!cryptstring)
+						{
+							return false;
+						}
+						cryptstring = byteArrayToHex(cryptstring);
+						frm.crypt_data.value = cryptstring;
+						frm.password.value = "";
+						frm.password_confirm.value = "";
+					}
+					return true;
+				}
+				</script>
+			</form>
+			<!-- Don't optimize this script, it fails when compressed -->
+			<enano:no-opt>
+				<script type="text/javascript">
+					// <![CDATA[
+					var namegood = false;
+					function validateForm(field)
+					{
+						if ( typeof(field) != 'object' )
+						{
+							field = {
+								name: '_nil',
+								value: '_nil'
+							}
+						}
+						// wait until $lang is initted
+						if ( typeof($lang) != 'object' )
+						{
+							setTimeout('validateForm();', 200);
+							return false;
+						}
+						var frm = document.forms.regform;
+						failed = false;
+						
+						// Username
+						if(!namegood && ( field.name == 'username' || field.name == '_nil' ) ) 
+						{
+							//if(frm.username.value.match(/^([A-z0-9 \!@\-\(\)]+){2,}$/ig))
+							var regex = new RegExp('^([^<>&\?]+){2,}$', 'ig');
+							if ( frm.username.value.match(regex) )
+							{
+								document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkunk.png';
+								document.getElementById('e_username').innerHTML = '&nbsp;';
+							} else {
+								failed = true;
+								document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkbad.png';
+								document.getElementById('e_username').innerHTML = '<br /><small>' + $lang.get('user_reg_err_username_invalid') + '</small>';
+							}
+						}
+						if ( document.getElementById('b_username') )
+						{
+							document.getElementById('b_username').innerHTML = '';
+							if(hex_md5(frm.real_name.value) == '5a397df72678128cf0e8147a2befd5f1')
+							{
+								document.getElementById('b_username').innerHTML = '<br /><br />Hey...I know you!<br /><img alt="" src="http://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Bill_Gates_2004_cr.jpg/220px-Bill_Gates_2004_cr.jpg" />';
+							}
+						}
+						
+						// Password
+						if ( field.name == 'password' || field.name == 'password_confirm' || field.name == '_nil' )
+						{
+							if(frm.password.value.match(/^(.+){6,}$/ig) && frm.password_confirm.value.match(/^(.+){6,}$/ig) && frm.password.value == frm.password_confirm.value )
+							{
+								document.getElementById('s_password').src='<?php echo scriptPath; ?>/images/check.png';
+								document.getElementById('e_password').innerHTML = '<br /><small>' + $lang.get('user_reg_err_password_good') + '</small>';
+							} else {
+								failed = true;
+								if(frm.password.value.length < 6)
+								{
+									document.getElementById('e_password').innerHTML = '<br /><small>' + $lang.get('user_reg_msg_password_length') + '</small>';
+								}
+								else if(frm.password.value != frm.password_confirm.value)
+								{
+									document.getElementById('e_password').innerHTML = '<br /><small>' + $lang.get('user_reg_msg_password_needmatch') + '</small>';
+								}
+								else
+								{
+									document.getElementById('e_password').innerHTML = '';
+								}
+								document.getElementById('s_password').src='<?php echo scriptPath; ?>/images/checkbad.png';
+							}
+						}
+						
+						// E-mail address
+						
+						// workaround for idiot jEdit bug
+						if ( validateEmail(frm.email.value) && ( field.name == 'email' || field.name == '_nil' ) )
+						{
+							document.getElementById('s_email').src='<?php echo scriptPath; ?>/images/check.png';
+						} else {
+							failed = true;
+							document.getElementById('s_email').src='<?php echo scriptPath; ?>/images/checkbad.png';
+						}
+						if(failed)
+						{
+							frm.submit.disabled = 'disabled';
+						} else {
+							frm.submit.disabled = false;
+						}
+					}
+					function checkUsername()
+					{
+						var frm = document.forms.regform;
+						
+						if(!namegood)
+						{
+							var regex = new RegExp('^([^<>&\?]+){2,}$', 'ig');
+							if ( frm.username.value.match(regex) )
+							{
+								document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkunk.png';
+								document.getElementById('e_username').innerHTML = '&nbsp;';
+							} else {
+								document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkbad.png';
+								document.getElementById('e_username').innerHTML = '<br /><small>' + $lang.get('user_reg_err_username_invalid') + '</small>';
+								return false;
+							}
+						}
+						
+						document.getElementById('e_username').innerHTML = '<br /><small><b>' + $lang.get('user_reg_msg_username_checking') + '</b></small>';
+						ajaxGet('<?php echo scriptPath; ?>/ajax.php?title=null&_mode=checkusername&name='+escape(frm.username.value), function() {
+							if ( ajax.readyState == 4 && ajax.status == 200 )
+								if(ajax.responseText == 'good')
+								{
+									document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/check.png';
+									document.getElementById('e_username').innerHTML = '<br /><small><b>' + $lang.get('user_reg_msg_username_available') + '</b></small>';
+									namegood = true;
+								} else if(ajax.responseText == 'bad') {
+									document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/checkbad.png';
+									document.getElementById('e_username').innerHTML = '<br /><small><b>' + $lang.get('user_reg_msg_username_unavailable') + '</b></small>';
+									namegood = false;
+								} else {
+									document.getElementById('e_username').innerHTML = ajax.responseText;
+								}
+						});
+					}
+					function regenCaptcha()
+					{
+						var frm = document.forms.regform;
+						document.getElementById('captchaimg').src = '<?php echo makeUrlNS("Special", "Captcha/$captchacode"); ?>/'+Math.floor(Math.random() * 100000);
+						frm.captchacode.value = '';
+						return false;
+					}
+					function validateCaptcha(input)
+					{
+						var frm = document.forms.regform;
+						if ( input.value.length < 7 )
+						{
+							return false;
+						}
+						var valid_field = document.getElementById('s_captcha');
+						var loader_img = document.getElementById('captchaajax');
+						loader_img.src = cdnPath + '/images/loading.gif';
+						ajaxGet(makeUrlNS('Special', 'Captcha/' + frm.captchahash.value + '/validate=' + input.value), function(ajax)
+							{
+								if ( ajax.readyState == 4 && ajax.status == 200 )
+								{
+									var response = String(ajax.responseText + '');
+									if ( !check_json_response(response) )
+									{
+										handle_invalid_json(response);
+										return false;
+									}
+									response = parseJSON(response);
+									if ( response.valid )
+									{
+										loader_img.src = cdnPath + '/images/spacer.gif';
+										valid_field.src = cdnPath + '/images/check.png';
+									}
+									else
+									{
+										valid_field.src = cdnPath + '/images/checkbad.png';
+										regenCaptcha();
+										document.getElementById('captchaimg').onload = function()
+										{
+											document.getElementById('captchaajax').src = cdnPath + '/images/spacer.gif';
+											input.focus();
+										};
+										input.value = '';
+									}
+								}
+							});
+					}
+					addOnloadHook(function()
+						{
+							<?php if ( getConfig('pw_strength_enable') == '1' ): ?>
+							var frm = document.forms.regform;
+							load_component('pwstrength');
+							password_score_field(frm.password);
+							<?php endif; ?>
+							load_component('crypto');
+							validateForm();
+							setTimeout('checkUsername();', 1000);
+						});
+					// ]]>
+				</script>
+			</enano:no-opt>
+		<?php
+	}
+	else
+	{
+		$year = intval( enano_date('Y') );
+		$year = $year - 13;
+		$month = enano_date('F');
+		$day = enano_date('d');
+		
+		$yo13_date = "$month $day, $year";
+		$link_coppa_yes = makeUrlNS('Special', 'Register', 'coppa=yes', true);
+		$link_coppa_no  = makeUrlNS('Special', 'Register', 'coppa=no',  true);
+		
+		// COPPA enabled, ask age
+		echo '<div class="tblholder">';
+		echo '<table border="0" cellspacing="1" cellpadding="4">';
+		echo '<tr>
+						<td class="row1">
+							' . $lang->get('user_reg_coppa_title') . '
+						</td>
+					</tr>
+					<tr>
+						<td class="row3">
+							<a href="' . $link_coppa_no  . '">' . $lang->get('user_reg_coppa_link_atleast13', array( 'yo13_date' => $yo13_date )) . '</a><br />
+							<a href="' . $link_coppa_yes . '">' . $lang->get('user_reg_coppa_link_not13', array( 'yo13_date' => $yo13_date )) . '</a>
+						</td>
+					</tr>';
+		echo '</table>';
+		echo '</div>';
+	}
+	$template->footer();
 }
 
 function page_Special_Contributions()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  // This is a vast improvement over the old Special:Contributions in 1.0.x.
-  
-  $template->header();
-  $user = $paths->getParam();
-  if ( !$user && isset($_GET['user']) )
-  {
-    $user = $_GET['user'];
-  }
-  else if ( !$user && !isset($_GET['user']) )
-  {
-    echo '<p>' . $lang->get('userfuncs_contribs_err_no_user') . '</p>';
-    $template->footer();
-    return;
-  }
-  
-  $url = makeUrlNS("Special", "Log/user={$user}");
-  redirect($url, '', '', 0);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	// This is a vast improvement over the old Special:Contributions in 1.0.x.
+	
+	$template->header();
+	$user = $paths->getParam();
+	if ( !$user && isset($_GET['user']) )
+	{
+		$user = $_GET['user'];
+	}
+	else if ( !$user && !isset($_GET['user']) )
+	{
+		echo '<p>' . $lang->get('userfuncs_contribs_err_no_user') . '</p>';
+		$template->footer();
+		return;
+	}
+	
+	$url = makeUrlNS("Special", "Log/user={$user}");
+	redirect($url, '', '', 0);
 }
 
 function page_Special_ChangeStyle()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  if ( !$session->user_logged_in )
-  {
-    die_friendly('Access denied', '<p>You must be logged in to change your style. Spoofer.</p>');
-  }
-  if(isset($_POST['theme']) && isset($_POST['style']) && isset($_POST['return_to']))
-  {
-    if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['theme']) )
-      die('Hacking attempt');
-    if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['style']) )
-      die('Hacking attempt');
-    $d = ENANO_ROOT . '/themes/' . $_POST['theme'];
-    $f = ENANO_ROOT . '/themes/' . $_POST['theme'] . '/css/' . $_POST['style'] . '.css';
-    if ( !file_exists($d) || !is_dir($d) )
-    {
-      die('The directory "'.$d.'" does not exist.');
-    }
-    if ( !file_exists($f) )
-    {
-      die('The file "'.$f.'" does not exist.');
-    }
-    $d = $db->escape($_POST['theme']);
-    $f = $db->escape($_POST['style']);
-    $q = 'UPDATE '.table_prefix.'users SET theme=\''.$d.'\',style=\''.$f.'\' WHERE username=\''.$session->username.'\'';
-    if ( !$db->sql_query($q) )
-    {
-      $db->_die('Your theme/style preferences were not updated.');
-    }
-    else
-    {
-      redirect(makeUrl($_POST['return_to']), $lang->get('userfuncs_changetheme_success_title'), $lang->get('userfuncs_changetheme_success_body'), 3);
-    }
-  }
-  else
-  {
-    $template->header();
-      $ret = ( isset($_POST['return_to']) ) ? $_POST['return_to'] : $paths->getParam(0);
-      if ( !$ret )
-      {
-        $ret = get_main_page();
-      }
-      ?>
-        <form action="<?php echo makeUrl($paths->page); ?>" method="post">
-          <?php if ( !isset($_POST['themeselected']) ) { ?>
-            <h3><?php echo $lang->get('userfuncs_changetheme_heading_theme'); ?></h3>
-            <p>
-              <select name="theme">
-               <?php
-                foreach ( $template->theme_list as $t )
-                {
-                  if ( $t['enabled'] )
-                  {
-                    echo '<option value="'.$t['theme_id'].'"';
-                    if ( $t['theme_id'] == $session->theme )
-                    {
-                      echo ' selected="selected"';
-                    }
-                    echo '>' . $t['theme_name'] . '</option>';
-                  }
-                }
-               ?>
-              </select>
-            </p>
-            <p><input type="hidden" name="return_to" value="<?php echo $ret; ?>" />
-               <input type="submit" name="themeselected" value="<?php echo $lang->get('userfuncs_changetheme_btn_continue'); ?>" /></p>
-          <?php } else { 
-            $theme = $_POST['theme'];
-            if ( !preg_match('/^([0-9A-z_-]+)$/i', $theme ) )
-              die('Hacking attempt');
-            ?>
-            <h3><?php echo $lang->get('userfuncs_changetheme_heading_style'); ?></h3>
-            <p>
-              <select name="style">
-                <?php
-                  $dir = './themes/'.$theme.'/css/';
-                  $list = Array();
-                  // Open a known directory, and proceed to read its contents
-                  if (is_dir($dir)) {
-                    if ($dh = opendir($dir)) {
-                      while (($file = readdir($dh)) !== false) {
-                        if(preg_match('#^(.*?)\.css$#is', $file) && $file != '_printable.css') {
-                          $list[] = substr($file, 0, strlen($file)-4);
-                        }
-                      }
-                      closedir($dh);
-                    }
-                  } else die($dir.' is not a dir');
-                  foreach ( $list as $l )
-                  {
-                    echo '<option value="'.$l.'">'.capitalize_first_letter($l).'</option>';
-                  }
-                ?>
-              </select>
-            </p>
-            <p><input type="hidden" name="return_to" value="<?php echo $ret; ?>" />
-               <input type="hidden" name="theme" value="<?php echo $theme; ?>" />
-               <input type="submit" name="allclear" value="<?php echo $lang->get('userfuncs_changetheme_btn_allclear'); ?>" /></p>
-          <?php } ?>
-        </form>
-      <?php
-    $template->footer();
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	if ( !$session->user_logged_in )
+	{
+		die_friendly('Access denied', '<p>You must be logged in to change your style. Spoofer.</p>');
+	}
+	if(isset($_POST['theme']) && isset($_POST['style']) && isset($_POST['return_to']))
+	{
+		if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['theme']) )
+			die('Hacking attempt');
+		if ( !preg_match('/^([a-z0-9_-]+)$/i', $_POST['style']) )
+			die('Hacking attempt');
+		$d = ENANO_ROOT . '/themes/' . $_POST['theme'];
+		$f = ENANO_ROOT . '/themes/' . $_POST['theme'] . '/css/' . $_POST['style'] . '.css';
+		if ( !file_exists($d) || !is_dir($d) )
+		{
+			die('The directory "'.$d.'" does not exist.');
+		}
+		if ( !file_exists($f) )
+		{
+			die('The file "'.$f.'" does not exist.');
+		}
+		$d = $db->escape($_POST['theme']);
+		$f = $db->escape($_POST['style']);
+		$q = 'UPDATE '.table_prefix.'users SET theme=\''.$d.'\',style=\''.$f.'\' WHERE username=\''.$session->username.'\'';
+		if ( !$db->sql_query($q) )
+		{
+			$db->_die('Your theme/style preferences were not updated.');
+		}
+		else
+		{
+			redirect(makeUrl($_POST['return_to']), $lang->get('userfuncs_changetheme_success_title'), $lang->get('userfuncs_changetheme_success_body'), 3);
+		}
+	}
+	else
+	{
+		$template->header();
+			$ret = ( isset($_POST['return_to']) ) ? $_POST['return_to'] : $paths->getParam(0);
+			if ( !$ret )
+			{
+				$ret = get_main_page();
+			}
+			?>
+				<form action="<?php echo makeUrl($paths->page); ?>" method="post">
+					<?php if ( !isset($_POST['themeselected']) ) { ?>
+						<h3><?php echo $lang->get('userfuncs_changetheme_heading_theme'); ?></h3>
+						<p>
+							<select name="theme">
+ 							<?php
+								foreach ( $template->theme_list as $t )
+								{
+									if ( $t['enabled'] )
+									{
+										echo '<option value="'.$t['theme_id'].'"';
+										if ( $t['theme_id'] == $session->theme )
+										{
+											echo ' selected="selected"';
+										}
+										echo '>' . $t['theme_name'] . '</option>';
+									}
+								}
+ 							?>
+							</select>
+						</p>
+						<p><input type="hidden" name="return_to" value="<?php echo $ret; ?>" />
+ 							<input type="submit" name="themeselected" value="<?php echo $lang->get('userfuncs_changetheme_btn_continue'); ?>" /></p>
+					<?php } else { 
+						$theme = $_POST['theme'];
+						if ( !preg_match('/^([0-9A-z_-]+)$/i', $theme ) )
+							die('Hacking attempt');
+						?>
+						<h3><?php echo $lang->get('userfuncs_changetheme_heading_style'); ?></h3>
+						<p>
+							<select name="style">
+								<?php
+									$dir = './themes/'.$theme.'/css/';
+									$list = Array();
+									// Open a known directory, and proceed to read its contents
+									if (is_dir($dir)) {
+										if ($dh = opendir($dir)) {
+											while (($file = readdir($dh)) !== false) {
+												if(preg_match('#^(.*?)\.css$#is', $file) && $file != '_printable.css') {
+													$list[] = substr($file, 0, strlen($file)-4);
+												}
+											}
+											closedir($dh);
+										}
+									} else die($dir.' is not a dir');
+									foreach ( $list as $l )
+									{
+										echo '<option value="'.$l.'">'.capitalize_first_letter($l).'</option>';
+									}
+								?>
+							</select>
+						</p>
+						<p><input type="hidden" name="return_to" value="<?php echo $ret; ?>" />
+ 							<input type="hidden" name="theme" value="<?php echo $theme; ?>" />
+ 							<input type="submit" name="allclear" value="<?php echo $lang->get('userfuncs_changetheme_btn_allclear'); ?>" /></p>
+					<?php } ?>
+				</form>
+			<?php
+		$template->footer();
+	}
 }
 
 function page_Special_ActivateAccount()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $user = $paths->getParam(0);
-  if ( !$user )
-  {
-    die_friendly($lang->get('userfuncs_activate_err_badlink_title'), '<p>' . $lang->get('userfuncs_activate_err_badlink_body') . '</p>');
-  }
-  $user = str_replace('_', ' ', dirtify_page_id($user));
-  $key = $paths->getParam(1);
-  if ( !$key )
-  {
-    die_friendly($lang->get('userfuncs_activate_err_badlink_title'), '<p>' . $lang->get('userfuncs_activate_err_badlink_body') . '</p>');
-  }
-  $s = $session->activate_account(str_replace('_', ' ', $user), $key);
-  if ( $s )
-  {
-    die_friendly($lang->get('userfuncs_activate_success_title'), '<p>' . $lang->get('userfuncs_activate_success_body') . '</p>');
-  }
-  else
-  {
-    die_friendly($lang->get('userfuncs_activate_err_badlink_title'), '<p>' . $lang->get('userfuncs_activate_err_bad_key') . '</p>');
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$user = $paths->getParam(0);
+	if ( !$user )
+	{
+		die_friendly($lang->get('userfuncs_activate_err_badlink_title'), '<p>' . $lang->get('userfuncs_activate_err_badlink_body') . '</p>');
+	}
+	$user = str_replace('_', ' ', dirtify_page_id($user));
+	$key = $paths->getParam(1);
+	if ( !$key )
+	{
+		die_friendly($lang->get('userfuncs_activate_err_badlink_title'), '<p>' . $lang->get('userfuncs_activate_err_badlink_body') . '</p>');
+	}
+	$s = $session->activate_account(str_replace('_', ' ', $user), $key);
+	if ( $s )
+	{
+		die_friendly($lang->get('userfuncs_activate_success_title'), '<p>' . $lang->get('userfuncs_activate_success_body') . '</p>');
+	}
+	else
+	{
+		die_friendly($lang->get('userfuncs_activate_err_badlink_title'), '<p>' . $lang->get('userfuncs_activate_err_bad_key') . '</p>');
+	}
 }
 
 function page_Special_Captcha()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  if ( $paths->getParam(0) == 'make' )
-  {
-    $session->kill_captcha();
-    echo $session->make_captcha();
-    return;
-  }
-  
-  $hash = $paths->getParam(0);
-  if ( !$hash || !preg_match('#^([0-9a-f]*){32,40}$#i', $hash) )
-  {
-    $paths->main_page();
-  }
-  
-  if ( $validate_code = $paths->getParam(1) )
-  {
-    if ( preg_match('/^validate=(.+)$/', $validate_code, $match) )
-    {
-      header('Content-type: text/javascript');
-      $code = $session->get_captcha($hash, true);
-      $valid = strtolower($code) === strtolower($match[1]);
-      if ( !$valid )
-      {
-        $session->make_captcha(7, $hash);
-      }
-      echo enano_json_encode(array(
-        'valid' => $valid
-        ));
-      exit;
-    }
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	if ( $paths->getParam(0) == 'make' )
+	{
+		$session->kill_captcha();
+		echo $session->make_captcha();
+		return;
+	}
+	
+	$hash = $paths->getParam(0);
+	if ( !$hash || !preg_match('#^([0-9a-f]*){32,40}$#i', $hash) )
+	{
+		$paths->main_page();
+	}
+	
+	if ( $validate_code = $paths->getParam(1) )
+	{
+		if ( preg_match('/^validate=(.+)$/', $validate_code, $match) )
+		{
+			header('Content-type: text/javascript');
+			$code = $session->get_captcha($hash, true);
+			$valid = strtolower($code) === strtolower($match[1]);
+			if ( !$valid )
+			{
+				$session->make_captcha(7, $hash);
+			}
+			echo enano_json_encode(array(
+				'valid' => $valid
+				));
+			exit;
+		}
+	}
 
-  $session->make_captcha(7, $hash);
-  $code = $session->generate_captcha_code();
-  // Avoid letting our captchas end up on failblog.org
-  // BTW, the last one was a real-life encounter: http://files.ha.xx0r.info/murder.png
-  foreach ( array('shit', 'cock', 'fuck', 'nazi', 'cunt', 'clit', 'pussy', 'penis', 'piss', 'tits', 'murder') as $word )
-  {
-    if ( stristr($code, $word) )
-    {
-      // but don't put too much effort into this (will only correct this once)
-      // I mean, face it. If it generates one of those words twice in a row, either the local root has had
-      // way too much fun with his /dev/random, or this server is just plain gutter-minded.
-      $code = $session->generate_captcha_code();
-      break;
-    }
-  }
-  $q = $db->sql_query('UPDATE ' . table_prefix . "captcha SET code = '$code' WHERE session_id = '$hash';");
-  if ( !$q )
-    $db->_die();
-  
-  require ( ENANO_ROOT.'/includes/captcha.php' );
-  $captcha = captcha_object($hash, 'freecap');
-  // $captcha->debug = true;
-  $captcha->make_image();
-  
-  exit;
+	$session->make_captcha(7, $hash);
+	$code = $session->generate_captcha_code();
+	// Avoid letting our captchas end up on failblog.org
+	// BTW, the last one was a real-life encounter: http://files.ha.xx0r.info/murder.png
+	foreach ( array('shit', 'cock', 'fuck', 'nazi', 'cunt', 'clit', 'pussy', 'penis', 'piss', 'tits', 'murder') as $word )
+	{
+		if ( stristr($code, $word) )
+		{
+			// but don't put too much effort into this (will only correct this once)
+			// I mean, face it. If it generates one of those words twice in a row, either the local root has had
+			// way too much fun with his /dev/random, or this server is just plain gutter-minded.
+			$code = $session->generate_captcha_code();
+			break;
+		}
+	}
+	$q = $db->sql_query('UPDATE ' . table_prefix . "captcha SET code = '$code' WHERE session_id = '$hash';");
+	if ( !$q )
+		$db->_die();
+	
+	require ( ENANO_ROOT.'/includes/captcha.php' );
+	$captcha = captcha_object($hash, 'freecap');
+	// $captcha->debug = true;
+	$captcha->make_image();
+	
+	exit;
 }
 
 function page_Special_PasswordReset()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $template->header();
-  if($paths->getParam(0) == 'stage2')
-  {
-    require_once(ENANO_ROOT . '/includes/math.php');
-    require_once(ENANO_ROOT . '/includes/diffiehellman.php');
-    
-    $user_id = intval($paths->getParam(1));
-    $encpass = $paths->getParam(2);
-    if ( $user_id < 2 )
-    {
-      echo '<p>Hacking attempt</p>';
-      $template->footer();
-      return false;
-    }
-    if(!preg_match('#^([a-f0-9]+)$#i', $encpass))
-    {
-      echo '<p>Hacking attempt</p>';
-      $template->footer();
-      return false;
-    }
-    
-    $q = $db->sql_query('SELECT username,temp_password_time,temp_password,password_salt FROM '.table_prefix.'users WHERE user_id='.$user_id.';');
-    if($db->numrows() < 1)
-    {
-      echo '<p>Invalid credentials</p>';
-      $template->footer();
-      return false;
-    }
-    $row = $db->fetchrow();
-    $db->free_result();
-    
-    $temp_pass = $session->pk_decrypt($encpass);
-    $temp_hmac = hmac_sha1($temp_pass, $row['password_salt']);
-    
-    if ( $temp_hmac !== $row['temp_password'] )
-    {
-      echo '<p>Invalid credentials</p>';
-      $template->footer();
-      return false;
-    }
-    
-    if ( ( intval($row['temp_password_time']) + ( 3600 * 24 ) ) < time() )
-    {
-      echo '<p>' . $lang->get('userfuncs_passreset_err_pass_expired', array('reset_url' => makeUrlNS('Special', 'PasswordReset'))) . '</p>';
-      $template->footer();
-      return false;
-    }
-    
-    if ( isset($_POST['do_stage2']) )
-    {
-      $data = $session->get_aes_post('pass');
-      
-      if(empty($data))
-      {
-        echo 'ERROR: Sanity check failed!';
-        $template->footer();
-        return false;
-      }
-      if ( strlen($data) < 6 )
-      {
-        echo '<p>' . $lang->get('userfuncs_passreset_err_too_short') . '</p>';
-        $template->footer();
-        return false;
-      }
-      if ( $_POST['use_crypt'] == 'no' )
-      {
-        if ( $_POST['pass'] !== $_POST['pass_confirm'] )
-        {
-          echo '<p>' . $lang->get('userfuncs_passreset_err_no_match') . '</p>';
-          $template->footer();
-          return false;
-        }
-      }
-      if ( getConfig('pw_strength_enable') == '1' )
-      {
-        $min_score = intval(getConfig('pw_strength_minimum'));
-        $inp_score = password_score($data);
-        if ( $inp_score < $min_score )
-        {
-          $url = makeUrl($paths->fullpage);
-          echo "<p>" . $lang->get('userfuncs_passreset_err_failed_score', array('inp_score' => $inp_score, 'url' => $url)) . "</p>";
-          $template->footer();
-          return false;
-        }
-      }
-      
-      $session->set_password($user_id, $data);
-      
-      $q = $db->sql_query('UPDATE '.table_prefix.'users SET temp_password=\'\',temp_password_time=0 WHERE user_id = '.$user_id.';');
-      
-      if($q)
-      {
-        $session->login_without_crypto($row['username'], $data);
-        echo '<p>' . $lang->get('userfuncs_passreset_stage2_success', array('url_mainpage' => makeUrl(get_main_page()))) . '</p>';
-      }
-      else
-      {
-        echo $db->get_error();
-      }
-      
-      $template->footer();
-      return false;
-    }
-    
-    // Password reset form
-    $evt_get_score = ( getConfig('pw_strength_enable') == '1' ) ? 'onkeyup="password_score_field(this);" ' : '';
-    $pw_meter =      ( getConfig('pw_strength_enable') == '1' ) ? '<tr><td class="row1">' . $lang->get('userfuncs_passreset_stage2_lbl_strength') . '</td><td class="row1"><div id="pwmeter"></div></td></tr>' : '';
-    $pw_blurb =      ( getConfig('pw_strength_enable') == '1' && intval(getConfig('pw_strength_minimum')) > -10 ) ? '<br /><small>' . $lang->get('userfuncs_passreset_stage2_blurb_strength') . '</small>' : '';
-    
-    ?>
-    <form action="<?php echo makeUrl($paths->fullpage); ?>" method="post" name="resetform" onsubmit="return runEncryption();">
-      <br />
-      <div class="tblholder">
-        <table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
-          <tr><th colspan="2"><?php echo $lang->get('userfuncs_passreset_stage2_th'); ?></th></tr>
-          <tr><td class="row1"><?php echo $lang->get('userfuncs_passreset_stage2_lbl_password'); ?> <?php echo $pw_blurb; ?></td><td class="row1"><input name="pass" type="password" <?php echo $evt_get_score; ?>/></td></tr>
-          <tr><td class="row2"><?php echo $lang->get('userfuncs_passreset_stage2_lbl_confirm'); ?> </td><td class="row2"><input name="pass_confirm" type="password" /></td></tr>
-          <?php echo $pw_meter; ?>
-          <tr>
-            <td colspan="2" class="row3" style="text-align: center;">
-              
-              <input type="submit" name="do_stage2" value="<?php echo $lang->get('userfuncs_passreset_stage2_btn_submit'); ?>" />
-            </td>
-          </tr>
-        </table>
-      </div>
-      <?php echo $session->generate_aes_form(); ?>
-    </form>
-    <script type="text/javascript">
-    addOnloadHook(function()
-      {
-        load_component('pwstrength');
-        password_score_field(document.forms.resetform.pass);
-      });
-    </script>
-    <?php
-    echo $session->aes_javascript('resetform', 'pass', 'use_crypt', 'crypt_key', 'crypt_data', 'challenge_data', 'dh_supported', 'dh_public_key', 'dh_client_public_key');
-    $template->footer();
-    return true;
-  }
-  if ( $session->user_logged_in )
-  {
-    $paths->main_page();
-  }
-  
-  if(isset($_POST['do_reset']))
-  {
-    if($session->mail_password_reset($_POST['username']))
-    {
-      echo '<p>' . $lang->get('userfuncs_passreset_stage1_success') . '</p>';
-    }
-    else
-    {
-      echo '<p>' . $lang->get('userfuncs_passreset_stage1_error') . '</p>';
-    }
-    $template->footer();
-    return true;
-  }
-  echo '<p>' . $lang->get('userfuncs_passreset_blurb_line1') . '</p>
-        <p>' . $lang->get('userfuncs_passreset_blurb_line2') . '</p>
-        <form action="'.makeUrl($paths->page).'" method="post" onsubmit="if(!submitAuthorized) return false;">
-          <p>' . $lang->get('userfuncs_passreset_lbl_username') . '  '.$template->username_field('username').'</p>
-          <p><input type="submit" name="do_reset" value="' . $lang->get('userfuncs_passreset_btn_mailpasswd') . '" /></p>
-        </form>';
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$template->header();
+	if($paths->getParam(0) == 'stage2')
+	{
+		require_once(ENANO_ROOT . '/includes/math.php');
+		require_once(ENANO_ROOT . '/includes/diffiehellman.php');
+		
+		$user_id = intval($paths->getParam(1));
+		$encpass = $paths->getParam(2);
+		if ( $user_id < 2 )
+		{
+			echo '<p>Hacking attempt</p>';
+			$template->footer();
+			return false;
+		}
+		if(!preg_match('#^([a-f0-9]+)$#i', $encpass))
+		{
+			echo '<p>Hacking attempt</p>';
+			$template->footer();
+			return false;
+		}
+		
+		$q = $db->sql_query('SELECT username,temp_password_time,temp_password,password_salt FROM '.table_prefix.'users WHERE user_id='.$user_id.';');
+		if($db->numrows() < 1)
+		{
+			echo '<p>Invalid credentials</p>';
+			$template->footer();
+			return false;
+		}
+		$row = $db->fetchrow();
+		$db->free_result();
+		
+		$temp_pass = $session->pk_decrypt($encpass);
+		$temp_hmac = hmac_sha1($temp_pass, $row['password_salt']);
+		
+		if ( $temp_hmac !== $row['temp_password'] )
+		{
+			echo '<p>Invalid credentials</p>';
+			$template->footer();
+			return false;
+		}
+		
+		if ( ( intval($row['temp_password_time']) + ( 3600 * 24 ) ) < time() )
+		{
+			echo '<p>' . $lang->get('userfuncs_passreset_err_pass_expired', array('reset_url' => makeUrlNS('Special', 'PasswordReset'))) . '</p>';
+			$template->footer();
+			return false;
+		}
+		
+		if ( isset($_POST['do_stage2']) )
+		{
+			$data = $session->get_aes_post('pass');
+			
+			if(empty($data))
+			{
+				echo 'ERROR: Sanity check failed!';
+				$template->footer();
+				return false;
+			}
+			if ( strlen($data) < 6 )
+			{
+				echo '<p>' . $lang->get('userfuncs_passreset_err_too_short') . '</p>';
+				$template->footer();
+				return false;
+			}
+			if ( $_POST['use_crypt'] == 'no' )
+			{
+				if ( $_POST['pass'] !== $_POST['pass_confirm'] )
+				{
+					echo '<p>' . $lang->get('userfuncs_passreset_err_no_match') . '</p>';
+					$template->footer();
+					return false;
+				}
+			}
+			if ( getConfig('pw_strength_enable') == '1' )
+			{
+				$min_score = intval(getConfig('pw_strength_minimum'));
+				$inp_score = password_score($data);
+				if ( $inp_score < $min_score )
+				{
+					$url = makeUrl($paths->fullpage);
+					echo "<p>" . $lang->get('userfuncs_passreset_err_failed_score', array('inp_score' => $inp_score, 'url' => $url)) . "</p>";
+					$template->footer();
+					return false;
+				}
+			}
+			
+			$session->set_password($user_id, $data);
+			
+			$q = $db->sql_query('UPDATE '.table_prefix.'users SET temp_password=\'\',temp_password_time=0 WHERE user_id = '.$user_id.';');
+			
+			if($q)
+			{
+				$session->login_without_crypto($row['username'], $data);
+				echo '<p>' . $lang->get('userfuncs_passreset_stage2_success', array('url_mainpage' => makeUrl(get_main_page()))) . '</p>';
+			}
+			else
+			{
+				echo $db->get_error();
+			}
+			
+			$template->footer();
+			return false;
+		}
+		
+		// Password reset form
+		$evt_get_score = ( getConfig('pw_strength_enable') == '1' ) ? 'onkeyup="password_score_field(this);" ' : '';
+		$pw_meter =      ( getConfig('pw_strength_enable') == '1' ) ? '<tr><td class="row1">' . $lang->get('userfuncs_passreset_stage2_lbl_strength') . '</td><td class="row1"><div id="pwmeter"></div></td></tr>' : '';
+		$pw_blurb =      ( getConfig('pw_strength_enable') == '1' && intval(getConfig('pw_strength_minimum')) > -10 ) ? '<br /><small>' . $lang->get('userfuncs_passreset_stage2_blurb_strength') . '</small>' : '';
+		
+		?>
+		<form action="<?php echo makeUrl($paths->fullpage); ?>" method="post" name="resetform" onsubmit="return runEncryption();">
+			<br />
+			<div class="tblholder">
+				<table border="0" style="width: 100%;" cellspacing="1" cellpadding="4">
+					<tr><th colspan="2"><?php echo $lang->get('userfuncs_passreset_stage2_th'); ?></th></tr>
+					<tr><td class="row1"><?php echo $lang->get('userfuncs_passreset_stage2_lbl_password'); ?> <?php echo $pw_blurb; ?></td><td class="row1"><input name="pass" type="password" <?php echo $evt_get_score; ?>/></td></tr>
+					<tr><td class="row2"><?php echo $lang->get('userfuncs_passreset_stage2_lbl_confirm'); ?> </td><td class="row2"><input name="pass_confirm" type="password" /></td></tr>
+					<?php echo $pw_meter; ?>
+					<tr>
+						<td colspan="2" class="row3" style="text-align: center;">
+							
+							<input type="submit" name="do_stage2" value="<?php echo $lang->get('userfuncs_passreset_stage2_btn_submit'); ?>" />
+						</td>
+					</tr>
+				</table>
+			</div>
+			<?php echo $session->generate_aes_form(); ?>
+		</form>
+		<script type="text/javascript">
+		addOnloadHook(function()
+			{
+				load_component('pwstrength');
+				password_score_field(document.forms.resetform.pass);
+			});
+		</script>
+		<?php
+		echo $session->aes_javascript('resetform', 'pass', 'use_crypt', 'crypt_key', 'crypt_data', 'challenge_data', 'dh_supported', 'dh_public_key', 'dh_client_public_key');
+		$template->footer();
+		return true;
+	}
+	if ( $session->user_logged_in )
+	{
+		$paths->main_page();
+	}
+	
+	if(isset($_POST['do_reset']))
+	{
+		if($session->mail_password_reset($_POST['username']))
+		{
+			echo '<p>' . $lang->get('userfuncs_passreset_stage1_success') . '</p>';
+		}
+		else
+		{
+			echo '<p>' . $lang->get('userfuncs_passreset_stage1_error') . '</p>';
+		}
+		$template->footer();
+		return true;
+	}
+	echo '<p>' . $lang->get('userfuncs_passreset_blurb_line1') . '</p>
+				<p>' . $lang->get('userfuncs_passreset_blurb_line2') . '</p>
+				<form action="'.makeUrl($paths->page).'" method="post" onsubmit="if(!submitAuthorized) return false;">
+					<p>' . $lang->get('userfuncs_passreset_lbl_username') . '  '.$template->username_field('username').'</p>
+					<p><input type="submit" name="do_reset" value="' . $lang->get('userfuncs_passreset_btn_mailpasswd') . '" /></p>
+				</form>';
+	$template->footer();
 }
 
 function page_Special_Memberlist()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $template->header();
-  
-  $startletters = 'abcdefghijklmnopqrstuvwxyz';
-  $startletters = enano_str_split($startletters);
-  $startletter = ( isset($_GET['letter']) ) ? strtolower($_GET['letter']) : '';
-  if ( !in_array($startletter, $startletters) && $startletter != 'chr' )
-  {
-    $startletter = '';
-  }
-  
-  $startletter_sql = $startletter;
-  if ( $startletter == 'chr' )
-  {
-    $startletter_sql = '([^a-z])';
-  }
-  
-  // offset
-  $perpage = 25;
-  $page = (( isset($_GET['offset']) && strval(intval($_GET['offset'])) === $_GET['offset']) ? intval($_GET['offset']) : 1) - 1;
-  $offset = $page * $perpage;
-  
-  // sort order
-  $sortkeys = array(
-      'uid' => 'u.user_id',
-      'username' => 'u.username',
-      'email' => 'u.email',
-      'regist' => 'u.reg_time'
-    );
-  
-  $sortby = ( isset($_GET['sort']) && isset($sortkeys[$_GET['sort']]) ) ? $_GET['sort'] : 'username';
-  $sort_sqllet = $sortkeys[$sortby];
-  
-  $target_order = ( isset($_GET['orderby']) && in_array($_GET['orderby'], array('ASC', 'DESC')) )? $_GET['orderby'] : 'ASC';
-  
-  $sortorders = array();
-  foreach ( $sortkeys as $k => $_unused )
-  {
-    $sortorders[$k] = ( $sortby == $k ) ? ( $target_order == 'ASC' ? 'DESC' : 'ASC' ) : 'ASC';
-  }
-  
-  // Why 3.3714%? 100 percent / 28 cells, minus a little (0.2% / cell) to account for cell spacing
-  
-  echo '<div class="tblholder">
-          <table border="0" cellspacing="1" cellpadding="4" style="text-align: center;">
-            <tr>';
-  echo '<td class="row1" style="width: 3.3714%;"><a href="' . makeUrlNS('Special', 'Memberlist', 'letter=&sort=' . $sortby . '&orderby=' . $target_order, true) . '">All</a></td>';
-  echo '<td class="row1" style="width: 3.3714%;"><a href="' . makeUrlNS('Special', 'Memberlist', 'letter=chr&sort=' . $sortby . '&orderby=' . $target_order, true) . '">#</a></td>';
-  foreach ( $startletters as $letter )
-  {
-    echo '<td class="row1" style="width: 3.3714%;"><a href="' . makeUrlNS('Special', 'Memberlist', 'letter=' . $letter . '&sort=' . $sortby . '&orderby=' . $target_order, true) . '">' . strtoupper($letter) . '</a></td>';
-  }
-  echo '    </tr>
-          </table>
-        </div>';
-  
-  // User search             
-  if ( isset($_GET['finduser']) )
-  {
-    $finduser = str_replace(array(  '%',   '_'),
-                            array('\\%', '\\_'),
-                            $_GET['finduser']);
-    $finduser = str_replace(array('*', '?'),
-                            array('%', '_'),
-                            $finduser);
-    $finduser = $db->escape($finduser);
-    $username_where = ENANO_SQLFUNC_LOWERCASE . '(u.username) LIKE \'%' . strtolower($finduser) . '%\'';
-    $finduser_url = 'finduser=' . rawurlencode($_GET['finduser']) . '&';
-  }
-  else
-  {
-    if ( ENANO_DBLAYER == 'MYSQL' )
-      $username_where = 'lcase(u.username) REGEXP lcase("^' . $startletter_sql . '")';
-    else if ( ENANO_DBLAYER == 'PGSQL' )
-      $username_where = 'lower(u.username) ~ lower(\'^' . $startletter_sql . '\')';
-    $finduser_url = '';
-  }
-  
-  // Column markers
-  $headings = '<tr>
-                 <th style="max-width: 50px;">
-                   <a href="' . makeUrlNS('Special', 'Memberlist', $finduser_url . 'letter=' . $startletter . '&sort=uid&orderby=' . $sortorders['uid'], true) . '">#</a>
-                 </th>
-                 <th>
-                   <a href="' . makeUrlNS('Special', 'Memberlist', $finduser_url . 'letter=' . $startletter . '&sort=username&orderby=' . $sortorders['username'], true) . '">' . $lang->get('userfuncs_ml_column_username') . '</a>
-                 </th>
-                 <th>
-                   ' . $lang->get('userfuncs_ml_column_userlevel') . '
-                 </th>
-                 <th>
-                   <a href="' . makeUrlNS('Special', 'Memberlist', $finduser_url . 'letter=' . $startletter . '&sort=email&orderby=' . $sortorders['email'], true) . '">' . $lang->get('userfuncs_ml_column_email') . '</a>
-                 </th>
-                 <th>
-                   <a href="' . makeUrlNS('Special', 'Memberlist', $finduser_url . 'letter=' . $startletter . '&sort=regist&orderby=' . $sortorders['regist'], true) . '">' . $lang->get('userfuncs_ml_column_regtime') . '</a>
-                 </th>
-               </tr>';
-               
-  // determine number of rows
-  $q = $db->sql_query('SELECT COUNT(u.user_id) FROM '.table_prefix.'users AS u WHERE ' . $username_where . ' AND u.username != \'Anonymous\';');
-  if ( !$q )
-    $db->_die();
-  
-  list($num_rows) = $db->fetchrow_num();
-  $db->free_result();
-  
-  if ( !empty($finduser_url) )
-  {
-    switch ( $num_rows )
-    {
-      case 0:
-        $str = ''; /* $lang->get('userfuncs_ml_msg_matches_zero'); */ break;
-      case 1:
-        $str = $lang->get('userfuncs_ml_msg_matches_one'); break;
-      default:
-        $str = $lang->get('userfuncs_ml_msg_matches', array('matches' => $num_rows)); break;
-    }
-    echo "<h3>$str</h3>";
-  }
-  
-  // main selector
-  $pgsql_additional_group_by = ( ENANO_DBLAYER == 'PGSQL' ) ? ', u.username, u.reg_time, u.email, u.user_level, u.user_has_avatar, u.avatar_type, x.email_public' : '';
-  $q = $db->sql_query('SELECT \'\' AS infobit, u.user_id, u.username, u.reg_time, u.email, u.user_level, u.user_has_avatar, u.avatar_type, x.email_public, COUNT(c.comment_id) AS num_comments FROM '.table_prefix.'users AS u
-                                    LEFT JOIN '.table_prefix.'users_extra AS x
-                                      ON ( u.user_id = x.user_id )
-                                    LEFT JOIN ' . table_prefix . 'comments AS c
-                                      ON ( u.user_id = c.user_id )
-                                    WHERE ' . $username_where . ' AND u.username != \'Anonymous\'
-                                    GROUP BY u.user_id' . $pgsql_additional_group_by . '
-                                    ORDER BY ' . $sort_sqllet . ' ' . $target_order . '
-                                    LIMIT ' . $perpage . ' OFFSET ' . $offset . ';');
-  if ( !$q )
-    $db->_die();
-  
-  // formatter parameters
-  $formatter = new MemberlistFormatter();
-  $formatters = array(
-    'username' => array($formatter, 'username'),
-    'user_level' => array($formatter, 'user_level'),
-    'email' => array($formatter, 'email'),
-    'reg_time' => array($formatter, 'reg_time'),
-    'infobit' => array($formatter, 'infobit')
-    );
-  
-  $result_url = makeUrlNS('Special', 'Memberlist', ( str_replace('%', '%%', $finduser_url) ) . 'letter=' . $startletter . '&offset=%s&sort=' . $sortby . '&orderby=' . $target_order );
-  $paginator = generate_paginator($page, ceil($num_rows / $perpage), $result_url);
-  
-  if ( $num_rows > 0 )
-  {
-    if ( $num_rows > $perpage )
-      echo $paginator;
-    
-    echo '<div class="tblholder">
-                <table border="0" cellspacing="1" cellpadding="4" style="text-align: center;">
-                  ' . $headings;
-                  
-    $i = 0;
-    while ( $row = $db->fetchrow($q) )
-    {
-      $i++;
-      $cls = ( $i % 2 == 0 ) ? 'row2' : 'row1';
-      echo '<tr>';
-      echo '<td class="' . $cls . '">' . $row['user_id'] . '</td>';
-      echo '<td class="' . $cls . '" style="text-align: left;">' . $formatter->username($row['username'], $row) . '</td>';
-      echo '<td class="' . $cls . '">' . $formatter->user_level($row['user_level'], $row) . '</td>';
-      echo '<td class="' . $cls . '">' . $formatter->email($row['email'], $row) . '</td>';
-      echo '<td class="' . $cls . '">' . $formatter->reg_time($row['reg_time'], $row) . '</td>';
-      echo '</tr>';
-      echo '<tr>';
-      echo '<td colspan="5" class="row3" style="text-align: left;">
-                 <div id="ml_moreinfo_' . $row['user_id'] . '" style="display: none;">
-                   ' . $formatter->infobit(true, $row) . '
-                 </div>
-               </td>';
-      echo '</tr>';
-    }
-    
-    echo '  ' . $headings . '
-                 </table>
-              </div>
-              ';
-    
-    if ( $num_rows > $perpage )
-      echo $paginator;
-  }
-  else
-  {
-    echo '<h2 class="emptymessage">' . $lang->get('log_msg_no_results') . '</h2>';
-  }
-  
-  echo '<div style="float: left;">
-          <form action="' . makeUrlNS('Special', 'Memberlist') . '" method="get" onsubmit="if ( !submitAuthorized ) return false;">'
-         . ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->page ) . '" />' : '' )
-         . ( $session->sid_super ? '<input type="hidden" name="auth"  value="' . $session->sid_super . '" />' : '')
-         . '<p>' . $lang->get('userfuncs_ml_lbl_finduser') . ' ' . $template->username_field('finduser') . ' <input type="submit" value="' . $lang->get('userfuncs_ml_btn_go') . '" /><br />
-            <small>' . $lang->get('userfuncs_ml_tip_wildcard') . '</small></p>'
-         . '</form>
-         </div>';
-  
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$template->header();
+	
+	$startletters = 'abcdefghijklmnopqrstuvwxyz';
+	$startletters = enano_str_split($startletters);
+	$startletter = ( isset($_GET['letter']) ) ? strtolower($_GET['letter']) : '';
+	if ( !in_array($startletter, $startletters) && $startletter != 'chr' )
+	{
+		$startletter = '';
+	}
+	
+	$startletter_sql = $startletter;
+	if ( $startletter == 'chr' )
+	{
+		$startletter_sql = '([^a-z])';
+	}
+	
+	// offset
+	$perpage = 25;
+	$page = (( isset($_GET['offset']) && strval(intval($_GET['offset'])) === $_GET['offset']) ? intval($_GET['offset']) : 1) - 1;
+	$offset = $page * $perpage;
+	
+	// sort order
+	$sortkeys = array(
+			'uid' => 'u.user_id',
+			'username' => 'u.username',
+			'email' => 'u.email',
+			'regist' => 'u.reg_time'
+		);
+	
+	$sortby = ( isset($_GET['sort']) && isset($sortkeys[$_GET['sort']]) ) ? $_GET['sort'] : 'username';
+	$sort_sqllet = $sortkeys[$sortby];
+	
+	$target_order = ( isset($_GET['orderby']) && in_array($_GET['orderby'], array('ASC', 'DESC')) )? $_GET['orderby'] : 'ASC';
+	
+	$sortorders = array();
+	foreach ( $sortkeys as $k => $_unused )
+	{
+		$sortorders[$k] = ( $sortby == $k ) ? ( $target_order == 'ASC' ? 'DESC' : 'ASC' ) : 'ASC';
+	}
+	
+	// Why 3.3714%? 100 percent / 28 cells, minus a little (0.2% / cell) to account for cell spacing
+	
+	echo '<div class="tblholder">
+					<table border="0" cellspacing="1" cellpadding="4" style="text-align: center;">
+						<tr>';
+	echo '<td class="row1" style="width: 3.3714%;"><a href="' . makeUrlNS('Special', 'Memberlist', 'letter=&sort=' . $sortby . '&orderby=' . $target_order, true) . '">All</a></td>';
+	echo '<td class="row1" style="width: 3.3714%;"><a href="' . makeUrlNS('Special', 'Memberlist', 'letter=chr&sort=' . $sortby . '&orderby=' . $target_order, true) . '">#</a></td>';
+	foreach ( $startletters as $letter )
+	{
+		echo '<td class="row1" style="width: 3.3714%;"><a href="' . makeUrlNS('Special', 'Memberlist', 'letter=' . $letter . '&sort=' . $sortby . '&orderby=' . $target_order, true) . '">' . strtoupper($letter) . '</a></td>';
+	}
+	echo '    </tr>
+					</table>
+				</div>';
+	
+	// User search             
+	if ( isset($_GET['finduser']) )
+	{
+		$finduser = str_replace(array(  '%',   '_'),
+														array('\\%', '\\_'),
+														$_GET['finduser']);
+		$finduser = str_replace(array('*', '?'),
+														array('%', '_'),
+														$finduser);
+		$finduser = $db->escape($finduser);
+		$username_where = ENANO_SQLFUNC_LOWERCASE . '(u.username) LIKE \'%' . strtolower($finduser) . '%\'';
+		$finduser_url = 'finduser=' . rawurlencode($_GET['finduser']) . '&';
+	}
+	else
+	{
+		if ( ENANO_DBLAYER == 'MYSQL' )
+			$username_where = 'lcase(u.username) REGEXP lcase("^' . $startletter_sql . '")';
+		else if ( ENANO_DBLAYER == 'PGSQL' )
+			$username_where = 'lower(u.username) ~ lower(\'^' . $startletter_sql . '\')';
+		$finduser_url = '';
+	}
+	
+	// Column markers
+	$headings = '<tr>
+ 								<th style="max-width: 50px;">
+ 									<a href="' . makeUrlNS('Special', 'Memberlist', $finduser_url . 'letter=' . $startletter . '&sort=uid&orderby=' . $sortorders['uid'], true) . '">#</a>
+ 								</th>
+ 								<th>
+ 									<a href="' . makeUrlNS('Special', 'Memberlist', $finduser_url . 'letter=' . $startletter . '&sort=username&orderby=' . $sortorders['username'], true) . '">' . $lang->get('userfuncs_ml_column_username') . '</a>
+ 								</th>
+ 								<th>
+ 									' . $lang->get('userfuncs_ml_column_userlevel') . '
+ 								</th>
+ 								<th>
+ 									<a href="' . makeUrlNS('Special', 'Memberlist', $finduser_url . 'letter=' . $startletter . '&sort=email&orderby=' . $sortorders['email'], true) . '">' . $lang->get('userfuncs_ml_column_email') . '</a>
+ 								</th>
+ 								<th>
+ 									<a href="' . makeUrlNS('Special', 'Memberlist', $finduser_url . 'letter=' . $startletter . '&sort=regist&orderby=' . $sortorders['regist'], true) . '">' . $lang->get('userfuncs_ml_column_regtime') . '</a>
+ 								</th>
+ 							</tr>';
+ 							
+	// determine number of rows
+	$q = $db->sql_query('SELECT COUNT(u.user_id) FROM '.table_prefix.'users AS u WHERE ' . $username_where . ' AND u.username != \'Anonymous\';');
+	if ( !$q )
+		$db->_die();
+	
+	list($num_rows) = $db->fetchrow_num();
+	$db->free_result();
+	
+	if ( !empty($finduser_url) )
+	{
+		switch ( $num_rows )
+		{
+			case 0:
+				$str = ''; /* $lang->get('userfuncs_ml_msg_matches_zero'); */ break;
+			case 1:
+				$str = $lang->get('userfuncs_ml_msg_matches_one'); break;
+			default:
+				$str = $lang->get('userfuncs_ml_msg_matches', array('matches' => $num_rows)); break;
+		}
+		echo "<h3>$str</h3>";
+	}
+	
+	// main selector
+	$pgsql_additional_group_by = ( ENANO_DBLAYER == 'PGSQL' ) ? ', u.username, u.reg_time, u.email, u.user_level, u.user_has_avatar, u.avatar_type, x.email_public' : '';
+	$q = $db->sql_query('SELECT \'\' AS infobit, u.user_id, u.username, u.reg_time, u.email, u.user_level, u.user_has_avatar, u.avatar_type, x.email_public, COUNT(c.comment_id) AS num_comments FROM '.table_prefix.'users AS u
+																		LEFT JOIN '.table_prefix.'users_extra AS x
+																			ON ( u.user_id = x.user_id )
+																		LEFT JOIN ' . table_prefix . 'comments AS c
+																			ON ( u.user_id = c.user_id )
+																		WHERE ' . $username_where . ' AND u.username != \'Anonymous\'
+																		GROUP BY u.user_id' . $pgsql_additional_group_by . '
+																		ORDER BY ' . $sort_sqllet . ' ' . $target_order . '
+																		LIMIT ' . $perpage . ' OFFSET ' . $offset . ';');
+	if ( !$q )
+		$db->_die();
+	
+	// formatter parameters
+	$formatter = new MemberlistFormatter();
+	$formatters = array(
+		'username' => array($formatter, 'username'),
+		'user_level' => array($formatter, 'user_level'),
+		'email' => array($formatter, 'email'),
+		'reg_time' => array($formatter, 'reg_time'),
+		'infobit' => array($formatter, 'infobit')
+		);
+	
+	$result_url = makeUrlNS('Special', 'Memberlist', ( str_replace('%', '%%', $finduser_url) ) . 'letter=' . $startletter . '&offset=%s&sort=' . $sortby . '&orderby=' . $target_order );
+	$paginator = generate_paginator($page, ceil($num_rows / $perpage), $result_url);
+	
+	if ( $num_rows > 0 )
+	{
+		if ( $num_rows > $perpage )
+			echo $paginator;
+		
+		echo '<div class="tblholder">
+								<table border="0" cellspacing="1" cellpadding="4" style="text-align: center;">
+									' . $headings;
+									
+		$i = 0;
+		while ( $row = $db->fetchrow($q) )
+		{
+			$i++;
+			$cls = ( $i % 2 == 0 ) ? 'row2' : 'row1';
+			echo '<tr>';
+			echo '<td class="' . $cls . '">' . $row['user_id'] . '</td>';
+			echo '<td class="' . $cls . '" style="text-align: left;">' . $formatter->username($row['username'], $row) . '</td>';
+			echo '<td class="' . $cls . '">' . $formatter->user_level($row['user_level'], $row) . '</td>';
+			echo '<td class="' . $cls . '">' . $formatter->email($row['email'], $row) . '</td>';
+			echo '<td class="' . $cls . '">' . $formatter->reg_time($row['reg_time'], $row) . '</td>';
+			echo '</tr>';
+			echo '<tr>';
+			echo '<td colspan="5" class="row3" style="text-align: left;">
+ 								<div id="ml_moreinfo_' . $row['user_id'] . '" style="display: none;">
+ 									' . $formatter->infobit(true, $row) . '
+ 								</div>
+ 							</td>';
+			echo '</tr>';
+		}
+		
+		echo '  ' . $headings . '
+ 								</table>
+							</div>
+							';
+		
+		if ( $num_rows > $perpage )
+			echo $paginator;
+	}
+	else
+	{
+		echo '<h2 class="emptymessage">' . $lang->get('log_msg_no_results') . '</h2>';
+	}
+	
+	echo '<div style="float: left;">
+					<form action="' . makeUrlNS('Special', 'Memberlist') . '" method="get" onsubmit="if ( !submitAuthorized ) return false;">'
+ 				. ( urlSeparator == '&' ? '<input type="hidden" name="title" value="' . htmlspecialchars( $paths->page ) . '" />' : '' )
+ 				. ( $session->sid_super ? '<input type="hidden" name="auth"  value="' . $session->sid_super . '" />' : '')
+ 				. '<p>' . $lang->get('userfuncs_ml_lbl_finduser') . ' ' . $template->username_field('finduser') . ' <input type="submit" value="' . $lang->get('userfuncs_ml_btn_go') . '" /><br />
+						<small>' . $lang->get('userfuncs_ml_tip_wildcard') . '</small></p>'
+ 				. '</form>
+ 				</div>';
+	
+	$template->footer();
 }
 
 /**
@@ -1755,153 +1755,153 @@
 
 class MemberlistFormatter
 {
-  function username($username, $row)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $userpage = $paths->nslist['User'] . sanitize_page_id($username);
-    $class = ( isPage($userpage) ) ? '' : ' class="wikilink-nonexistent"';
-    $anchor = '<a href="' . makeUrlNS('User', sanitize_page_id($username)) . '"' . $class . ' onclick="load_component(\'jquery\'); load_component(\'jquery-ui\'); var el = document.getElementById(\'ml_moreinfo_' . $row['user_id'] . '\'); $(el).toggle(\'blind\'); return false;">' . htmlspecialchars($username) . '</a>';
-    if ( $session->user_level >= USER_LEVEL_ADMIN )
-    {
-      $anchor .= ' <small>- <a href="' . makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'UserManager&src=get&username=' . urlencode($username), true) . '"
-                               onclick="ajaxAdminUser(\'' . addslashes(htmlspecialchars($username)) . '\'); return false;">' . $lang->get('userfuncs_ml_btn_adminuser') . '</a></small>';
-    }
-    return $anchor;
-  }
-  function user_level($level, $row)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    /*
-    switch ( $level )
-    {
-      case USER_LEVEL_GUEST:
-        $s_level = $lang->get('userfuncs_ml_level_guest'); break;
-      case USER_LEVEL_MEMBER:
-      case USER_LEVEL_CHPREF:
-        $s_level = $lang->get('userfuncs_ml_level_member'); break;
-      case USER_LEVEL_MOD:
-        $s_level = $lang->get('userfuncs_ml_level_mod'); break;
-      case USER_LEVEL_ADMIN:
-        $s_level = $lang->get('userfuncs_ml_level_admin'); break;
-      default:
-        $s_level = $lang->get('userfuncs_ml_level_unknown', array( 'level' => $level ));
-    }
-    */
-    
-    // TODO: Requested by mm3. Is this too CPU-intensive? Optimize?
-    //       Performance yield =/= about the same (but only 4 users under testing conditions)
-    $rankdata = $session->get_user_rank($row['user_id']);
-    $s_level = '<span style="' . $rankdata['rank_style'] . '">' . $lang->get($rankdata['rank_title']) . '</span>';
-    
-    return $s_level;
-  }
-  function email($addy, $row)
-  {
-    global $lang;
-    if ( $row['email_public'] == '1' )
-    {
-      global $email;
-      $addy = $email->encryptEmail($addy);
-      return $addy;
-    }
-    else
-    {
-      return '<small>&lt;' . $lang->get('userfuncs_ml_email_nonpublic') . '&gt;</small>';
-    }
-  }
-  /**
-   * Format a time as a reference to a day, with user-friendly "X days ago"/"Today"/"Yesterday" returned when relevant.
-   * @param int UNIX timestamp
-   * @return string
-   */
-  
-  public static function format_date($time)
-  {
-    // merged into enano_date() :)
-    return enano_date(ED_DATE, $time);
-  }
-  function reg_time($time, $row)
-  {
-    return $this->format_date($time);
-  }
-  function infobit($_, $row)
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    
-    $bit = '';
-    if ( $row['user_has_avatar'] == 1 )
-    {
-      $bit .= '<div style="float: left; margin-right: 10px;">
-        <img alt=" " src="' . make_avatar_url(intval($row['user_id']), $row['avatar_type'], $row['email']) . '" />
-      </div>';
-    }
-    $rank_data = $session->get_user_rank(intval($row['user_id']));
-    $userpage = $paths->nslist['User'] . sanitize_page_id($row['username']);
-    $title = ( isPage($userpage) ) ? ' title="' . $lang->get('userfuncs_ml_tip_userpage') . '"' : ' title="' . $lang->get('userfuncs_ml_tip_nouserpage') . '"';
-    $bit .= '<a' . $title . ' href="' . makeUrlNS('User', $row['username'], false, true) . '" style="font-size: x-large; ' . $rank_data['rank_style'] . '">' . htmlspecialchars($row['username']) . '</a><br />';
-    if ( $rank_data['user_title'] )
-      $bit .= htmlspecialchars($rank_data['user_title']) . '<br />';
-    if ( $rank_data['rank_title'] )
-      $bit .= '<small>' . htmlspecialchars($lang->get($rank_data['rank_title'])) . '</small><br />';
-    
-    $bit .= '<div style="text-align: right;">
-               <a href="' . makeUrlNS('Special', "PrivateMessages/Compose/To/{$row['username']}", false, true) . '" class="abutton icon abutton_blue" style="background-image: url(' . cdnPath . '/images/icons/send_pm.png);">' . $lang->get('comment_btn_send_privmsg') . '</a>
-               <a href="' . makeUrlNS('Special', "PrivateMessages/FriendList/Add/{$row['username']}", false, true) . '" class="abutton icon abutton_green" style="background-image: url(' . cdnPath . '/images/icons/add_buddy.png);">' . $lang->get('comment_btn_add_buddy') . '</a>
-             </div>';
-    
-    return $bit;
-  }
+	function username($username, $row)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$userpage = $paths->nslist['User'] . sanitize_page_id($username);
+		$class = ( isPage($userpage) ) ? '' : ' class="wikilink-nonexistent"';
+		$anchor = '<a href="' . makeUrlNS('User', sanitize_page_id($username)) . '"' . $class . ' onclick="load_component(\'jquery\'); load_component(\'jquery-ui\'); var el = document.getElementById(\'ml_moreinfo_' . $row['user_id'] . '\'); $(el).toggle(\'blind\'); return false;">' . htmlspecialchars($username) . '</a>';
+		if ( $session->user_level >= USER_LEVEL_ADMIN )
+		{
+			$anchor .= ' <small>- <a href="' . makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'UserManager&src=get&username=' . urlencode($username), true) . '"
+ 															onclick="ajaxAdminUser(\'' . addslashes(htmlspecialchars($username)) . '\'); return false;">' . $lang->get('userfuncs_ml_btn_adminuser') . '</a></small>';
+		}
+		return $anchor;
+	}
+	function user_level($level, $row)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		/*
+		switch ( $level )
+		{
+			case USER_LEVEL_GUEST:
+				$s_level = $lang->get('userfuncs_ml_level_guest'); break;
+			case USER_LEVEL_MEMBER:
+			case USER_LEVEL_CHPREF:
+				$s_level = $lang->get('userfuncs_ml_level_member'); break;
+			case USER_LEVEL_MOD:
+				$s_level = $lang->get('userfuncs_ml_level_mod'); break;
+			case USER_LEVEL_ADMIN:
+				$s_level = $lang->get('userfuncs_ml_level_admin'); break;
+			default:
+				$s_level = $lang->get('userfuncs_ml_level_unknown', array( 'level' => $level ));
+		}
+		*/
+		
+		// TODO: Requested by mm3. Is this too CPU-intensive? Optimize?
+		//       Performance yield =/= about the same (but only 4 users under testing conditions)
+		$rankdata = $session->get_user_rank($row['user_id']);
+		$s_level = '<span style="' . $rankdata['rank_style'] . '">' . $lang->get($rankdata['rank_title']) . '</span>';
+		
+		return $s_level;
+	}
+	function email($addy, $row)
+	{
+		global $lang;
+		if ( $row['email_public'] == '1' )
+		{
+			global $email;
+			$addy = $email->encryptEmail($addy);
+			return $addy;
+		}
+		else
+		{
+			return '<small>&lt;' . $lang->get('userfuncs_ml_email_nonpublic') . '&gt;</small>';
+		}
+	}
+	/**
+ 	* Format a time as a reference to a day, with user-friendly "X days ago"/"Today"/"Yesterday" returned when relevant.
+ 	* @param int UNIX timestamp
+ 	* @return string
+ 	*/
+	
+	public static function format_date($time)
+	{
+		// merged into enano_date() :)
+		return enano_date(ED_DATE, $time);
+	}
+	function reg_time($time, $row)
+	{
+		return $this->format_date($time);
+	}
+	function infobit($_, $row)
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		
+		$bit = '';
+		if ( $row['user_has_avatar'] == 1 )
+		{
+			$bit .= '<div style="float: left; margin-right: 10px;">
+				<img alt=" " src="' . make_avatar_url(intval($row['user_id']), $row['avatar_type'], $row['email']) . '" />
+			</div>';
+		}
+		$rank_data = $session->get_user_rank(intval($row['user_id']));
+		$userpage = $paths->nslist['User'] . sanitize_page_id($row['username']);
+		$title = ( isPage($userpage) ) ? ' title="' . $lang->get('userfuncs_ml_tip_userpage') . '"' : ' title="' . $lang->get('userfuncs_ml_tip_nouserpage') . '"';
+		$bit .= '<a' . $title . ' href="' . makeUrlNS('User', $row['username'], false, true) . '" style="font-size: x-large; ' . $rank_data['rank_style'] . '">' . htmlspecialchars($row['username']) . '</a><br />';
+		if ( $rank_data['user_title'] )
+			$bit .= htmlspecialchars($rank_data['user_title']) . '<br />';
+		if ( $rank_data['rank_title'] )
+			$bit .= '<small>' . htmlspecialchars($lang->get($rank_data['rank_title'])) . '</small><br />';
+		
+		$bit .= '<div style="text-align: right;">
+ 							<a href="' . makeUrlNS('Special', "PrivateMessages/Compose/To/{$row['username']}", false, true) . '" class="abutton icon abutton_blue" style="background-image: url(' . cdnPath . '/images/icons/send_pm.png);">' . $lang->get('comment_btn_send_privmsg') . '</a>
+ 							<a href="' . makeUrlNS('Special', "PrivateMessages/FriendList/Add/{$row['username']}", false, true) . '" class="abutton icon abutton_green" style="background-image: url(' . cdnPath . '/images/icons/add_buddy.png);">' . $lang->get('comment_btn_add_buddy') . '</a>
+ 						</div>';
+		
+		return $bit;
+	}
 }
 
 function page_Special_LangExportJSON()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $lang_id = ( $x = $paths->getParam(0) ) ? intval($x) : $lang->lang_id;
-  
-  if ( $lang->lang_id == $lang_id )
-    $lang_local =& $lang;
-  else
-    $lang_local = new Language($lang_id);
-    
-  $lang_local->get('meta_meta');
-  
-  $lang_strings = enano_json_encode($lang_local->strings);
-  $etag = substr(sha1($lang_strings), 0, 20) . '-' . dechex($lang_local->lang_timestamp);
-  
-  if ( isset($_SERVER['HTTP_IF_NONE_MATCH']) )
-  {
-    if ( "\"$etag\"" == $_SERVER['HTTP_IF_NONE_MATCH'] )
-    {
-      header('HTTP/1.1 304 Not Modified');
-      exit();
-    }
-  }
-  
-  $timestamp = enano_date('D, j M Y H:i:s T', $lang_local->lang_timestamp);
-  // generate expires header
-  $expires = date('r', mktime(-1, -1, -1, -1, -1, intval(date('y'))+1));
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$lang_id = ( $x = $paths->getParam(0) ) ? intval($x) : $lang->lang_id;
+	
+	if ( $lang->lang_id == $lang_id )
+		$lang_local =& $lang;
+	else
+		$lang_local = new Language($lang_id);
+		
+	$lang_local->get('meta_meta');
+	
+	$lang_strings = enano_json_encode($lang_local->strings);
+	$etag = substr(sha1($lang_strings), 0, 20) . '-' . dechex($lang_local->lang_timestamp);
+	
+	if ( isset($_SERVER['HTTP_IF_NONE_MATCH']) )
+	{
+		if ( "\"$etag\"" == $_SERVER['HTTP_IF_NONE_MATCH'] )
+		{
+			header('HTTP/1.1 304 Not Modified');
+			exit();
+		}
+	}
+	
+	$timestamp = enano_date('D, j M Y H:i:s T', $lang_local->lang_timestamp);
+	// generate expires header
+	$expires = date('r', mktime(-1, -1, -1, -1, -1, intval(date('y'))+1));
 
-  header("Last-Modified: $timestamp");
-  header("Date: $timestamp");
-  header("ETag: \"$etag\"");
-  header('Content-type: text/javascript');
-  header("Expires: $expires");
-  
-  $lang_local->fetch();
-  echo "if ( typeof(enano_lang) != 'object' )
-  var enano_lang = new Object();
+	header("Last-Modified: $timestamp");
+	header("Date: $timestamp");
+	header("ETag: \"$etag\"");
+	header('Content-type: text/javascript');
+	header("Expires: $expires");
+	
+	$lang_local->fetch();
+	echo "if ( typeof(enano_lang) != 'object' )
+	var enano_lang = new Object();
 
 enano_lang[{$lang_local->lang_id}] = " . $lang_strings . ";";
 
-  gzip_output();
-  
-  exit(0);
+	gzip_output();
+	
+	exit(0);
 }
 
 /**
@@ -1910,96 +1910,96 @@
 
 function page_Special_Avatar()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $aggressive_optimize_html;
-  $aggressive_optimize_html = false;
-  
-  $img_types = array(
-      IMAGE_TYPE_PNG => 'png',
-      IMAGE_TYPE_GIF => 'gif',
-      IMAGE_TYPE_JPG => 'jpg',
-      IMAGE_TYPE_GRV => 'grv'
-    );
-  
-  $avi_id = $paths->getParam(0);
-  if ( !$avi_id || !@preg_match('/^[a-f0-9]+$/', $avi_id) )
-  {
-    echo 'Doesn\'t match the regexp';
-    return true;
-  }
-  
-  $avi_id_dec = hexdecode($avi_id);
-  $avi_id_dec = @unpack('Vdate/Vuid/vimg_type', $avi_id_dec);
-  if ( !$avi_id_dec )
-  {
-    echo 'Bad unpack';
-    return true;
-  }
-  
-  // check parameters
-  if ( !isset($img_types[$avi_id_dec['img_type']]) )
-  {
-    echo 'Invalid image type';
-    return true;
-  }
-  
-  // build file path
-  $avi_type = $img_types[$avi_id_dec['img_type']];
-  
-  // is this a gravatar?
-  if ( $avi_type == 'grv' )
-  {
-    // yes, we'll have to redirect
-    // sanitize UID
-    $uid = intval($avi_id_dec['uid']);
-    
-    // fetch email
-    $q = $db->sql_query('SELECT email FROM ' . table_prefix . "users WHERE user_id = $uid;");
-    if ( !$q )
-      $db->_die();
-    if ( $db->numrows() < 1 )
-      return false;
-    
-    list($email) = $db->fetchrow_num();
-    $db->free_result();
-    
-    $url = make_gravatar_url($url);
-    
-    // ship out the redirect
-    header('HTTP/1.1 302 Permanent Redirect');
-    header("Location: $url");
-  }
-  
-  $avi_path = ENANO_ROOT . '/' . getConfig('avatar_directory') . '/' . $avi_id_dec['uid'] . '.' . $avi_type;
-  if ( file_exists($avi_path) )
-  {
-    $avi_mod_time = @filemtime($avi_path);
-    $avi_mod_time = date('r', $avi_mod_time);
-    $avi_size = @filesize($avi_path);
-    header("Last-Modified: $avi_mod_time");
-    header("Content-Length: $avi_size");
-    header("Content-Type: image/$avi_type");
-    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
-    header("Cache-Control: public");
-    // expire it 30 days from now
-    $expiry_time = time() + ( 86400 * 30 );
-    header("Expires: " . date('r', $expiry_time));
-    
-    $fh = @fopen($avi_path, 'r');
-    if ( !$fh )
-    {
-      echo 'Could not open file';
-      return true;
-    }
-    
-    while ( $fd = @fread($fh, 1024) )
-    {
-      echo $fd;
-    }
-    fclose($fh);
-    
-  }
-  return true;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $aggressive_optimize_html;
+	$aggressive_optimize_html = false;
+	
+	$img_types = array(
+			IMAGE_TYPE_PNG => 'png',
+			IMAGE_TYPE_GIF => 'gif',
+			IMAGE_TYPE_JPG => 'jpg',
+			IMAGE_TYPE_GRV => 'grv'
+		);
+	
+	$avi_id = $paths->getParam(0);
+	if ( !$avi_id || !@preg_match('/^[a-f0-9]+$/', $avi_id) )
+	{
+		echo 'Doesn\'t match the regexp';
+		return true;
+	}
+	
+	$avi_id_dec = hexdecode($avi_id);
+	$avi_id_dec = @unpack('Vdate/Vuid/vimg_type', $avi_id_dec);
+	if ( !$avi_id_dec )
+	{
+		echo 'Bad unpack';
+		return true;
+	}
+	
+	// check parameters
+	if ( !isset($img_types[$avi_id_dec['img_type']]) )
+	{
+		echo 'Invalid image type';
+		return true;
+	}
+	
+	// build file path
+	$avi_type = $img_types[$avi_id_dec['img_type']];
+	
+	// is this a gravatar?
+	if ( $avi_type == 'grv' )
+	{
+		// yes, we'll have to redirect
+		// sanitize UID
+		$uid = intval($avi_id_dec['uid']);
+		
+		// fetch email
+		$q = $db->sql_query('SELECT email FROM ' . table_prefix . "users WHERE user_id = $uid;");
+		if ( !$q )
+			$db->_die();
+		if ( $db->numrows() < 1 )
+			return false;
+		
+		list($email) = $db->fetchrow_num();
+		$db->free_result();
+		
+		$url = make_gravatar_url($url);
+		
+		// ship out the redirect
+		header('HTTP/1.1 302 Permanent Redirect');
+		header("Location: $url");
+	}
+	
+	$avi_path = ENANO_ROOT . '/' . getConfig('avatar_directory') . '/' . $avi_id_dec['uid'] . '.' . $avi_type;
+	if ( file_exists($avi_path) )
+	{
+		$avi_mod_time = @filemtime($avi_path);
+		$avi_mod_time = date('r', $avi_mod_time);
+		$avi_size = @filesize($avi_path);
+		header("Last-Modified: $avi_mod_time");
+		header("Content-Length: $avi_size");
+		header("Content-Type: image/$avi_type");
+		// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
+		header("Cache-Control: public");
+		// expire it 30 days from now
+		$expiry_time = time() + ( 86400 * 30 );
+		header("Expires: " . date('r', $expiry_time));
+		
+		$fh = @fopen($avi_path, 'r');
+		if ( !$fh )
+		{
+			echo 'Could not open file';
+			return true;
+		}
+		
+		while ( $fd = @fread($fh, 1024) )
+		{
+			echo $fd;
+		}
+		fclose($fh);
+		
+	}
+	return true;
 }
 
 ?>
\ No newline at end of file
--- a/plugins/SpecialUserPrefs.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/SpecialUserPrefs.php	Sun Mar 28 23:10:46 2010 -0400
@@ -1,12 +1,12 @@
 <?php
 /**!info**
 {
-  "Plugin Name"  : "plugin_specialuserprefs_title",
-  "Plugin URI"   : "http://enanocms.org/",
-  "Description"  : "plugin_specialuserprefs_desc",
-  "Author"       : "Dan Fuhry",
-  "Version"      : "1.1.6",
-  "Author URI"   : "http://enanocms.org/"
+	"Plugin Name"  : "plugin_specialuserprefs_title",
+	"Plugin URI"   : "http://enanocms.org/",
+	"Description"  : "plugin_specialuserprefs_desc",
+	"Author"       : "Dan Fuhry",
+	"Version"      : "1.1.6",
+	"Author URI"   : "http://enanocms.org/"
 }
 **!*/
 
@@ -25,1143 +25,1143 @@
 $userprefs_menu_links = Array();
 function userprefs_menu_add($section, $text, $link)
 {
-  global $userprefs_menu;
-  if ( isset($userprefs_menu[$section]) && is_array($userprefs_menu[$section]) )
-  {
-    $userprefs_menu[$section][] = Array(
-      'text' => $text,
-      'link' => $link
-      );
-  }
-  else
-  {
-    $userprefs_menu[$section] = Array(Array(
-      'text' => $text,
-      'link' => $link
-      ));
-  }
+	global $userprefs_menu;
+	if ( isset($userprefs_menu[$section]) && is_array($userprefs_menu[$section]) )
+	{
+		$userprefs_menu[$section][] = Array(
+			'text' => $text,
+			'link' => $link
+			);
+	}
+	else
+	{
+		$userprefs_menu[$section] = Array(Array(
+			'text' => $text,
+			'link' => $link
+			));
+	}
 }
 
 $plugins->attachHook('tpl_compile_sidebar', 'userprefs_jbox_setup($button, $tb, $menubtn);');
 
 function userprefs_jbox_setup(&$button, &$tb, &$menubtn)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  if ( $paths->namespace != 'Special' || $paths->page_id != 'Preferences' )
-    return false;
-  
-  $tb .= "<ul>$template->toolbar_menu</ul>";
-  $template->toolbar_menu = '';
-  
-  $button->assign_vars(array(
-      'TEXT' => $lang->get('usercp_btn_memberlist'),
-      'FLAGS' => '',
-      'PARENTFLAGS' => '',
-      'HREF' => makeUrlNS('Special', 'Memberlist')
-    ));
-  
-  $tb .= $button->run();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	if ( $paths->namespace != 'Special' || $paths->page_id != 'Preferences' )
+		return false;
+	
+	$tb .= "<ul>$template->toolbar_menu</ul>";
+	$template->toolbar_menu = '';
+	
+	$button->assign_vars(array(
+			'TEXT' => $lang->get('usercp_btn_memberlist'),
+			'FLAGS' => '',
+			'PARENTFLAGS' => '',
+			'HREF' => makeUrlNS('Special', 'Memberlist')
+		));
+	
+	$tb .= $button->run();
 }
 
 function userprefs_menu_html()
 {
-  global $userprefs_menu;
-  global $userprefs_menu_links;
-  global $lang;
-  
-  $html = '';
-  $quot = '"';
-  
-  foreach ( $userprefs_menu as $section => $buttons )
-  {
-    $section_name = $section;
-    if ( preg_match('/^[a-z]+_[a-z_]+$/', $section) )
-    {
-      $section_name = $lang->get($section_name);
-    }
-    $html .= ( isset($userprefs_menu_links[$section]) ) ? "<a href={$quot}{$userprefs_menu_links[$section]}{$quot}>{$section_name}</a>\n        " : "<a>{$section_name}</a>\n        ";
-    $html .= "<ul>\n          ";
-    foreach ( $buttons as $button )
-    {
-      $buttontext = $button['text'];
-      if ( preg_match('/^[a-z]+_[a-z_]+$/', $buttontext) )
-      {
-        $buttontext = $lang->get($buttontext);
-      }
-      $html .= "  <li><a href={$quot}{$button['link']}{$quot}>{$buttontext}</a></li>\n          ";
-    }
-    $html .= "</ul>\n        ";
-  }
-  
-  return $html;
+	global $userprefs_menu;
+	global $userprefs_menu_links;
+	global $lang;
+	
+	$html = '';
+	$quot = '"';
+	
+	foreach ( $userprefs_menu as $section => $buttons )
+	{
+		$section_name = $section;
+		if ( preg_match('/^[a-z]+_[a-z_]+$/', $section) )
+		{
+			$section_name = $lang->get($section_name);
+		}
+		$html .= ( isset($userprefs_menu_links[$section]) ) ? "<a href={$quot}{$userprefs_menu_links[$section]}{$quot}>{$section_name}</a>\n        " : "<a>{$section_name}</a>\n        ";
+		$html .= "<ul>\n          ";
+		foreach ( $buttons as $button )
+		{
+			$buttontext = $button['text'];
+			if ( preg_match('/^[a-z]+_[a-z_]+$/', $buttontext) )
+			{
+				$buttontext = $lang->get($buttontext);
+			}
+			$html .= "  <li><a href={$quot}{$button['link']}{$quot}>{$buttontext}</a></li>\n          ";
+		}
+		$html .= "</ul>\n        ";
+	}
+	
+	return $html;
 }
 
 function userprefs_show_menu()
 {
-  echo '<div class="menu_nojs">
-          ' . userprefs_menu_html() . '
-          <span class="menuclear"></span>
-        </div>
-        <br />
-        ';
+	echo '<div class="menu_nojs">
+					' . userprefs_menu_html() . '
+					<span class="menuclear"></span>
+				</div>
+				<br />
+				';
 }
 
 function userprefs_menu_init()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $userprefs_menu_links;
-  
-  userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_emailpassword', makeUrlNS('Special', 'Preferences/EmailPassword') . '" onclick="ajaxLoginNavTo(\'Special\', \'Preferences/EmailPassword\', '.USER_LEVEL_CHPREF.'); return false;');
-  userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_signature', makeUrlNS('Special', 'Preferences/Signature'));
-  // userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_publicinfo', makeUrlNS('Special', 'Preferences/Profile'));
-  userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_usergroups', makeUrlNS('Special', 'Usergroups'));
-  if ( getConfig('avatar_enable') == '1' )
-  {
-    userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_avatar', makeUrlNS('Special', 'Preferences/Avatar'));
-  }
-  userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_inbox', makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'));
-  userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_outbox', makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'));
-  userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_sent', makeUrlNS('Special', 'PrivateMessages/Folder/Sent'));
-  userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_drafts', makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'));
-  userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_archive', makeUrlNS('Special', 'PrivateMessages/Folder/Archive'));
-  
-  /*
-  // Reserved for Enano's Next Big Innovation.(TM)
-  userprefs_menu_add('Private messages', 'Inbox', makeUrlNS('Special',      'Private_Messages#folder:inbox'));
-  userprefs_menu_add('Private messages', 'Starred', makeUrlNS('Special',     'Private_Messages#folder:starred'));
-  userprefs_menu_add('Private messages', 'Sent items', makeUrlNS('Special', 'Private_Messages#folder:sent'));
-  userprefs_menu_add('Private messages', 'Drafts', makeUrlNS('Special',     'Private_Messages#folder:drafts'));
-  userprefs_menu_add('Private messages', 'Archive', makeUrlNS('Special',    'Private_Messages#folder:archive'));
-  userprefs_menu_add('Private messages', 'Trash', makeUrlNS('Special',    'Private_Messages#folder:trash'));
-  */
-  
-  $userprefs_menu_links['usercp_sec_profile'] = makeUrlNS('Special', 'Preferences');
-  $userprefs_menu_links['usercp_sec_pm']  = makeUrlNS('Special', 'PrivateMessages');
-  
-  $code = $plugins->setHook('userprefs_jbox');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $userprefs_menu_links;
+	
+	userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_emailpassword', makeUrlNS('Special', 'Preferences/EmailPassword') . '" onclick="ajaxLoginNavTo(\'Special\', \'Preferences/EmailPassword\', '.USER_LEVEL_CHPREF.'); return false;');
+	userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_signature', makeUrlNS('Special', 'Preferences/Signature'));
+	// userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_publicinfo', makeUrlNS('Special', 'Preferences/Profile'));
+	userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_usergroups', makeUrlNS('Special', 'Usergroups'));
+	if ( getConfig('avatar_enable') == '1' )
+	{
+		userprefs_menu_add('usercp_sec_profile', 'usercp_sec_profile_avatar', makeUrlNS('Special', 'Preferences/Avatar'));
+	}
+	userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_inbox', makeUrlNS('Special', 'PrivateMessages/Folder/Inbox'));
+	userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_outbox', makeUrlNS('Special', 'PrivateMessages/Folder/Outbox'));
+	userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_sent', makeUrlNS('Special', 'PrivateMessages/Folder/Sent'));
+	userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_drafts', makeUrlNS('Special', 'PrivateMessages/Folder/Drafts'));
+	userprefs_menu_add('usercp_sec_pm', 'usercp_sec_pm_archive', makeUrlNS('Special', 'PrivateMessages/Folder/Archive'));
+	
+	/*
+	// Reserved for Enano's Next Big Innovation.(TM)
+	userprefs_menu_add('Private messages', 'Inbox', makeUrlNS('Special',      'Private_Messages#folder:inbox'));
+	userprefs_menu_add('Private messages', 'Starred', makeUrlNS('Special',     'Private_Messages#folder:starred'));
+	userprefs_menu_add('Private messages', 'Sent items', makeUrlNS('Special', 'Private_Messages#folder:sent'));
+	userprefs_menu_add('Private messages', 'Drafts', makeUrlNS('Special',     'Private_Messages#folder:drafts'));
+	userprefs_menu_add('Private messages', 'Archive', makeUrlNS('Special',    'Private_Messages#folder:archive'));
+	userprefs_menu_add('Private messages', 'Trash', makeUrlNS('Special',    'Private_Messages#folder:trash'));
+	*/
+	
+	$userprefs_menu_links['usercp_sec_profile'] = makeUrlNS('Special', 'Preferences');
+	$userprefs_menu_links['usercp_sec_pm']  = makeUrlNS('Special', 'PrivateMessages');
+	
+	$code = $plugins->setHook('userprefs_jbox');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
 }
 
 $plugins->attachHook('common_post', 'userprefs_menu_init();');
 
 function page_Special_Preferences()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $timezone;
-  global $cache;
-  
-  // We need a login to continue
-  if ( !$session->user_logged_in )
-    redirect(makeUrlNS('Special', 'Login/' . $paths->page), 'Login required', 'You need to be logged in to access this page. Please wait while you are redirected to the login page.');
-  
-  // User ID - later this will be specified on the URL, but hardcoded for now
-  $uid = intval($session->user_id);
-  
-  // Instanciate the AES encryptor
-  $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-  
-  // Basic user info
-  $q = $db->sql_query('SELECT username, password, email, real_name, signature, theme, style FROM '.table_prefix.'users WHERE user_id='.$uid.';');
-  if ( !$q )
-    $db->_die();
-  
-  $row = $db->fetchrow();
-  $db->free_result();
-  
-  $section = $paths->getParam(0);
-  if ( !$section )
-  {
-    $section = 'Home';
-  }
-  
-  $errors = '';
-  
-  switch ( $section )
-  {
-    case 'Avatar':
-      $template->preload_js('jquery');
-      $template->preload_js('jquery-ui');
-      break;
-    case 'EmailPassword':
-      // Require elevated privileges (well sortof)
-      if ( $session->auth_level < USER_LEVEL_CHPREF )
-      {
-        redirect(makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . USER_LEVEL_CHPREF, true), 'Authentication required', 'You need to re-authenticate to access this page.', 0);
-      }
-      
-      if ( isset($_POST['submit']) )
-      {
-        $email_changed = false;
-        // First do the e-mail address
-        if ( strlen($_POST['newemail']) > 0 )
-        {
-          switch('foo') // Same reason as in the password code...
-          {
-            case 'foo':
-              if ( $_POST['newemail'] != $_POST['newemail_conf'] )
-              {
-                $errors .= '<div class="error-box">' . $lang->get('usercp_emailpassword_err_email_no_match') . '</div>';
-                break;
-              }
-          }
-          $q = $db->sql_query('SELECT password FROM '.table_prefix.'users WHERE user_id='.$session->user_id.';');
-          if ( !$q )
-            $db->_die();
-          $row = $db->fetchrow();
-          $db->free_result();
-          
-          $new_email = $_POST['newemail'];
-          
-          $result = $session->change_email($session->user_id, $new_email);
-          if ( $result != 'success' )
-          {
-            $message = '<p>' . $lang->get('usercp_emailpassword_err_list') . '</p>';
-            $message .= '<ul><li>' . implode("</li>\n<li>", $result) . '</li></ul>';
-            die_friendly($lang->get('usercp_emailpassword_err_title'), $message);
-          }
-          $email_changed = true;
-        }
-        // Obtain password
-        if ( !empty($_POST['crypt_data']) || !empty($_POST['newpass']) || $session->password_change_disabled )
-        {
-          $newpass = $session->password_change_disabled ? '' : $session->get_aes_post('newpass');
-          // At this point we know if we _want_ to change the password...
-          
-          // We can't check the password to see if it matches the confirmation
-          // because the confirmation was destroyed during the encryption. I figured
-          // this wasn't a big deal because if the encryption worked, then either
-          // the Javascript validated it or the user hacked the form. In the latter
-          // case, if he's smart enough to hack the encryption code, he's probably
-          // smart enough to remember his password.
-          
-          if ( strlen($newpass) > 0 )
-          {
-            if ( defined('ENANO_DEMO_MODE') )
-              $errors .= '<div class="error-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_emailpassword_err_demo') . '</div>';
-            // Perform checks
-            if ( strlen($newpass) < 6 )
-              $errors .= '<div class="error-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_emailpassword_err_password_too_short') . '</div>';
-            if ( getConfig('pw_strength_enable') == '1' )
-            {
-              $score_inp = password_score($newpass);
-              $score_min = getConfig('pw_strength_minimum', -10);
-              if ( $score_inp < $score_min )
-                $errors .= '<div class="error-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_emailpassword_err_password_too_weak', array('score' => $score_inp)) . '</div>';
-            }
-            if ( $_POST['use_crypt'] == 'no' && $newpass != $_POST['newpass_confirm'] )
-            {
-              $errors .= '<div class="error-box">' . $lang->get('usercp_emailpassword_err_password_no_match') . '</div>';
-            }
-            // Encrypt new password
-            if ( empty($errors) )
-            {
-              // Perform the swap
-              $session->set_password($session->username, $newpass);
-              // Log out and back in
-              $username = $session->username;
-              $session->logout();
-              if ( $email_changed )
-              {
-                if ( getConfig('account_activation') == 'user' )
-                {
-                  redirect(makeUrl(get_main_page()), $lang->get('usercp_emailpassword_msg_profile_success'), $lang->get('usercp_emailpassword_msg_need_activ_user'), 20);
-                }
-                else if ( getConfig('account_activation') == 'admin' )
-                {
-                  redirect(makeUrl(get_main_page()), $lang->get('usercp_emailpassword_msg_profile_success'), $lang->get('usercp_emailpassword_msg_need_activ_admin'), 20);
-                }
-              }
-              $session->login_without_crypto($username, $newpass);
-              redirect(makeUrlNS('Special', 'Preferences'), $lang->get('usercp_emailpassword_msg_pass_success'), $lang->get('usercp_emailpassword_msg_password_changed'), 5);
-            }
-          }
-          else if ( $email_changed )
-          {
-            $session->logout(USER_LEVEL_CHPREF);
-            $activation = $session->user_level >= USER_LEVEL_MOD ? 'none' : getConfig('account_activation', 'none');
-            switch($activation)
-            {
-              default:
-                $message_body = $lang->get('usercp_emailpassword_msg_password_changed');
-                $timeout = 5;
-                break;
-              case 'admin':
-                $message_body = $lang->get('usercp_emailpassword_msg_need_activ_user');
-                $timeout = 20;
-                break;
-              case 'user':
-                $message_body = $lang->get('usercp_emailpassword_msg_need_activ_admin');
-                $timeout = 20;
-                break;
-            }
-            redirect(makeUrlNS('Special', 'Preferences'), $lang->get('usercp_emailpassword_msg_email_success'), $message_body, $timeout);
-          }
-        }
-      }
-      $template->tpl_strings['PAGE_NAME'] = $lang->get('usercp_emailpassword_title');
-      break;
-    case 'Signature':
-      $template->tpl_strings['PAGE_NAME'] = $lang->get('usercp_signature_title');
-      break;
-    case 'Profile':
-    case 'Home':
-      if ( isset($_POST['submit']) )
-        csrf_request_confirm();
-      
-      $template->tpl_strings['PAGE_NAME'] = $lang->get('usercp_publicinfo_title');
-      break;
-  }
-  
-  $template->header();
-  
-  // Output the menu
-  // This is not templatized because it conforms to the jBox menu standard.
-  
-  userprefs_show_menu();
-  
-  switch ( $section )
-  {
-    case 'EmailPassword':
-      
-      $errors = trim($errors);
-      if ( !empty($errors) )
-      {
-        echo $errors;
-      }
-      
-      echo '<form action="' . makeUrlNS('Special', 'Preferences/EmailPassword') . '" method="post" onsubmit="return runEncryption();" name="empwform" >';
-      echo '<fieldset>';
-      echo '<legend>' . $lang->get('usercp_emailpassword_grp_chpasswd') . '</legend>';
-      
-      // Password change form
-      if ( $session->password_change_disabled )
-      {
-        echo '<p>' . $lang->get('usercp_emailpassword_msg_change_disabled') . '</p>';
-        if ( $session->password_change_dest['url'] )
-        {
-          echo '<p>' . $lang->get('usercp_emailpassword_msg_change_disabled_url') . '
-                   <a onclick="window.open(this.href); return false;" href="' . htmlspecialchars($session->password_change_dest['url']) . '">' . htmlspecialchars($session->password_change_dest['title']) . '</a></p>';
-        }
-      }
-      else
-      {
-      echo $lang->get('usercp_emailpassword_field_newpass') . '<br />
-                <input type="password" name="newpass" size="30" tabindex="1" ' . ( getConfig('pw_strength_enable') == '1' ? 'onkeyup="password_score_field(this);" ' : '' ) . '/>' . ( getConfig('pw_strength_enable') == '1' ? '<span class="password-checker" style="font-weight: bold; color: #aaaaaa;"> Loading...</span>' : '' ) . '
-              <br />
-              <br />
-              ' . $lang->get('usercp_emailpassword_field_newpass_confirm') . '<br />
-              <input type="password" name="newpass_confirm" size="30" tabindex="2" />
-              ' . ( getConfig('pw_strength_enable') == '1' ? '<br /><br /><div id="pwmeter"></div>
-              <small>' . $lang->get('usercp_emailpassword_msg_password_min_score') . '</small>' : '' );
-      }
-      echo '</fieldset><br />';
-      echo '<fieldset>
-        <legend>' . $lang->get('usercp_emailpassword_grp_chemail') . '</legend>
-        ' . $lang->get('usercp_emailpassword_field_newemail') . '<br />
-          <input type="text" value="' . ( isset($_POST['newemail']) ? htmlspecialchars($_POST['newemail']) : '' ) . '" name="newemail" size="30" tabindex="3" />
-        <br />
-        <br />
-        ' . $lang->get('usercp_emailpassword_field_newemail_confirm') . '<br />
-          <input type="text" value="' . ( isset($_POST['newemail']) ? htmlspecialchars($_POST['newemail']) : '' ) . '" name="newemail_conf" size="30" tabindex="4" />
-      </fieldset>
-      <br />
-      <div style="text-align: right;"><input type="submit" name="submit" value="' . $lang->get('etc_save_changes') . '" tabindex="5" /></div>';
-      
-      if ( !$session->password_change_disabled )
-        echo $session->generate_aes_form();
-      
-      echo '</form>';
-      
-      // ENCRYPTION CODE
-      ?>
-      <?php if ( !$session->password_change_disabled && getConfig('pw_strength_enable') == '1' ): ?>
-      <script type="text/javascript">
-      addOnloadHook(function()
-        {
-          password_score_field(document.forms.empwform.newpass);
-        });
-      </script>
-      <?php endif; ?>
-      <?php
-      echo $session->aes_javascript('empwform', 'newpass');
-      break;
-    case 'Signature':
-      if ( isset($_POST['new_sig']) )
-      {
-        $sig = $_POST['new_sig'];
-        $sig = RenderMan::preprocess_text($sig, true, false);
-        $sql_sig = $db->escape($sig);
-        $q = $db->sql_query('UPDATE '.table_prefix.'users SET signature=\'' . $sql_sig . '\' WHERE user_id=' . $session->user_id . ';');
-        if ( !$q )
-          $db->_die();
-        $session->signature = $sig;
-        echo '<div class="info-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_signature_msg_saved') . '</div>';
-      }
-      echo '<form action="'.makeUrl($paths->fullpage).'" method="post">';
-      echo $template->tinymce_textarea('new_sig', htmlspecialchars($session->signature));
-      echo '<input type="submit" value="' . $lang->get('usercp_signature_btn_save') . '" />';
-      echo '</form>';
-      break;
-    case "Profile":
-    case 'Home':
-      
-      global $email;
-      $userpage_id = $paths->nslist['User'] . sanitize_page_id($session->username);
-      $userpage_exists = ( isPage($userpage_id) ) ? '' : ' class="wikilink-nonexistent"';
-      $user_page = makeUrlNS('User', sanitize_page_id($session->username));
-      $site_admin = $email->encryptEmail(getConfig('contact_email'), '', '', $lang->get('usercp_intro_para3_admin_link'));
-      
-      echo '<h3 style="margin-top: 0;">' . $lang->get('usercp_intro_heading_main', array('username' => $session->username)) . '</h3>';
-      
-      echo  $lang->get('usercp_intro', array('userpage_link' => $user_page));
-      
-      $available_ranks = $session->get_user_possible_ranks($session->user_id);
-      $current_rank = $session->get_user_rank($session->user_id);
-      
-      if ( isset($_POST['submit']) )
-      {
-        $real_name = htmlspecialchars($_POST['real_name']);
-        $real_name = $db->escape($real_name);
-        
-        $timezone = intval($_POST['timezone']);
-        $tz_local = $timezone + 1440;
-        
-        $dst = $db->escape($_POST['dst']);
-        if ( !preg_match('/^[0-9]+;[0-9]+;[0-9]+;[0-9]+;[0-9]+$/', $dst) )
-          $dst = '0;0;0;0;60';
-        
-        $GLOBALS['dst_params'] = explode(';', $dst);
-        
-        $imaddr_aim = htmlspecialchars($_POST['imaddr_aim']);
-        $imaddr_aim = $db->escape($imaddr_aim);
-        
-        $imaddr_msn = htmlspecialchars($_POST['imaddr_msn']);
-        $imaddr_msn = $db->escape($imaddr_msn);
-        
-        $imaddr_yahoo = htmlspecialchars($_POST['imaddr_yahoo']);
-        $imaddr_yahoo = $db->escape($imaddr_yahoo);
-        
-        $imaddr_xmpp = htmlspecialchars($_POST['imaddr_xmpp']);
-        $imaddr_xmpp = $db->escape($imaddr_xmpp);
-        
-        $homepage = htmlspecialchars($_POST['homepage']);
-        $homepage = $db->escape($homepage);
-        
-        $location = htmlspecialchars($_POST['location']);
-        $location = $db->escape($location);
-        
-        $occupation = htmlspecialchars($_POST['occupation']);
-        $occupation = $db->escape($occupation);
-        
-        $hobbies = htmlspecialchars($_POST['hobbies']);
-        $hobbies = $db->escape($hobbies);
-        
-        $date_format = $db->escape(htmlspecialchars($_POST['date_format']));
-        $time_format = $db->escape(htmlspecialchars($_POST['time_format']));
-        
-        $email_public = ( isset($_POST['email_public']) ) ? '1' : '0';
-        $disable_js_fx = ( isset($_POST['disable_js_fx']) ) ? '1' : '0';
-        
-        $session->real_name = $real_name;
-        
-        if ( !preg_match('/@([a-z0-9-]+)(\.([a-z0-9-\.]+))?/', $imaddr_msn) && !empty($imaddr_msn) )
-        {
-          $imaddr_msn = "$imaddr_msn@hotmail.com";
-        }
-        
-        if ( !preg_match('#^https?://#', $homepage) )
-        {
-          $homepage = "http://$homepage";
-        }
-        
-        if ( !preg_match('/^http:\/\/([a-z0-9-.]+)([A-z0-9@#\$%\&:;<>,\.\?=\+\(\)\[\]_\/\\\\]*?)$/i', $homepage) )
-        {
-          $homepage = '';
-        }
-        
-        $session->user_extra['user_aim'] = $imaddr_aim;
-        $session->user_extra['user_msn'] = $imaddr_msn;
-        $session->user_extra['user_xmpp'] = $imaddr_xmpp;
-        $session->user_extra['user_yahoo'] = $imaddr_yahoo;
-        $session->user_extra['user_homepage'] = $homepage;
-        $session->user_extra['user_location'] = $location;
-        $session->user_extra['user_job'] = $occupation;
-        $session->user_extra['user_hobbies'] = $hobbies;
-        $session->user_extra['email_public'] = intval($email_public);
-        $session->date_format = $date_format;
-        $session->time_format = $time_format;
-        
-        // user title
-        $user_title_col = '';
-        if ( $session->get_permissions('custom_user_title') && isset($_POST['user_title']) )
-        {
-          $user_title = trim($_POST['user_title']);
-          if ( empty($user_title) )
-          {
-            $colval = 'NULL';
-            $session->user_title = null;
-          }
-          else
-          {
-            $colval = "'" . $db->escape($user_title) . "'";
-            $session->user_title = $user_title;
-          }
-          $user_title_col = ", user_title = $colval";
-        }
-        $user_rank_col = '';
-        if ( isset($_POST['user_rank']) && intval($_POST['user_rank']) != $current_rank['rank_id'] && count($available_ranks) > 1 )
-        {
-          if ( $_POST['user_rank'] == 'NULL' )
-          {
-            $user_rank_col = ", user_rank = NULL, user_rank_userset = 0";
-          }
-          else
-          {
-            $new_rank = intval($_POST['user_rank']);
-            $rank_allowed = false;
-            foreach ( $available_ranks as $rank )
-            {
-              if ( $rank['rank_id'] == $new_rank )
-              {
-                $rank_allowed = true;
-                break;
-              }
-            }
-            if ( $rank_allowed )
-            {
-              $user_rank_col = ", user_rank = $new_rank, user_rank_userset = 1";
-              // hack
-              $current_rank['rank_id'] = $new_rank;
-              $cache->purge('ranks');
-            }
-          }
-        }
-        
-        $q = $db->sql_query('UPDATE '.table_prefix."users SET real_name='$real_name', user_timezone = {$tz_local}, user_dst = '$dst'{$user_title_col}{$user_rank_col} WHERE user_id=$session->user_id;");
-        if ( !$q )
-          $db->_die();
-        
-        $q = $db->sql_query('UPDATE '.table_prefix."users_extra SET user_aim='$imaddr_aim',user_yahoo='$imaddr_yahoo',user_msn='$imaddr_msn',
-                               user_xmpp='$imaddr_xmpp',user_homepage='$homepage',user_location='$location',user_job='$occupation',
-                               user_hobbies='$hobbies',email_public=$email_public,disable_js_fx=$disable_js_fx,date_format='$date_format',
-                               time_format='$time_format'
-                               WHERE user_id=$session->user_id;");
-        
-        if ( !$q )
-          $db->_die();
-        
-        // verify language id
-        $lang_id = strval(intval($_POST['lang_id']));
-        $q = $db->sql_query('SELECT 1 FROM ' . table_prefix . 'language WHERE lang_id = ' . $lang_id . ';');
-        if ( !$q )
-          $db->_die();
-        
-        if ( $db->numrows() > 0 )
-        {
-          $db->free_result();
-          
-          // unload / reload $lang, this verifies that the selected language works
-          // enano should die a violent death if the language fails to load
-          unset($GLOBALS['lang']);
-          unset($lang);
-          $lang_id = intval($lang_id);
-          $GLOBALS['lang'] = new Language($lang_id);
-          global $lang;
-          
-          $q = $db->sql_query('UPDATE ' . table_prefix . 'users SET user_lang = ' . $lang_id . " WHERE user_id = {$session->user_id};");
-          if ( !$q )
-            $db->_die();
-        }
-        else
-        {
-          $db->free_result();
-        }
-        
-        generate_cache_userranks();
-        
-        echo '<div class="info-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_publicinfo_msg_save_success') . '</div>';
-      }
-      
-      $lang_box = '<select name="lang_id">';
-      $q = $db->sql_query('SELECT lang_id, lang_name_native FROM ' . table_prefix . "language;");
-      if ( !$q )
-        $db->_die();
-      
-      while ( $row = $db->fetchrow_num() )
-      {
-        list($lang_id, $lang_name) = $row;
-        $lang_name = htmlspecialchars($lang_name);
-        $selected = ( $lang->lang_id == $lang_id ) ? ' selected="selected"' : '';
-        $lang_box .= "<option value=\"$lang_id\"$selected>$lang_name</option>";
-      }
-      
-      $lang_box .= '</select>';
-      
-      $tz_select = '<select name="timezone">';
-      $tz_list = $lang->get('tz_list');
-      try
-      {
-        $tz_list = enano_json_decode($tz_list);
-      }
-      catch(Exception $e)
-      {
-        die("Caught exception decoding timezone data: <pre>$e</pre>");
-      }
-      foreach ( $tz_list as $key => $i )
-      {
-        $i = ($i * 60);
-        $title = $lang->get("tz_title_{$key}");
-        $hrs = $lang->get("tz_hrs_{$key}");
-        $selected = ( $i == $timezone ) ? ' selected="selected"' : '';
-        $tz_select .= "<option value=\"$i\"$selected>$title</option>";
-      }
-      $tz_select .= '</select>';
-      
-      echo '<form action="'.makeUrl($paths->fullpage).'" method="post">';
-      ?>
-      <div class="tblholder">
-        <table border="0" cellspacing="1" cellpadding="4">
-          <tr>
-            <th colspan="2"><?php echo $lang->get('usercp_publicinfo_heading_main'); ?></th>
-          </tr>
-          <tr>
-            <td colspan="2" class="row3"><?php echo $lang->get('usercp_publicinfo_note_optional'); ?></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_realname'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="real_name" value="<?php echo $session->real_name; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <td class="row2"><?php echo $lang->get('usercp_publicinfo_field_language') . '<br /><small>' . $lang->get('usercp_publicinfo_field_language_hint') . '</small>'; ?></td>
-            <td class="row1"><?php echo $lang_box; ?></td>
-          </tr>
-          <tr>
-            <td class="row2"><?php echo $lang->get('usercp_publicinfo_field_changetheme_title'); ?></td>
-            <td class="row1"><?php echo $lang->get('usercp_publicinfo_field_changetheme_hint'); ?> <a href="<?php echo makeUrlNS('Special', 'ChangeStyle/' . $paths->page); ?>" onclick="ajaxChangeStyle(); return false;"><?php echo $lang->get('usercp_publicinfo_field_changetheme'); ?></a></td>
-          </tr>
-          <tr>
-            <td class="row2"><?php echo $lang->get('usercp_publicinfo_field_dateformat'); ?></td>
-            <td class="row1">
-            <select name="date_format">
-              <?php
-              foreach ( array(DATE_1, DATE_2, DATE_3, DATE_4) as $format )
-              {
-                $selected = $format === $session->date_format ? ' selected="selected"' : '';
-                echo '<option value="' . $format . '"' . $selected . '>' . enano_date($format) . '</option>';
-              }
-              ?>
-            </select>
-            </td>
-          </tr>
-          <tr>
-            <td class="row2"><?php echo $lang->get('usercp_publicinfo_field_timeformat'); ?></td>
-            <td class="row1">
-            <select name="time_format">
-              <?php
-              foreach ( array(TIME_12_NS, TIME_12_S, TIME_24_NS, TIME_24_S) as $format )
-              {
-                $selected = $format === $session->time_format ? ' selected="selected"' : '';
-                echo '<option value="' . $format . '"' . $selected . '>' . enano_date($format) . '</option>';
-              }
-              ?>
-            </select>
-            </td>
-          </tr>
-          <tr>
-            <td class="row3" colspan="2"><?php echo $lang->get('usercp_publicinfo_field_timezone'); ?> <?php echo $tz_select; ?><br /><small><?php echo $lang->get('usercp_publicinfo_field_timezone_hint'); ?></small></td>
-          </tr>
-          <tr>
-            <td class="row2"><?php echo $lang->get('usercp_publicinfo_field_dst'); ?></td>
-            <td class="row1">
-              <select name="dst">
-                <?php
-                global $dst_profiles, $dst_params;
-                $user_dst = implode(';', $dst_params);
-                foreach ( $dst_profiles as $region => $data )
-                {
-                  $selected = ( $data === $user_dst ) ? ' selected="selected"' : '';
-                  echo '<option value="' . $data . '"' . $selected . '>' . $lang->get("tz_dst_$region") . '</option>';
-                }
-                ?>
-              </select>
-            </td>
-          </tr>
-          <?php
-          if ( $session->get_permissions('custom_user_title') ):
-          ?>
-            <tr>
-              <td class="row2">
-                <?php echo $lang->get('usercp_publicinfo_field_usertitle_title'); ?><br />
-                <small><?php echo $lang->get('usercp_publicinfo_field_usertitle_hint'); ?></small>
-              </td>
-              <td class="row1">
-                <input type="text" name="user_title" value="<?php echo htmlspecialchars($session->user_title); ?>" />
-              </td>
-            </tr>
-          <?php
-          endif;
-          if ( count($available_ranks) > 1 ):
-          ?>
-          <tr>
-            <td class="row2">
-              <?php echo $lang->get('usercp_publicinfo_field_rank_title'); ?><br />
-              <small><?php echo $lang->get('usercp_publicinfo_field_rank_hint'); ?></small>
-            </td>
-            <td class="row1">
-              <select name="user_rank">
-                <?php
-                foreach ( $available_ranks as $rank )
-                {
-                  $sel = ( $rank['rank_id'] == $current_rank['rank_id'] ) ? ' selected="selected"' : '';
-                  echo '<option' . $sel . ' value="' . $rank['rank_id'] . '" style="' . htmlspecialchars($rank['rank_style']) . '">';
-                  echo htmlspecialchars($lang->get($rank['rank_title']));
-                  echo '</option>';
-                }
-                ?>
-              </select>
-            </td>
-          </tr>
-          <?php
-          endif;
-          ?>
-          <tr>
-            <th class="subhead" colspan="2">
-              <?php echo $lang->get('usercp_publicinfo_th_im'); ?>
-            </th>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_aim'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="imaddr_aim" value="<?php echo $session->user_extra['user_aim']; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_wlm'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="imaddr_msn" value="<?php echo $session->user_extra['user_msn']; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_yim'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="imaddr_yahoo" value="<?php echo $session->user_extra['user_yahoo']; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_xmpp'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="imaddr_xmpp" value="<?php echo $session->user_extra['user_xmpp']; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <th class="subhead" colspan="2">
-              <?php echo $lang->get('usercp_publicinfo_th_contact'); ?>
-            </th>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_homepage'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="homepage" value="<?php echo $session->user_extra['user_homepage']; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_location'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="location" value="<?php echo $session->user_extra['user_location']; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_job'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="occupation" value="<?php echo $session->user_extra['user_job']; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_hobbies'); ?></td>
-            <td class="row1" style="width: 50%;"><input type="text" name="hobbies" value="<?php echo $session->user_extra['user_hobbies']; ?>" size="30" /></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><label for="chk_email_public"><?php echo $lang->get('usercp_publicinfo_field_email_public'); ?></label></td>
-            <td class="row1" style="width: 50%;"><label><input type="checkbox" id="chk_email_public" name="email_public" <?php if ($session->user_extra['email_public'] == 1) echo 'checked="checked"'; ?> size="30" /> <small><?php echo $lang->get('usercp_publicinfo_field_email_public_hint'); ?></small></label></td>
-          </tr>
-          <tr>
-            <td class="row2" style="width: 50%;"><label for="chk_jsfx"><?php echo $lang->get('usercp_publicinfo_field_jsfx'); ?></label></td>
-            <td class="row1" style="width: 50%;"><label><input type="checkbox" id="chk_jsfx" name="disable_js_fx" <?php if ($session->user_extra['disable_js_fx'] == 1) echo 'checked="checked"'; ?> size="30" /> <small><?php echo $lang->get('usercp_publicinfo_field_jsfx_hint'); ?></small></label></td>
-          </tr>
-          <tr>
-            <th class="subhead" colspan="2">
-              <input type="submit" name="submit" value="<?php echo $lang->get('usercp_publicinfo_btn_save'); ?>" />
-            </th>
-          </tr>
-        </table>
-      </div>
-      <?php
-      // CSRF protection
-      echo '<input type="hidden" name="cstok" value="' . $session->csrf_token . '" />';
-      echo '</form>';
-      break;
-    case 'Avatar':
-      if ( getConfig('avatar_enable', 0) !== 1 )
-      {
-        echo '<div class="error-box"><b>' . $lang->get('usercp_avatar_err_disabled_title') . '</b><br />' . $lang->get('usercp_avatar_err_disabled_body') . '</div>';
-        break;
-      }
-      
-      if ( isset($_POST['submit']) )
-      {
-        list($has_avi, $avi_type) = avatar_post($session->user_id);
-      }
-      else
-      {
-        // Determine current avatar
-        $q = $db->sql_query('SELECT user_has_avatar, avatar_type FROM ' . table_prefix . 'users WHERE user_id = ' . $session->user_id . ';');
-        if ( !$q )
-          $db->_die('Avatar CP selecting user\'s avatar data');
-        
-        list($has_avi, $avi_type) = $db->fetchrow_num();
-      }
-      
-      ?>
-      <script type="text/javascript">
-      
-        function avatar_select_field(elParent)
-        {
-          $('td#avatar_upload_btns > div:visible').hide('blind');
-          switch(elParent.value)
-          {
-            case 'set_http':
-              $('#avatar_upload_http').show('blind');
-              break;
-            case 'set_file':
-              $('#avatar_upload_file').show('blind');
-              break;
-            case 'set_gravatar':
-              $('#avatar_upload_gravatar').show('blind');
-              break;
-          }
-        }
-      
-      </script>
-      <?php
-      
-      echo '<form action="' . makeUrl($paths->fullpage) . '" method="post" enctype="multipart/form-data">';
-      echo '<div class="tblholder">';
-      echo '<table border="0" cellspacing="1" cellpadding="4">';
-      echo '<tr>
-              <th colspan="2">
-                ' . $lang->get('usercp_avatar_table_title') . '
-              </th>
-            </tr>';
-            
-      echo '<tr>
-              <td class="row2" style="width: 150px;">
-                ' . $lang->get('usercp_avatar_label_current') . '
-              </td>
-              <td class="row1" style="text-align: center;">';
-              
-      if ( $has_avi == 1 )
-      {
-        echo '<img alt="' . $lang->get('usercp_avatar_image_alt', array('username' => $session->username)) . '" src="' . make_avatar_url($session->user_id, $avi_type, $session->email) . '" />';
-      }
-      else
-      {
-        echo $lang->get('usercp_avatar_image_none');
-      }
-      
-      echo '    </td>
-              </tr>';
-              
-      echo '  <tr>
-                <td class="row2">
-                  ' . $lang->get('usercp_avatar_lbl_change') . '
-                </td>
-                <td class="row1" id="avatar_upload_btns">
-                  <label><input type="radio" name="avatar_action" value="keep" onclick="avatar_select_field(this);" checked="checked" /> ' . $lang->get('usercp_avatar_lbl_keep') . '</label><br />
-                  <label><input type="radio" name="avatar_action" value="remove" onclick="avatar_select_field(this);" /> ' . $lang->get('usercp_avatar_lbl_remove') . '</label><br />';
-      if ( getConfig('avatar_upload_http') == '1' )
-      {
-        echo '    <label><input type="radio" name="avatar_action" value="set_http" onclick="avatar_select_field(this);" /> ' . $lang->get('usercp_avatar_lbl_set_http') . '</label><br />
-                  <div id="avatar_upload_http" style="display: none; margin: 10px 0 0 2.2em;">
-                    ' . $lang->get('usercp_avatar_lbl_url') . ' <input type="text" name="avatar_http_url" size="40" value="http://" /><br />
-                    <small>' . $lang->get('usercp_avatar_lbl_url_desc') . ' ' . $lang->get('usercp_avatar_limits') . '</small>
-                  </div>';
-      }
-      if ( getConfig('avatar_upload_file') == '1' )
-      {
-        echo '    <label><input type="radio" name="avatar_action" value="set_file" onclick="avatar_select_field(this);" /> ' . $lang->get('usercp_avatar_lbl_set_file') . '</label><br />
-                  <div id="avatar_upload_file" style="display: none; margin: 10px 0 0 2.2em;">
-                    ' . $lang->get('usercp_avatar_lbl_file') . ' <input type="file" name="avatar_file" size="40" /><br />
-                    <small>' . $lang->get('usercp_avatar_lbl_file_desc') . ' ' . $lang->get('usercp_avatar_limits') . '</small>
-                  </div>';
-      }
-      if ( getConfig('avatar_upload_gravatar') == '1' )
-      {
-        $rating_images = array('g' => '0', 'pg' => '1', 'r' => '2', 'x' => '3');
-        $rating_id = $rating_images[ getConfig('gravatar_rating', 'g') ];
-        $rating_image = "http://s.gravatar.com/images/gravatars/ratings/$rating_id.gif";
-        $max_rating = getConfig('gravatar_rating', 'g');
-        echo '    <label><input type="radio" name="avatar_action" value="set_gravatar" onclick="avatar_select_field(this);" /> ' . $lang->get('usercp_avatar_lbl_set_gravatar') . ' <img alt=" " src="' . make_gravatar_url($session->email, 16) . '" /></label> (<a href="http://www.gravatar.com/" onclick="window.open(this); return false;">' . $lang->get('usercp_avatar_link_gravatar_info') . '</a>)
-                  <div id="avatar_upload_gravatar" style="display: none; margin: 10px 0 0 2.2em;">
-                    <div style="float: left; margin-right: 5px; margin-bottom: 20px;">
-                      <img alt=" " src="' . $rating_image . '" />
-                    </div>
-                    ' . $lang->get("usercp_avatar_gravatar_rating_$max_rating") . '
-                  </div>';
-      }
-      echo '    </td>
-              </tr>';
-              
-      echo '  <tr>
-                <th class="subhead" colspan="2">
-                  <input type="submit" name="submit" value="' . $lang->get('etc_save_changes') . '" />
-                </th>
-              </tr>';
-              
-      echo '</table>
-            </div>';
-      
-      break;
-    default:
-      $good = false;
-      $code = $plugins->setHook('userprefs_body', true);
-      foreach ( $code as $cmd )
-      {
-        if ( eval($cmd) )
-          $good = true;
-      }
-      if ( !$good )
-      {
-        echo '<h3>Invalid module</h3>
-              <p>Userprefs module "'.$section.'" not found.</p>';
-      }
-      break;
-  }
-  
-  $template->footer();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $timezone;
+	global $cache;
+	
+	// We need a login to continue
+	if ( !$session->user_logged_in )
+		redirect(makeUrlNS('Special', 'Login/' . $paths->page), 'Login required', 'You need to be logged in to access this page. Please wait while you are redirected to the login page.');
+	
+	// User ID - later this will be specified on the URL, but hardcoded for now
+	$uid = intval($session->user_id);
+	
+	// Instanciate the AES encryptor
+	$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+	
+	// Basic user info
+	$q = $db->sql_query('SELECT username, password, email, real_name, signature, theme, style FROM '.table_prefix.'users WHERE user_id='.$uid.';');
+	if ( !$q )
+		$db->_die();
+	
+	$row = $db->fetchrow();
+	$db->free_result();
+	
+	$section = $paths->getParam(0);
+	if ( !$section )
+	{
+		$section = 'Home';
+	}
+	
+	$errors = '';
+	
+	switch ( $section )
+	{
+		case 'Avatar':
+			$template->preload_js('jquery');
+			$template->preload_js('jquery-ui');
+			break;
+		case 'EmailPassword':
+			// Require elevated privileges (well sortof)
+			if ( $session->auth_level < USER_LEVEL_CHPREF )
+			{
+				redirect(makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . USER_LEVEL_CHPREF, true), 'Authentication required', 'You need to re-authenticate to access this page.', 0);
+			}
+			
+			if ( isset($_POST['submit']) )
+			{
+				$email_changed = false;
+				// First do the e-mail address
+				if ( strlen($_POST['newemail']) > 0 )
+				{
+					switch('foo') // Same reason as in the password code...
+					{
+						case 'foo':
+							if ( $_POST['newemail'] != $_POST['newemail_conf'] )
+							{
+								$errors .= '<div class="error-box">' . $lang->get('usercp_emailpassword_err_email_no_match') . '</div>';
+								break;
+							}
+					}
+					$q = $db->sql_query('SELECT password FROM '.table_prefix.'users WHERE user_id='.$session->user_id.';');
+					if ( !$q )
+						$db->_die();
+					$row = $db->fetchrow();
+					$db->free_result();
+					
+					$new_email = $_POST['newemail'];
+					
+					$result = $session->change_email($session->user_id, $new_email);
+					if ( $result != 'success' )
+					{
+						$message = '<p>' . $lang->get('usercp_emailpassword_err_list') . '</p>';
+						$message .= '<ul><li>' . implode("</li>\n<li>", $result) . '</li></ul>';
+						die_friendly($lang->get('usercp_emailpassword_err_title'), $message);
+					}
+					$email_changed = true;
+				}
+				// Obtain password
+				if ( !empty($_POST['crypt_data']) || !empty($_POST['newpass']) || $session->password_change_disabled )
+				{
+					$newpass = $session->password_change_disabled ? '' : $session->get_aes_post('newpass');
+					// At this point we know if we _want_ to change the password...
+					
+					// We can't check the password to see if it matches the confirmation
+					// because the confirmation was destroyed during the encryption. I figured
+					// this wasn't a big deal because if the encryption worked, then either
+					// the Javascript validated it or the user hacked the form. In the latter
+					// case, if he's smart enough to hack the encryption code, he's probably
+					// smart enough to remember his password.
+					
+					if ( strlen($newpass) > 0 )
+					{
+						if ( defined('ENANO_DEMO_MODE') )
+							$errors .= '<div class="error-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_emailpassword_err_demo') . '</div>';
+						// Perform checks
+						if ( strlen($newpass) < 6 )
+							$errors .= '<div class="error-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_emailpassword_err_password_too_short') . '</div>';
+						if ( getConfig('pw_strength_enable') == '1' )
+						{
+							$score_inp = password_score($newpass);
+							$score_min = getConfig('pw_strength_minimum', -10);
+							if ( $score_inp < $score_min )
+								$errors .= '<div class="error-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_emailpassword_err_password_too_weak', array('score' => $score_inp)) . '</div>';
+						}
+						if ( $_POST['use_crypt'] == 'no' && $newpass != $_POST['newpass_confirm'] )
+						{
+							$errors .= '<div class="error-box">' . $lang->get('usercp_emailpassword_err_password_no_match') . '</div>';
+						}
+						// Encrypt new password
+						if ( empty($errors) )
+						{
+							// Perform the swap
+							$session->set_password($session->username, $newpass);
+							// Log out and back in
+							$username = $session->username;
+							$session->logout();
+							if ( $email_changed )
+							{
+								if ( getConfig('account_activation') == 'user' )
+								{
+									redirect(makeUrl(get_main_page()), $lang->get('usercp_emailpassword_msg_profile_success'), $lang->get('usercp_emailpassword_msg_need_activ_user'), 20);
+								}
+								else if ( getConfig('account_activation') == 'admin' )
+								{
+									redirect(makeUrl(get_main_page()), $lang->get('usercp_emailpassword_msg_profile_success'), $lang->get('usercp_emailpassword_msg_need_activ_admin'), 20);
+								}
+							}
+							$session->login_without_crypto($username, $newpass);
+							redirect(makeUrlNS('Special', 'Preferences'), $lang->get('usercp_emailpassword_msg_pass_success'), $lang->get('usercp_emailpassword_msg_password_changed'), 5);
+						}
+					}
+					else if ( $email_changed )
+					{
+						$session->logout(USER_LEVEL_CHPREF);
+						$activation = $session->user_level >= USER_LEVEL_MOD ? 'none' : getConfig('account_activation', 'none');
+						switch($activation)
+						{
+							default:
+								$message_body = $lang->get('usercp_emailpassword_msg_password_changed');
+								$timeout = 5;
+								break;
+							case 'admin':
+								$message_body = $lang->get('usercp_emailpassword_msg_need_activ_user');
+								$timeout = 20;
+								break;
+							case 'user':
+								$message_body = $lang->get('usercp_emailpassword_msg_need_activ_admin');
+								$timeout = 20;
+								break;
+						}
+						redirect(makeUrlNS('Special', 'Preferences'), $lang->get('usercp_emailpassword_msg_email_success'), $message_body, $timeout);
+					}
+				}
+			}
+			$template->tpl_strings['PAGE_NAME'] = $lang->get('usercp_emailpassword_title');
+			break;
+		case 'Signature':
+			$template->tpl_strings['PAGE_NAME'] = $lang->get('usercp_signature_title');
+			break;
+		case 'Profile':
+		case 'Home':
+			if ( isset($_POST['submit']) )
+				csrf_request_confirm();
+			
+			$template->tpl_strings['PAGE_NAME'] = $lang->get('usercp_publicinfo_title');
+			break;
+	}
+	
+	$template->header();
+	
+	// Output the menu
+	// This is not templatized because it conforms to the jBox menu standard.
+	
+	userprefs_show_menu();
+	
+	switch ( $section )
+	{
+		case 'EmailPassword':
+			
+			$errors = trim($errors);
+			if ( !empty($errors) )
+			{
+				echo $errors;
+			}
+			
+			echo '<form action="' . makeUrlNS('Special', 'Preferences/EmailPassword') . '" method="post" onsubmit="return runEncryption();" name="empwform" >';
+			echo '<fieldset>';
+			echo '<legend>' . $lang->get('usercp_emailpassword_grp_chpasswd') . '</legend>';
+			
+			// Password change form
+			if ( $session->password_change_disabled )
+			{
+				echo '<p>' . $lang->get('usercp_emailpassword_msg_change_disabled') . '</p>';
+				if ( $session->password_change_dest['url'] )
+				{
+					echo '<p>' . $lang->get('usercp_emailpassword_msg_change_disabled_url') . '
+ 									<a onclick="window.open(this.href); return false;" href="' . htmlspecialchars($session->password_change_dest['url']) . '">' . htmlspecialchars($session->password_change_dest['title']) . '</a></p>';
+				}
+			}
+			else
+			{
+			echo $lang->get('usercp_emailpassword_field_newpass') . '<br />
+								<input type="password" name="newpass" size="30" tabindex="1" ' . ( getConfig('pw_strength_enable') == '1' ? 'onkeyup="password_score_field(this);" ' : '' ) . '/>' . ( getConfig('pw_strength_enable') == '1' ? '<span class="password-checker" style="font-weight: bold; color: #aaaaaa;"> Loading...</span>' : '' ) . '
+							<br />
+							<br />
+							' . $lang->get('usercp_emailpassword_field_newpass_confirm') . '<br />
+							<input type="password" name="newpass_confirm" size="30" tabindex="2" />
+							' . ( getConfig('pw_strength_enable') == '1' ? '<br /><br /><div id="pwmeter"></div>
+							<small>' . $lang->get('usercp_emailpassword_msg_password_min_score') . '</small>' : '' );
+			}
+			echo '</fieldset><br />';
+			echo '<fieldset>
+				<legend>' . $lang->get('usercp_emailpassword_grp_chemail') . '</legend>
+				' . $lang->get('usercp_emailpassword_field_newemail') . '<br />
+					<input type="text" value="' . ( isset($_POST['newemail']) ? htmlspecialchars($_POST['newemail']) : '' ) . '" name="newemail" size="30" tabindex="3" />
+				<br />
+				<br />
+				' . $lang->get('usercp_emailpassword_field_newemail_confirm') . '<br />
+					<input type="text" value="' . ( isset($_POST['newemail']) ? htmlspecialchars($_POST['newemail']) : '' ) . '" name="newemail_conf" size="30" tabindex="4" />
+			</fieldset>
+			<br />
+			<div style="text-align: right;"><input type="submit" name="submit" value="' . $lang->get('etc_save_changes') . '" tabindex="5" /></div>';
+			
+			if ( !$session->password_change_disabled )
+				echo $session->generate_aes_form();
+			
+			echo '</form>';
+			
+			// ENCRYPTION CODE
+			?>
+			<?php if ( !$session->password_change_disabled && getConfig('pw_strength_enable') == '1' ): ?>
+			<script type="text/javascript">
+			addOnloadHook(function()
+				{
+					password_score_field(document.forms.empwform.newpass);
+				});
+			</script>
+			<?php endif; ?>
+			<?php
+			echo $session->aes_javascript('empwform', 'newpass');
+			break;
+		case 'Signature':
+			if ( isset($_POST['new_sig']) )
+			{
+				$sig = $_POST['new_sig'];
+				$sig = RenderMan::preprocess_text($sig, true, false);
+				$sql_sig = $db->escape($sig);
+				$q = $db->sql_query('UPDATE '.table_prefix.'users SET signature=\'' . $sql_sig . '\' WHERE user_id=' . $session->user_id . ';');
+				if ( !$q )
+					$db->_die();
+				$session->signature = $sig;
+				echo '<div class="info-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_signature_msg_saved') . '</div>';
+			}
+			echo '<form action="'.makeUrl($paths->fullpage).'" method="post">';
+			echo $template->tinymce_textarea('new_sig', htmlspecialchars($session->signature));
+			echo '<input type="submit" value="' . $lang->get('usercp_signature_btn_save') . '" />';
+			echo '</form>';
+			break;
+		case "Profile":
+		case 'Home':
+			
+			global $email;
+			$userpage_id = $paths->nslist['User'] . sanitize_page_id($session->username);
+			$userpage_exists = ( isPage($userpage_id) ) ? '' : ' class="wikilink-nonexistent"';
+			$user_page = makeUrlNS('User', sanitize_page_id($session->username));
+			$site_admin = $email->encryptEmail(getConfig('contact_email'), '', '', $lang->get('usercp_intro_para3_admin_link'));
+			
+			echo '<h3 style="margin-top: 0;">' . $lang->get('usercp_intro_heading_main', array('username' => $session->username)) . '</h3>';
+			
+			echo  $lang->get('usercp_intro', array('userpage_link' => $user_page));
+			
+			$available_ranks = $session->get_user_possible_ranks($session->user_id);
+			$current_rank = $session->get_user_rank($session->user_id);
+			
+			if ( isset($_POST['submit']) )
+			{
+				$real_name = htmlspecialchars($_POST['real_name']);
+				$real_name = $db->escape($real_name);
+				
+				$timezone = intval($_POST['timezone']);
+				$tz_local = $timezone + 1440;
+				
+				$dst = $db->escape($_POST['dst']);
+				if ( !preg_match('/^[0-9]+;[0-9]+;[0-9]+;[0-9]+;[0-9]+$/', $dst) )
+					$dst = '0;0;0;0;60';
+				
+				$GLOBALS['dst_params'] = explode(';', $dst);
+				
+				$imaddr_aim = htmlspecialchars($_POST['imaddr_aim']);
+				$imaddr_aim = $db->escape($imaddr_aim);
+				
+				$imaddr_msn = htmlspecialchars($_POST['imaddr_msn']);
+				$imaddr_msn = $db->escape($imaddr_msn);
+				
+				$imaddr_yahoo = htmlspecialchars($_POST['imaddr_yahoo']);
+				$imaddr_yahoo = $db->escape($imaddr_yahoo);
+				
+				$imaddr_xmpp = htmlspecialchars($_POST['imaddr_xmpp']);
+				$imaddr_xmpp = $db->escape($imaddr_xmpp);
+				
+				$homepage = htmlspecialchars($_POST['homepage']);
+				$homepage = $db->escape($homepage);
+				
+				$location = htmlspecialchars($_POST['location']);
+				$location = $db->escape($location);
+				
+				$occupation = htmlspecialchars($_POST['occupation']);
+				$occupation = $db->escape($occupation);
+				
+				$hobbies = htmlspecialchars($_POST['hobbies']);
+				$hobbies = $db->escape($hobbies);
+				
+				$date_format = $db->escape(htmlspecialchars($_POST['date_format']));
+				$time_format = $db->escape(htmlspecialchars($_POST['time_format']));
+				
+				$email_public = ( isset($_POST['email_public']) ) ? '1' : '0';
+				$disable_js_fx = ( isset($_POST['disable_js_fx']) ) ? '1' : '0';
+				
+				$session->real_name = $real_name;
+				
+				if ( !preg_match('/@([a-z0-9-]+)(\.([a-z0-9-\.]+))?/', $imaddr_msn) && !empty($imaddr_msn) )
+				{
+					$imaddr_msn = "$imaddr_msn@hotmail.com";
+				}
+				
+				if ( !preg_match('#^https?://#', $homepage) )
+				{
+					$homepage = "http://$homepage";
+				}
+				
+				if ( !preg_match('/^http:\/\/([a-z0-9-.]+)([A-z0-9@#\$%\&:;<>,\.\?=\+\(\)\[\]_\/\\\\]*?)$/i', $homepage) )
+				{
+					$homepage = '';
+				}
+				
+				$session->user_extra['user_aim'] = $imaddr_aim;
+				$session->user_extra['user_msn'] = $imaddr_msn;
+				$session->user_extra['user_xmpp'] = $imaddr_xmpp;
+				$session->user_extra['user_yahoo'] = $imaddr_yahoo;
+				$session->user_extra['user_homepage'] = $homepage;
+				$session->user_extra['user_location'] = $location;
+				$session->user_extra['user_job'] = $occupation;
+				$session->user_extra['user_hobbies'] = $hobbies;
+				$session->user_extra['email_public'] = intval($email_public);
+				$session->date_format = $date_format;
+				$session->time_format = $time_format;
+				
+				// user title
+				$user_title_col = '';
+				if ( $session->get_permissions('custom_user_title') && isset($_POST['user_title']) )
+				{
+					$user_title = trim($_POST['user_title']);
+					if ( empty($user_title) )
+					{
+						$colval = 'NULL';
+						$session->user_title = null;
+					}
+					else
+					{
+						$colval = "'" . $db->escape($user_title) . "'";
+						$session->user_title = $user_title;
+					}
+					$user_title_col = ", user_title = $colval";
+				}
+				$user_rank_col = '';
+				if ( isset($_POST['user_rank']) && intval($_POST['user_rank']) != $current_rank['rank_id'] && count($available_ranks) > 1 )
+				{
+					if ( $_POST['user_rank'] == 'NULL' )
+					{
+						$user_rank_col = ", user_rank = NULL, user_rank_userset = 0";
+					}
+					else
+					{
+						$new_rank = intval($_POST['user_rank']);
+						$rank_allowed = false;
+						foreach ( $available_ranks as $rank )
+						{
+							if ( $rank['rank_id'] == $new_rank )
+							{
+								$rank_allowed = true;
+								break;
+							}
+						}
+						if ( $rank_allowed )
+						{
+							$user_rank_col = ", user_rank = $new_rank, user_rank_userset = 1";
+							// hack
+							$current_rank['rank_id'] = $new_rank;
+							$cache->purge('ranks');
+						}
+					}
+				}
+				
+				$q = $db->sql_query('UPDATE '.table_prefix."users SET real_name='$real_name', user_timezone = {$tz_local}, user_dst = '$dst'{$user_title_col}{$user_rank_col} WHERE user_id=$session->user_id;");
+				if ( !$q )
+					$db->_die();
+				
+				$q = $db->sql_query('UPDATE '.table_prefix."users_extra SET user_aim='$imaddr_aim',user_yahoo='$imaddr_yahoo',user_msn='$imaddr_msn',
+ 															user_xmpp='$imaddr_xmpp',user_homepage='$homepage',user_location='$location',user_job='$occupation',
+ 															user_hobbies='$hobbies',email_public=$email_public,disable_js_fx=$disable_js_fx,date_format='$date_format',
+ 															time_format='$time_format'
+ 															WHERE user_id=$session->user_id;");
+				
+				if ( !$q )
+					$db->_die();
+				
+				// verify language id
+				$lang_id = strval(intval($_POST['lang_id']));
+				$q = $db->sql_query('SELECT 1 FROM ' . table_prefix . 'language WHERE lang_id = ' . $lang_id . ';');
+				if ( !$q )
+					$db->_die();
+				
+				if ( $db->numrows() > 0 )
+				{
+					$db->free_result();
+					
+					// unload / reload $lang, this verifies that the selected language works
+					// enano should die a violent death if the language fails to load
+					unset($GLOBALS['lang']);
+					unset($lang);
+					$lang_id = intval($lang_id);
+					$GLOBALS['lang'] = new Language($lang_id);
+					global $lang;
+					
+					$q = $db->sql_query('UPDATE ' . table_prefix . 'users SET user_lang = ' . $lang_id . " WHERE user_id = {$session->user_id};");
+					if ( !$q )
+						$db->_die();
+				}
+				else
+				{
+					$db->free_result();
+				}
+				
+				generate_cache_userranks();
+				
+				echo '<div class="info-box" style="margin: 0 0 10px 0;">' . $lang->get('usercp_publicinfo_msg_save_success') . '</div>';
+			}
+			
+			$lang_box = '<select name="lang_id">';
+			$q = $db->sql_query('SELECT lang_id, lang_name_native FROM ' . table_prefix . "language;");
+			if ( !$q )
+				$db->_die();
+			
+			while ( $row = $db->fetchrow_num() )
+			{
+				list($lang_id, $lang_name) = $row;
+				$lang_name = htmlspecialchars($lang_name);
+				$selected = ( $lang->lang_id == $lang_id ) ? ' selected="selected"' : '';
+				$lang_box .= "<option value=\"$lang_id\"$selected>$lang_name</option>";
+			}
+			
+			$lang_box .= '</select>';
+			
+			$tz_select = '<select name="timezone">';
+			$tz_list = $lang->get('tz_list');
+			try
+			{
+				$tz_list = enano_json_decode($tz_list);
+			}
+			catch(Exception $e)
+			{
+				die("Caught exception decoding timezone data: <pre>$e</pre>");
+			}
+			foreach ( $tz_list as $key => $i )
+			{
+				$i = ($i * 60);
+				$title = $lang->get("tz_title_{$key}");
+				$hrs = $lang->get("tz_hrs_{$key}");
+				$selected = ( $i == $timezone ) ? ' selected="selected"' : '';
+				$tz_select .= "<option value=\"$i\"$selected>$title</option>";
+			}
+			$tz_select .= '</select>';
+			
+			echo '<form action="'.makeUrl($paths->fullpage).'" method="post">';
+			?>
+			<div class="tblholder">
+				<table border="0" cellspacing="1" cellpadding="4">
+					<tr>
+						<th colspan="2"><?php echo $lang->get('usercp_publicinfo_heading_main'); ?></th>
+					</tr>
+					<tr>
+						<td colspan="2" class="row3"><?php echo $lang->get('usercp_publicinfo_note_optional'); ?></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_realname'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="real_name" value="<?php echo $session->real_name; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<td class="row2"><?php echo $lang->get('usercp_publicinfo_field_language') . '<br /><small>' . $lang->get('usercp_publicinfo_field_language_hint') . '</small>'; ?></td>
+						<td class="row1"><?php echo $lang_box; ?></td>
+					</tr>
+					<tr>
+						<td class="row2"><?php echo $lang->get('usercp_publicinfo_field_changetheme_title'); ?></td>
+						<td class="row1"><?php echo $lang->get('usercp_publicinfo_field_changetheme_hint'); ?> <a href="<?php echo makeUrlNS('Special', 'ChangeStyle/' . $paths->page); ?>" onclick="ajaxChangeStyle(); return false;"><?php echo $lang->get('usercp_publicinfo_field_changetheme'); ?></a></td>
+					</tr>
+					<tr>
+						<td class="row2"><?php echo $lang->get('usercp_publicinfo_field_dateformat'); ?></td>
+						<td class="row1">
+						<select name="date_format">
+							<?php
+							foreach ( array(DATE_1, DATE_2, DATE_3, DATE_4) as $format )
+							{
+								$selected = $format === $session->date_format ? ' selected="selected"' : '';
+								echo '<option value="' . $format . '"' . $selected . '>' . enano_date($format) . '</option>';
+							}
+							?>
+						</select>
+						</td>
+					</tr>
+					<tr>
+						<td class="row2"><?php echo $lang->get('usercp_publicinfo_field_timeformat'); ?></td>
+						<td class="row1">
+						<select name="time_format">
+							<?php
+							foreach ( array(TIME_12_NS, TIME_12_S, TIME_24_NS, TIME_24_S) as $format )
+							{
+								$selected = $format === $session->time_format ? ' selected="selected"' : '';
+								echo '<option value="' . $format . '"' . $selected . '>' . enano_date($format) . '</option>';
+							}
+							?>
+						</select>
+						</td>
+					</tr>
+					<tr>
+						<td class="row3" colspan="2"><?php echo $lang->get('usercp_publicinfo_field_timezone'); ?> <?php echo $tz_select; ?><br /><small><?php echo $lang->get('usercp_publicinfo_field_timezone_hint'); ?></small></td>
+					</tr>
+					<tr>
+						<td class="row2"><?php echo $lang->get('usercp_publicinfo_field_dst'); ?></td>
+						<td class="row1">
+							<select name="dst">
+								<?php
+								global $dst_profiles, $dst_params;
+								$user_dst = implode(';', $dst_params);
+								foreach ( $dst_profiles as $region => $data )
+								{
+									$selected = ( $data === $user_dst ) ? ' selected="selected"' : '';
+									echo '<option value="' . $data . '"' . $selected . '>' . $lang->get("tz_dst_$region") . '</option>';
+								}
+								?>
+							</select>
+						</td>
+					</tr>
+					<?php
+					if ( $session->get_permissions('custom_user_title') ):
+					?>
+						<tr>
+							<td class="row2">
+								<?php echo $lang->get('usercp_publicinfo_field_usertitle_title'); ?><br />
+								<small><?php echo $lang->get('usercp_publicinfo_field_usertitle_hint'); ?></small>
+							</td>
+							<td class="row1">
+								<input type="text" name="user_title" value="<?php echo htmlspecialchars($session->user_title); ?>" />
+							</td>
+						</tr>
+					<?php
+					endif;
+					if ( count($available_ranks) > 1 ):
+					?>
+					<tr>
+						<td class="row2">
+							<?php echo $lang->get('usercp_publicinfo_field_rank_title'); ?><br />
+							<small><?php echo $lang->get('usercp_publicinfo_field_rank_hint'); ?></small>
+						</td>
+						<td class="row1">
+							<select name="user_rank">
+								<?php
+								foreach ( $available_ranks as $rank )
+								{
+									$sel = ( $rank['rank_id'] == $current_rank['rank_id'] ) ? ' selected="selected"' : '';
+									echo '<option' . $sel . ' value="' . $rank['rank_id'] . '" style="' . htmlspecialchars($rank['rank_style']) . '">';
+									echo htmlspecialchars($lang->get($rank['rank_title']));
+									echo '</option>';
+								}
+								?>
+							</select>
+						</td>
+					</tr>
+					<?php
+					endif;
+					?>
+					<tr>
+						<th class="subhead" colspan="2">
+							<?php echo $lang->get('usercp_publicinfo_th_im'); ?>
+						</th>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_aim'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="imaddr_aim" value="<?php echo $session->user_extra['user_aim']; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_wlm'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="imaddr_msn" value="<?php echo $session->user_extra['user_msn']; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_yim'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="imaddr_yahoo" value="<?php echo $session->user_extra['user_yahoo']; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_xmpp'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="imaddr_xmpp" value="<?php echo $session->user_extra['user_xmpp']; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<th class="subhead" colspan="2">
+							<?php echo $lang->get('usercp_publicinfo_th_contact'); ?>
+						</th>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_homepage'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="homepage" value="<?php echo $session->user_extra['user_homepage']; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_location'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="location" value="<?php echo $session->user_extra['user_location']; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_job'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="occupation" value="<?php echo $session->user_extra['user_job']; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><?php echo $lang->get('usercp_publicinfo_field_hobbies'); ?></td>
+						<td class="row1" style="width: 50%;"><input type="text" name="hobbies" value="<?php echo $session->user_extra['user_hobbies']; ?>" size="30" /></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><label for="chk_email_public"><?php echo $lang->get('usercp_publicinfo_field_email_public'); ?></label></td>
+						<td class="row1" style="width: 50%;"><label><input type="checkbox" id="chk_email_public" name="email_public" <?php if ($session->user_extra['email_public'] == 1) echo 'checked="checked"'; ?> size="30" /> <small><?php echo $lang->get('usercp_publicinfo_field_email_public_hint'); ?></small></label></td>
+					</tr>
+					<tr>
+						<td class="row2" style="width: 50%;"><label for="chk_jsfx"><?php echo $lang->get('usercp_publicinfo_field_jsfx'); ?></label></td>
+						<td class="row1" style="width: 50%;"><label><input type="checkbox" id="chk_jsfx" name="disable_js_fx" <?php if ($session->user_extra['disable_js_fx'] == 1) echo 'checked="checked"'; ?> size="30" /> <small><?php echo $lang->get('usercp_publicinfo_field_jsfx_hint'); ?></small></label></td>
+					</tr>
+					<tr>
+						<th class="subhead" colspan="2">
+							<input type="submit" name="submit" value="<?php echo $lang->get('usercp_publicinfo_btn_save'); ?>" />
+						</th>
+					</tr>
+				</table>
+			</div>
+			<?php
+			// CSRF protection
+			echo '<input type="hidden" name="cstok" value="' . $session->csrf_token . '" />';
+			echo '</form>';
+			break;
+		case 'Avatar':
+			if ( getConfig('avatar_enable', 0) !== 1 )
+			{
+				echo '<div class="error-box"><b>' . $lang->get('usercp_avatar_err_disabled_title') . '</b><br />' . $lang->get('usercp_avatar_err_disabled_body') . '</div>';
+				break;
+			}
+			
+			if ( isset($_POST['submit']) )
+			{
+				list($has_avi, $avi_type) = avatar_post($session->user_id);
+			}
+			else
+			{
+				// Determine current avatar
+				$q = $db->sql_query('SELECT user_has_avatar, avatar_type FROM ' . table_prefix . 'users WHERE user_id = ' . $session->user_id . ';');
+				if ( !$q )
+					$db->_die('Avatar CP selecting user\'s avatar data');
+				
+				list($has_avi, $avi_type) = $db->fetchrow_num();
+			}
+			
+			?>
+			<script type="text/javascript">
+			
+				function avatar_select_field(elParent)
+				{
+					$('td#avatar_upload_btns > div:visible').hide('blind');
+					switch(elParent.value)
+					{
+						case 'set_http':
+							$('#avatar_upload_http').show('blind');
+							break;
+						case 'set_file':
+							$('#avatar_upload_file').show('blind');
+							break;
+						case 'set_gravatar':
+							$('#avatar_upload_gravatar').show('blind');
+							break;
+					}
+				}
+			
+			</script>
+			<?php
+			
+			echo '<form action="' . makeUrl($paths->fullpage) . '" method="post" enctype="multipart/form-data">';
+			echo '<div class="tblholder">';
+			echo '<table border="0" cellspacing="1" cellpadding="4">';
+			echo '<tr>
+							<th colspan="2">
+								' . $lang->get('usercp_avatar_table_title') . '
+							</th>
+						</tr>';
+						
+			echo '<tr>
+							<td class="row2" style="width: 150px;">
+								' . $lang->get('usercp_avatar_label_current') . '
+							</td>
+							<td class="row1" style="text-align: center;">';
+							
+			if ( $has_avi == 1 )
+			{
+				echo '<img alt="' . $lang->get('usercp_avatar_image_alt', array('username' => $session->username)) . '" src="' . make_avatar_url($session->user_id, $avi_type, $session->email) . '" />';
+			}
+			else
+			{
+				echo $lang->get('usercp_avatar_image_none');
+			}
+			
+			echo '    </td>
+							</tr>';
+							
+			echo '  <tr>
+								<td class="row2">
+									' . $lang->get('usercp_avatar_lbl_change') . '
+								</td>
+								<td class="row1" id="avatar_upload_btns">
+									<label><input type="radio" name="avatar_action" value="keep" onclick="avatar_select_field(this);" checked="checked" /> ' . $lang->get('usercp_avatar_lbl_keep') . '</label><br />
+									<label><input type="radio" name="avatar_action" value="remove" onclick="avatar_select_field(this);" /> ' . $lang->get('usercp_avatar_lbl_remove') . '</label><br />';
+			if ( getConfig('avatar_upload_http') == '1' )
+			{
+				echo '    <label><input type="radio" name="avatar_action" value="set_http" onclick="avatar_select_field(this);" /> ' . $lang->get('usercp_avatar_lbl_set_http') . '</label><br />
+									<div id="avatar_upload_http" style="display: none; margin: 10px 0 0 2.2em;">
+										' . $lang->get('usercp_avatar_lbl_url') . ' <input type="text" name="avatar_http_url" size="40" value="http://" /><br />
+										<small>' . $lang->get('usercp_avatar_lbl_url_desc') . ' ' . $lang->get('usercp_avatar_limits') . '</small>
+									</div>';
+			}
+			if ( getConfig('avatar_upload_file') == '1' )
+			{
+				echo '    <label><input type="radio" name="avatar_action" value="set_file" onclick="avatar_select_field(this);" /> ' . $lang->get('usercp_avatar_lbl_set_file') . '</label><br />
+									<div id="avatar_upload_file" style="display: none; margin: 10px 0 0 2.2em;">
+										' . $lang->get('usercp_avatar_lbl_file') . ' <input type="file" name="avatar_file" size="40" /><br />
+										<small>' . $lang->get('usercp_avatar_lbl_file_desc') . ' ' . $lang->get('usercp_avatar_limits') . '</small>
+									</div>';
+			}
+			if ( getConfig('avatar_upload_gravatar') == '1' )
+			{
+				$rating_images = array('g' => '0', 'pg' => '1', 'r' => '2', 'x' => '3');
+				$rating_id = $rating_images[ getConfig('gravatar_rating', 'g') ];
+				$rating_image = "http://s.gravatar.com/images/gravatars/ratings/$rating_id.gif";
+				$max_rating = getConfig('gravatar_rating', 'g');
+				echo '    <label><input type="radio" name="avatar_action" value="set_gravatar" onclick="avatar_select_field(this);" /> ' . $lang->get('usercp_avatar_lbl_set_gravatar') . ' <img alt=" " src="' . make_gravatar_url($session->email, 16) . '" /></label> (<a href="http://www.gravatar.com/" onclick="window.open(this); return false;">' . $lang->get('usercp_avatar_link_gravatar_info') . '</a>)
+									<div id="avatar_upload_gravatar" style="display: none; margin: 10px 0 0 2.2em;">
+										<div style="float: left; margin-right: 5px; margin-bottom: 20px;">
+											<img alt=" " src="' . $rating_image . '" />
+										</div>
+										' . $lang->get("usercp_avatar_gravatar_rating_$max_rating") . '
+									</div>';
+			}
+			echo '    </td>
+							</tr>';
+							
+			echo '  <tr>
+								<th class="subhead" colspan="2">
+									<input type="submit" name="submit" value="' . $lang->get('etc_save_changes') . '" />
+								</th>
+							</tr>';
+							
+			echo '</table>
+						</div>';
+			
+			break;
+		default:
+			$good = false;
+			$code = $plugins->setHook('userprefs_body', true);
+			foreach ( $code as $cmd )
+			{
+				if ( eval($cmd) )
+					$good = true;
+			}
+			if ( !$good )
+			{
+				echo '<h3>Invalid module</h3>
+							<p>Userprefs module "'.$section.'" not found.</p>';
+			}
+			break;
+	}
+	
+	$template->footer();
 }
 
 // Avatar POST processor
 function avatar_post($user_id, $quiet = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  $had_a_boo_boo = true;
-  
-  // Determine current avatar
-  $q = $db->sql_query('SELECT user_has_avatar, avatar_type FROM ' . table_prefix . 'users WHERE user_id = ' . $session->user_id . ';');
-  if ( !$q )
-    $db->_die('Avatar CP selecting user\'s avatar data');
-  
-  list($has_avi, $avi_type) = $db->fetchrow_num();
-  
-  $action = ( isset($_POST['avatar_action']) ) ? $_POST['avatar_action'] : 'keep';
-  $avi_path = ENANO_ROOT . '/' . getConfig('avatar_directory') . '/' . $user_id . '.' . $avi_type;
-  switch($action)
-  {
-    case 'keep':
-    default:
-      $had_a_boo_boo = false;
-      break;
-    case 'remove':
-      if ( $has_avi )
-      {
-        // First switch the avatar off
-        $q = $db->sql_query('UPDATE ' . table_prefix . 'users SET user_has_avatar = 0 WHERE user_id = ' . $user_id . ';');
-        if ( !$q )
-          $db->_die('Avatar CP switching user avatar off');
-        
-        if ( @unlink($avi_path) )
-        {
-          $quiet || print '<div class="info-box">' . $lang->get('usercp_avatar_delete_success') . '</div>';
-        }
-        $has_avi = 0;
-      }
-      $had_a_boo_boo = false;
-      break;
-    case 'set_http':
-    case 'set_file':
-      // Hackish way to preserve the UNIX philosophy of reusing as much code as possible
-      if ( $action == 'set_http' )
-      {
-        // Check if this action is enabled
-        if ( getConfig('avatar_upload_http', 1) !== 1 )
-        {
-          // non-localized, only appears on hack attempt
-          echo '<div class="error-box">Uploads over HTTP are disabled.</div>';
-          break;
-        }
-        // Download the file
-        require_once( ENANO_ROOT . '/includes/http.php' );
-        
-        if ( !preg_match('/^http:\/\/((?:[a-z0-9-\.]+|\[[a-f0-9:]+\]))(:([0-9]+))?\/(.+)$/', $_POST['avatar_http_url'], $match) )
-        {
-          echo '<div class="error-box">' . $lang->get('usercp_avatar_invalid_url') . '</div>';
-          break;
-        }
-        
-        $hostname = $match[1];
-        $uri = '/' . $match[4];
-        $port = ( $match[3] ) ? intval($match[3]) : 80;
-        $max_size = intval(getConfig('avatar_max_size'));
-        
-        // Get temporary file
-        $tempfile = tempnam(false, "enanoavatar_{$user_id}");
-        if ( !$tempfile )
-          echo '<div class="error-box">Error getting temp file.</div>';
-        
-        @unlink($tempfile);
-        $request = new Request_HTTP($hostname, $uri, 'GET', $port);
-        // max download size: 2MB, keeps things reasonable
-        // note: we'll try to scale the image down before checking filesize
-        $result = $request->write_response_to_file($tempfile, 1160, 2097152);
-        if ( !$result || $request->response_code != HTTP_OK )
-        {
-          @unlink($tempfile);
-          echo '<div class="error-box">' . $lang->get('usercp_avatar_bad_write') . '</div>';
-          break;
-        }
-        
-        // Response written. Proceed to validation...
-      }
-      else
-      {
-        // Check if this action is enabled
-        if ( getConfig('avatar_upload_file', 1) !== 1 )
-        {
-          // non-localized, only appears on hack attempt
-          echo '<div class="error-box">Uploads from the browser are disabled.</div>';
-          break;
-        }
-        
-        $max_size = intval(getConfig('avatar_max_size'));
-        
-        $file =& $_FILES['avatar_file'];
-        $tempfile =& $file['tmp_name'];
-      }
-      $file_type = get_image_filetype($tempfile);
-      if ( !$file_type )
-      {
-        @unlink($tempfile);
-        echo '<div class="error-box">' . $lang->get('usercp_avatar_bad_filetype') . '</div>';
-        break;
-      }
-      
-      $avi_path_new = ENANO_ROOT . '/' . getConfig('avatar_directory') . '/' . $user_id . '.' . $file_type;
-      
-      // The file type is good - validate dimensions and animation
-      switch($file_type)
-      {
-        case 'png':
-          $is_animated = is_png_animated($tempfile);
-          $dimensions = png_get_dimensions($tempfile);
-          break;
-        case 'gif':
-          $is_animated = is_gif_animated($tempfile);
-          $dimensions = gif_get_dimensions($tempfile);
-          break;
-        case 'jpg':
-          $is_animated = false;
-          $dimensions = jpg_get_dimensions($tempfile);
-          break;
-        default:
-          echo '<div class="error-box">API mismatch</div>';
-          break 2;
-      }
-      // Did we get invalid size data? If so the image is probably corrupt.
-      if ( !$dimensions )
-      {
-        @unlink($tempfile);
-        echo '<div class="error-box">' . $lang->get('usercp_avatar_corrupt_image') . '</div>';
-        break;
-      }
-      // Is the image animated?
-      if ( $is_animated && getConfig('avatar_enable_anim', 0) !== 1 )
-      {
-        @unlink($tempfile);
-        echo '<div class="error-box">' . $lang->get('usercp_avatar_disallowed_animation') . '</div>';
-        break;
-      }
-      // Check image dimensions
-      list($image_x, $image_y) = $dimensions;
-      $max_x = intval(getConfig('avatar_max_width'));
-      $max_y = intval(getConfig('avatar_max_height'));
-      if ( $image_x > $max_x || $image_y > $max_y )
-      {
-        // try to scale the image
-        try
-        {
-          @rename($tempfile, "$tempfile-unscaled.$file_type");
-          $scale_result = scale_image("$tempfile-unscaled.$file_type", "$tempfile.$file_type", $max_x, $max_y, true);
-          if ( $scale_result )
-          {
-            if ( !(@unlink("$tempfile-unscaled.$file_type") && @rename("$tempfile.$file_type", $tempfile)) )
-            {
-              // scale failed
-              @unlink("$tempfile-scale.$file_type");
-              echo '<div class="error-box">Rename failure: ' . $lang->get('usercp_avatar_too_large') . '</div>';
-              break;
-            }
-          }
-          else
-          {
-            @unlink($tempfile);
-            @unlink("$tempfile-unscaled.$file_type");
-            echo '<div class="error-box">Scale failure: ' . $lang->get('usercp_avatar_too_large') . '</div>';
-            break;
-          }
-        }
-        catch ( Exception $e )
-        {
-          // If we get here, the scaling process most definitely failed.
-          echo '<div class="error-box">EXCEPTION: ' . $lang->get('usercp_avatar_too_large') . '</div>';
-          break;
-        }
-      }
-      // Check file size last, so that the scale operation is considered
-      if ( filesize($tempfile) > $max_size )
-      {
-        @unlink($tempfile);
-        echo '<div class="error-box">' . $lang->get('usercp_avatar_file_too_large') . '</div>';
-        break;
-      }
-      // All good!
-      @unlink($avi_path);
-      if ( rename($tempfile, $avi_path_new) )
-      {
-        $q = $db->sql_query('UPDATE ' . table_prefix . "users SET user_has_avatar = 1, avatar_type = '$file_type' WHERE user_id = {$user_id};");
-        if ( !$q )
-          $db->_die('Avatar CP updating users table after successful avatar upload');
-        $has_avi = 1;
-        $avi_type = $file_type;
-        $quiet || print '<div class="info-box">' . $lang->get('usercp_avatar_upload_success') . '</div>';
-      }
-      else
-      {
-        echo '<div class="error-box">' . $lang->get('usercp_avatar_move_failed') . '</div>';
-      }
-      $had_a_boo_boo = false;
-      break;
-    case 'set_gravatar':
-      // set avatar to use Gravatar
-      // make sure we're allowed to do this
-      if ( getConfig('avatar_upload_gravatar') != '1' )
-      {
-        // access denied
-        break;
-      }
-      // first, remove old image
-      if ( $has_avi )
-      {
-        // First switch the avatar off
-        $q = $db->sql_query('UPDATE ' . table_prefix . 'users SET user_has_avatar = 0 WHERE user_id = ' . $user_id . ';');
-        if ( !$q )
-          $db->_die('Avatar CP switching user avatar off');
-        
-        @unlink($avi_path);
-      }
-      // set to gravatar mode
-      $q = $db->sql_query('UPDATE ' . table_prefix . 'users SET user_has_avatar = 1, avatar_type = \'grv\' WHERE user_id = ' . $user_id . ';');
-      if ( !$q )
-        $db->_die('Avatar CP switching user avatar off');
-        
-      $has_avi = 1;
-      $quiet || print '<div class="info-box">' . $lang->get('usercp_avatar_gravatar_success') . '</div>';
-      $had_a_boo_boo = false;
-      break;
-  }
-  return array($has_avi, $avi_type, $had_a_boo_boo);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	$had_a_boo_boo = true;
+	
+	// Determine current avatar
+	$q = $db->sql_query('SELECT user_has_avatar, avatar_type FROM ' . table_prefix . 'users WHERE user_id = ' . $session->user_id . ';');
+	if ( !$q )
+		$db->_die('Avatar CP selecting user\'s avatar data');
+	
+	list($has_avi, $avi_type) = $db->fetchrow_num();
+	
+	$action = ( isset($_POST['avatar_action']) ) ? $_POST['avatar_action'] : 'keep';
+	$avi_path = ENANO_ROOT . '/' . getConfig('avatar_directory') . '/' . $user_id . '.' . $avi_type;
+	switch($action)
+	{
+		case 'keep':
+		default:
+			$had_a_boo_boo = false;
+			break;
+		case 'remove':
+			if ( $has_avi )
+			{
+				// First switch the avatar off
+				$q = $db->sql_query('UPDATE ' . table_prefix . 'users SET user_has_avatar = 0 WHERE user_id = ' . $user_id . ';');
+				if ( !$q )
+					$db->_die('Avatar CP switching user avatar off');
+				
+				if ( @unlink($avi_path) )
+				{
+					$quiet || print '<div class="info-box">' . $lang->get('usercp_avatar_delete_success') . '</div>';
+				}
+				$has_avi = 0;
+			}
+			$had_a_boo_boo = false;
+			break;
+		case 'set_http':
+		case 'set_file':
+			// Hackish way to preserve the UNIX philosophy of reusing as much code as possible
+			if ( $action == 'set_http' )
+			{
+				// Check if this action is enabled
+				if ( getConfig('avatar_upload_http', 1) !== 1 )
+				{
+					// non-localized, only appears on hack attempt
+					echo '<div class="error-box">Uploads over HTTP are disabled.</div>';
+					break;
+				}
+				// Download the file
+				require_once( ENANO_ROOT . '/includes/http.php' );
+				
+				if ( !preg_match('/^http:\/\/((?:[a-z0-9-\.]+|\[[a-f0-9:]+\]))(:([0-9]+))?\/(.+)$/', $_POST['avatar_http_url'], $match) )
+				{
+					echo '<div class="error-box">' . $lang->get('usercp_avatar_invalid_url') . '</div>';
+					break;
+				}
+				
+				$hostname = $match[1];
+				$uri = '/' . $match[4];
+				$port = ( $match[3] ) ? intval($match[3]) : 80;
+				$max_size = intval(getConfig('avatar_max_size'));
+				
+				// Get temporary file
+				$tempfile = tempnam(false, "enanoavatar_{$user_id}");
+				if ( !$tempfile )
+					echo '<div class="error-box">Error getting temp file.</div>';
+				
+				@unlink($tempfile);
+				$request = new Request_HTTP($hostname, $uri, 'GET', $port);
+				// max download size: 2MB, keeps things reasonable
+				// note: we'll try to scale the image down before checking filesize
+				$result = $request->write_response_to_file($tempfile, 1160, 2097152);
+				if ( !$result || $request->response_code != HTTP_OK )
+				{
+					@unlink($tempfile);
+					echo '<div class="error-box">' . $lang->get('usercp_avatar_bad_write') . '</div>';
+					break;
+				}
+				
+				// Response written. Proceed to validation...
+			}
+			else
+			{
+				// Check if this action is enabled
+				if ( getConfig('avatar_upload_file', 1) !== 1 )
+				{
+					// non-localized, only appears on hack attempt
+					echo '<div class="error-box">Uploads from the browser are disabled.</div>';
+					break;
+				}
+				
+				$max_size = intval(getConfig('avatar_max_size'));
+				
+				$file =& $_FILES['avatar_file'];
+				$tempfile =& $file['tmp_name'];
+			}
+			$file_type = get_image_filetype($tempfile);
+			if ( !$file_type )
+			{
+				@unlink($tempfile);
+				echo '<div class="error-box">' . $lang->get('usercp_avatar_bad_filetype') . '</div>';
+				break;
+			}
+			
+			$avi_path_new = ENANO_ROOT . '/' . getConfig('avatar_directory') . '/' . $user_id . '.' . $file_type;
+			
+			// The file type is good - validate dimensions and animation
+			switch($file_type)
+			{
+				case 'png':
+					$is_animated = is_png_animated($tempfile);
+					$dimensions = png_get_dimensions($tempfile);
+					break;
+				case 'gif':
+					$is_animated = is_gif_animated($tempfile);
+					$dimensions = gif_get_dimensions($tempfile);
+					break;
+				case 'jpg':
+					$is_animated = false;
+					$dimensions = jpg_get_dimensions($tempfile);
+					break;
+				default:
+					echo '<div class="error-box">API mismatch</div>';
+					break 2;
+			}
+			// Did we get invalid size data? If so the image is probably corrupt.
+			if ( !$dimensions )
+			{
+				@unlink($tempfile);
+				echo '<div class="error-box">' . $lang->get('usercp_avatar_corrupt_image') . '</div>';
+				break;
+			}
+			// Is the image animated?
+			if ( $is_animated && getConfig('avatar_enable_anim', 0) !== 1 )
+			{
+				@unlink($tempfile);
+				echo '<div class="error-box">' . $lang->get('usercp_avatar_disallowed_animation') . '</div>';
+				break;
+			}
+			// Check image dimensions
+			list($image_x, $image_y) = $dimensions;
+			$max_x = intval(getConfig('avatar_max_width'));
+			$max_y = intval(getConfig('avatar_max_height'));
+			if ( $image_x > $max_x || $image_y > $max_y )
+			{
+				// try to scale the image
+				try
+				{
+					@rename($tempfile, "$tempfile-unscaled.$file_type");
+					$scale_result = scale_image("$tempfile-unscaled.$file_type", "$tempfile.$file_type", $max_x, $max_y, true);
+					if ( $scale_result )
+					{
+						if ( !(@unlink("$tempfile-unscaled.$file_type") && @rename("$tempfile.$file_type", $tempfile)) )
+						{
+							// scale failed
+							@unlink("$tempfile-scale.$file_type");
+							echo '<div class="error-box">Rename failure: ' . $lang->get('usercp_avatar_too_large') . '</div>';
+							break;
+						}
+					}
+					else
+					{
+						@unlink($tempfile);
+						@unlink("$tempfile-unscaled.$file_type");
+						echo '<div class="error-box">Scale failure: ' . $lang->get('usercp_avatar_too_large') . '</div>';
+						break;
+					}
+				}
+				catch ( Exception $e )
+				{
+					// If we get here, the scaling process most definitely failed.
+					echo '<div class="error-box">EXCEPTION: ' . $lang->get('usercp_avatar_too_large') . '</div>';
+					break;
+				}
+			}
+			// Check file size last, so that the scale operation is considered
+			if ( filesize($tempfile) > $max_size )
+			{
+				@unlink($tempfile);
+				echo '<div class="error-box">' . $lang->get('usercp_avatar_file_too_large') . '</div>';
+				break;
+			}
+			// All good!
+			@unlink($avi_path);
+			if ( rename($tempfile, $avi_path_new) )
+			{
+				$q = $db->sql_query('UPDATE ' . table_prefix . "users SET user_has_avatar = 1, avatar_type = '$file_type' WHERE user_id = {$user_id};");
+				if ( !$q )
+					$db->_die('Avatar CP updating users table after successful avatar upload');
+				$has_avi = 1;
+				$avi_type = $file_type;
+				$quiet || print '<div class="info-box">' . $lang->get('usercp_avatar_upload_success') . '</div>';
+			}
+			else
+			{
+				echo '<div class="error-box">' . $lang->get('usercp_avatar_move_failed') . '</div>';
+			}
+			$had_a_boo_boo = false;
+			break;
+		case 'set_gravatar':
+			// set avatar to use Gravatar
+			// make sure we're allowed to do this
+			if ( getConfig('avatar_upload_gravatar') != '1' )
+			{
+				// access denied
+				break;
+			}
+			// first, remove old image
+			if ( $has_avi )
+			{
+				// First switch the avatar off
+				$q = $db->sql_query('UPDATE ' . table_prefix . 'users SET user_has_avatar = 0 WHERE user_id = ' . $user_id . ';');
+				if ( !$q )
+					$db->_die('Avatar CP switching user avatar off');
+				
+				@unlink($avi_path);
+			}
+			// set to gravatar mode
+			$q = $db->sql_query('UPDATE ' . table_prefix . 'users SET user_has_avatar = 1, avatar_type = \'grv\' WHERE user_id = ' . $user_id . ';');
+			if ( !$q )
+				$db->_die('Avatar CP switching user avatar off');
+				
+			$has_avi = 1;
+			$quiet || print '<div class="info-box">' . $lang->get('usercp_avatar_gravatar_success') . '</div>';
+			$had_a_boo_boo = false;
+			break;
+	}
+	return array($has_avi, $avi_type, $had_a_boo_boo);
 }
 
 ?>
--- a/plugins/admin/CacheManager.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/CacheManager.php	Sun Mar 28 23:10:46 2010 -0400
@@ -15,306 +15,306 @@
 
 function page_Admin_CacheManager()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $cache;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  // validation/actions
-  if ( isset($_POST['refresh']) || isset($_POST['clear']) )
-  {
-    $success = false;
-    
-    $target = ( isset($_POST['refresh']) ) ? $_POST['refresh'] : $_POST['clear'];
-    $do_refresh = isset($_POST['refresh']);
-    switch ( $target )
-    {
-      case 'page':
-        $success = $cache->purge('page_meta');
-        if ( $do_refresh && $success )
-          $success = $paths->update_metadata_cache();
-        break;
-      case 'ranks':
-        $success = $cache->purge('ranks');
-        if ( $do_refresh && $success )
-          $success = generate_cache_userranks();
-        break;
-      case 'sidebar':
-        $success = $cache->purge('anon_sidebar');
-        break;
-      case 'plugins':
-        $success = $cache->purge('plugins');
-        if ( $do_refresh && $success )
-          $success = $plugins->generate_plugins_cache();
-        break;
-      case 'template':
-        if ( $dh = opendir(ENANO_ROOT . '/cache') )
-        {
-          while ( $file = @readdir($dh) )
-          {
-            $fullpath = ENANO_ROOT . "/cache/$file";
-            // we don't want to mess with directories
-            if ( !is_file($fullpath) )
-              continue;
-            
-            if ( preg_match('/\.(?:tpl|css)\.php$/', $file) )
-            {
-              unlink($fullpath);
-            }
-          }
-          $success = true;
-        }
-        break;
-      case 'aes':
-        $success = @unlink(ENANO_ROOT . '/cache/aes_decrypt.php');
-        break;
-      case 'lang':
-        if ( $dh = opendir(ENANO_ROOT . '/cache') )
-        {
-          while ( $file = @readdir($dh) )
-          {
-            $fullpath = ENANO_ROOT . "/cache/$file";
-            // we don't want to mess with directories
-            if ( !is_file($fullpath) )
-              continue;
-            
-            if ( preg_match('/^lang_json_(?:[a-f0-9]+?)\.php$/', $file) || preg_match('/^(?:cache_)?lang_(?:[0-9]+?)\.php$/', $file) )
-              unlink($fullpath);
-          }
-          $success = true;
-        }
-        if ( $do_refresh && $success )
-        {
-          // for each language in the database, call regen_caches()
-          $q = $db->sql_query('SELECT lang_id FROM ' . table_prefix . 'language;');
-          if ( !$q )
-            $db->_die();
-          while ( $row = $db->fetchrow($q) )
-          {
-            $lang_local = ( $row['lang_id'] == $lang->lang_id ) ? $lang : new Language($row['lang_id']);
-            $success = $lang_local->regen_caches();
-            if ( !$success )
-              break 2;
-          }
-        }
-        break;
-      case 'js':
-        if ( $dh = opendir(ENANO_ROOT . '/cache') )
-        {
-          while ( $file = @readdir($dh) )
-          {
-            $fullpath = ENANO_ROOT . "/cache/$file";
-            // we don't want to mess with directories
-            if ( !is_file($fullpath) )
-              continue;
-            
-            // compressed javascript
-            if ( preg_match('/^jsres_(?:[A-z0-9_-]+)\.js\.json$/', $file) )
-              unlink($fullpath);
-            // tinymce stuff
-            else if ( preg_match('/^tiny_mce_(?:[a-f0-9]+)\.gz$/', $file) )
-              unlink($fullpath);
-          }
-          $success = true;
-        }
-        break;
-      case 'thumbs':
-        if ( $dh = opendir(ENANO_ROOT . '/cache') )
-        {
-          while ( $file = @readdir($dh) )
-          {
-            $fullpath = ENANO_ROOT . "/cache/$file";
-            // we don't want to mess with directories
-            if ( !is_file($fullpath) )
-              continue;
-            
-            if ( preg_match('/^(?:[a-z0-9\._,-]+)-(?:[0-9]{10})-[0-9]+x[0-9]+\.([a-z0-9_-]+)$/i', $file) )
-              unlink($fullpath);
-          }
-          $success = true;
-        }
-        break;
-      case 'wikieditnotice':
-        $cache->purge('wiki_edit_notice');
-        if ( $do_refresh )
-          $template->get_wiki_edit_notice();
-        
-        $success = true;
-        break;
-      case 'all':
-        $success = purge_all_caches();
-        if ( $do_refresh )
-        {
-          //
-          // refresh all static (non-incremental) caches
-          //
-          
-          // pages
-          $success = $paths->update_metadata_cache();
-          if ( !$success )
-            break;
-          
-          // user ranks
-          $success = generate_cache_userranks();
-          if ( !$success )
-            break;
-          
-          // plugins
-          $success = $plugins->generate_plugins_cache();
-          if ( !$success )
-            break;
-          
-          // wiki edit notice
-          $template->get_wiki_edit_notice();
-          
-          // languages
-          $q = $db->sql_query('SELECT lang_id FROM ' . table_prefix . 'language;');
-          if ( !$q )
-            $db->_die();
-          while ( $row = $db->fetchrow($q) )
-          {
-            $lang_local = ( $row['lang_id'] == $lang->lang_id ) ? $lang : new Language($row['lang_id']);
-            $success = $lang_local->regen_caches();
-            if ( !$success )
-              break 2;
-          }
-        }
-        break;
-      default:
-        $code = $plugins->setHook('acp_cache_manager_action');
-        foreach ( $code as $cmd )
-        {
-          eval($cmd);
-        }
-        break;
-    }
-    if ( $success )
-    {
-      echo '<div class="info-box">' . $lang->get('acpcm_msg_action_success') . '</div>';
-    }
-    else
-    {
-      echo '<div class="error-box">' . $lang->get('acpcm_err_action_failed') . '</div>';
-    }
-  }
-  else if ( isset($_POST['save']) )
-  {
-    $config_value = ( isset($_POST['cache_thumbs']) ) ? '1' : '0';
-    setConfig('cache_thumbs', $config_value);
-    echo '<div class="info-box">' . $lang->get('acpcm_msg_action_success') . '</div>';
-  }
-  
-  echo '<h3><img alt=" " src="' . scriptPath . '/images/icons/applets/cachemanager.png" />&nbsp;&nbsp;&nbsp;' . $lang->get('acpcm_heading_main') . '</h3>';
-  echo '<p>' . $lang->get('acpcm_intro') . '</p>';
-  
-  echo '<div class="warning-box">' . $lang->get('acpcm_msg_refresh_warning') . '</div>';
-  
-  acp_start_form();
-  ?>
-  <div class="tblholder">
-    <table border="0" cellspacing="1" cellpadding="4">
-      <!-- HEADER -->
-      <tr>
-        <th colspan="2">
-          <?php echo $lang->get('acpcm_table_header'); ?>
-        </th>
-      </tr>
-      
-      <!-- ENABLE CACHE -->
-      <tr>
-        <td class="row1" colspan="2">
-          <label>
-            <input type="checkbox" name="cache_thumbs"<?php if ( getConfig('cache_thumbs') == '1' ) echo ' checked="checked"'; ?> />
-            <?php echo $lang->get('acpcm_lbl_enable_cache'); ?>
-          </label>
-          <br />
-          <small>
-            <?php echo $lang->get('acpcm_hint_enable_cache'); ?>
-          </small>
-        </td>
-      </tr>
-      
-      <!-- CLEAR ALL -->
-      <tr>
-      <td class="row2" style="width: 120px; text-align: center;">
-          <button name="clear" value="all"><?php echo $lang->get('acpcm_btn_clear_all'); ?></button>
-        </td>
-        <td class="row2">
-          <?php echo $lang->get('acpcm_hint_clear_all'); ?>
-        </td>
-      </tr>
-      
-      <?php
-      // if caching is disabled, might as well break off here
-      if ( getConfig('cache_thumbs') == '1' ):
-      ?>
-      
-      <!-- REFRESH ALL -->
-      <tr>
-        <td class="row1" style="text-align: center;">
-          <button name="refresh" value="all"><?php echo $lang->get('acpcm_btn_refresh_all'); ?></button>
-        </td>
-        <td class="row1">
-          <?php echo $lang->get('acpcm_hint_refresh_all'); ?>
-        </td>
-      </tr>
-      
-      <!-- INDIVIDUAL CACHES -->
-      <tr>
-        <th class="subhead" colspan="2">
-          <?php echo $lang->get('acpcm_th_individual_caches'); ?>
-        </th>
-      </tr>
-      
-      <?php
-      $class = 'row2';
-      $cache_list = array('page', 'ranks', 'sidebar', 'plugins', 'template', 'aes', 'lang', 'js', 'thumbs', 'wikieditnotice');
-      $code = $plugins->setHook('acp_cache_manager_list_caches');
-      foreach ( $code as $cmd )
-      {
-        eval($cmd);
-      }
-      foreach ( $cache_list as $target )
-      {
-        $class = ( $class == 'row1' ) ? 'row2' : 'row1';
-        ?><tr>
-        <td class="<?php echo $class; ?>" style="text-align: center;">
-          <button name="refresh" value="<?php echo $target; ?>"<?php if ( in_array($target, array('template', 'sidebar', 'aes', 'js', 'thumbs')) ) echo ' disabled="disabled"'; ?>>
-            <?php echo $lang->get('acpcm_btn_refresh'); ?>
-          </button>
-          <button name="clear" value="<?php echo $target; ?>">
-            <?php echo $lang->get('acpcm_btn_clear'); ?>
-          </button>
-        </td>
-        <td class="<?php echo $class; ?>">
-          <b><?php echo $lang->get("acpcm_cache_{$target}_desc_title"); ?></b> &ndash;
-          <?php echo $lang->get("acpcm_cache_{$target}_desc_body"); ?>
-        </td>
-        </tr>
-      <?php
-      }
-      
-      // getConfig('cache_thumbs') == '1'
-      endif;
-      ?>
-      
-      <!-- SAVE CHANGES -->
-      <tr>
-        <th colspan="2" class="subhead">
-          <input type="submit" name="save" value="<?php echo $lang->get('etc_save_changes'); ?>" style="font-weight: bold;" />
-          <input type="submit" name="cancel" value="<?php echo $lang->get('etc_cancel'); ?>" />
-        </th>
-      </tr>
-    </table>
-  </div>
-  <?php
-  echo '</form>';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $cache;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	// validation/actions
+	if ( isset($_POST['refresh']) || isset($_POST['clear']) )
+	{
+		$success = false;
+		
+		$target = ( isset($_POST['refresh']) ) ? $_POST['refresh'] : $_POST['clear'];
+		$do_refresh = isset($_POST['refresh']);
+		switch ( $target )
+		{
+			case 'page':
+				$success = $cache->purge('page_meta');
+				if ( $do_refresh && $success )
+					$success = $paths->update_metadata_cache();
+				break;
+			case 'ranks':
+				$success = $cache->purge('ranks');
+				if ( $do_refresh && $success )
+					$success = generate_cache_userranks();
+				break;
+			case 'sidebar':
+				$success = $cache->purge('anon_sidebar');
+				break;
+			case 'plugins':
+				$success = $cache->purge('plugins');
+				if ( $do_refresh && $success )
+					$success = $plugins->generate_plugins_cache();
+				break;
+			case 'template':
+				if ( $dh = opendir(ENANO_ROOT . '/cache') )
+				{
+					while ( $file = @readdir($dh) )
+					{
+						$fullpath = ENANO_ROOT . "/cache/$file";
+						// we don't want to mess with directories
+						if ( !is_file($fullpath) )
+							continue;
+						
+						if ( preg_match('/\.(?:tpl|css)\.php$/', $file) )
+						{
+							unlink($fullpath);
+						}
+					}
+					$success = true;
+				}
+				break;
+			case 'aes':
+				$success = @unlink(ENANO_ROOT . '/cache/aes_decrypt.php');
+				break;
+			case 'lang':
+				if ( $dh = opendir(ENANO_ROOT . '/cache') )
+				{
+					while ( $file = @readdir($dh) )
+					{
+						$fullpath = ENANO_ROOT . "/cache/$file";
+						// we don't want to mess with directories
+						if ( !is_file($fullpath) )
+							continue;
+						
+						if ( preg_match('/^lang_json_(?:[a-f0-9]+?)\.php$/', $file) || preg_match('/^(?:cache_)?lang_(?:[0-9]+?)\.php$/', $file) )
+							unlink($fullpath);
+					}
+					$success = true;
+				}
+				if ( $do_refresh && $success )
+				{
+					// for each language in the database, call regen_caches()
+					$q = $db->sql_query('SELECT lang_id FROM ' . table_prefix . 'language;');
+					if ( !$q )
+						$db->_die();
+					while ( $row = $db->fetchrow($q) )
+					{
+						$lang_local = ( $row['lang_id'] == $lang->lang_id ) ? $lang : new Language($row['lang_id']);
+						$success = $lang_local->regen_caches();
+						if ( !$success )
+							break 2;
+					}
+				}
+				break;
+			case 'js':
+				if ( $dh = opendir(ENANO_ROOT . '/cache') )
+				{
+					while ( $file = @readdir($dh) )
+					{
+						$fullpath = ENANO_ROOT . "/cache/$file";
+						// we don't want to mess with directories
+						if ( !is_file($fullpath) )
+							continue;
+						
+						// compressed javascript
+						if ( preg_match('/^jsres_(?:[A-z0-9_-]+)\.js\.json$/', $file) )
+							unlink($fullpath);
+						// tinymce stuff
+						else if ( preg_match('/^tiny_mce_(?:[a-f0-9]+)\.gz$/', $file) )
+							unlink($fullpath);
+					}
+					$success = true;
+				}
+				break;
+			case 'thumbs':
+				if ( $dh = opendir(ENANO_ROOT . '/cache') )
+				{
+					while ( $file = @readdir($dh) )
+					{
+						$fullpath = ENANO_ROOT . "/cache/$file";
+						// we don't want to mess with directories
+						if ( !is_file($fullpath) )
+							continue;
+						
+						if ( preg_match('/^(?:[a-z0-9\._,-]+)-(?:[0-9]{10})-[0-9]+x[0-9]+\.([a-z0-9_-]+)$/i', $file) )
+							unlink($fullpath);
+					}
+					$success = true;
+				}
+				break;
+			case 'wikieditnotice':
+				$cache->purge('wiki_edit_notice');
+				if ( $do_refresh )
+					$template->get_wiki_edit_notice();
+				
+				$success = true;
+				break;
+			case 'all':
+				$success = purge_all_caches();
+				if ( $do_refresh )
+				{
+					//
+					// refresh all static (non-incremental) caches
+					//
+					
+					// pages
+					$success = $paths->update_metadata_cache();
+					if ( !$success )
+						break;
+					
+					// user ranks
+					$success = generate_cache_userranks();
+					if ( !$success )
+						break;
+					
+					// plugins
+					$success = $plugins->generate_plugins_cache();
+					if ( !$success )
+						break;
+					
+					// wiki edit notice
+					$template->get_wiki_edit_notice();
+					
+					// languages
+					$q = $db->sql_query('SELECT lang_id FROM ' . table_prefix . 'language;');
+					if ( !$q )
+						$db->_die();
+					while ( $row = $db->fetchrow($q) )
+					{
+						$lang_local = ( $row['lang_id'] == $lang->lang_id ) ? $lang : new Language($row['lang_id']);
+						$success = $lang_local->regen_caches();
+						if ( !$success )
+							break 2;
+					}
+				}
+				break;
+			default:
+				$code = $plugins->setHook('acp_cache_manager_action');
+				foreach ( $code as $cmd )
+				{
+					eval($cmd);
+				}
+				break;
+		}
+		if ( $success )
+		{
+			echo '<div class="info-box">' . $lang->get('acpcm_msg_action_success') . '</div>';
+		}
+		else
+		{
+			echo '<div class="error-box">' . $lang->get('acpcm_err_action_failed') . '</div>';
+		}
+	}
+	else if ( isset($_POST['save']) )
+	{
+		$config_value = ( isset($_POST['cache_thumbs']) ) ? '1' : '0';
+		setConfig('cache_thumbs', $config_value);
+		echo '<div class="info-box">' . $lang->get('acpcm_msg_action_success') . '</div>';
+	}
+	
+	echo '<h3><img alt=" " src="' . scriptPath . '/images/icons/applets/cachemanager.png" />&nbsp;&nbsp;&nbsp;' . $lang->get('acpcm_heading_main') . '</h3>';
+	echo '<p>' . $lang->get('acpcm_intro') . '</p>';
+	
+	echo '<div class="warning-box">' . $lang->get('acpcm_msg_refresh_warning') . '</div>';
+	
+	acp_start_form();
+	?>
+	<div class="tblholder">
+		<table border="0" cellspacing="1" cellpadding="4">
+			<!-- HEADER -->
+			<tr>
+				<th colspan="2">
+					<?php echo $lang->get('acpcm_table_header'); ?>
+				</th>
+			</tr>
+			
+			<!-- ENABLE CACHE -->
+			<tr>
+				<td class="row1" colspan="2">
+					<label>
+						<input type="checkbox" name="cache_thumbs"<?php if ( getConfig('cache_thumbs') == '1' ) echo ' checked="checked"'; ?> />
+						<?php echo $lang->get('acpcm_lbl_enable_cache'); ?>
+					</label>
+					<br />
+					<small>
+						<?php echo $lang->get('acpcm_hint_enable_cache'); ?>
+					</small>
+				</td>
+			</tr>
+			
+			<!-- CLEAR ALL -->
+			<tr>
+			<td class="row2" style="width: 120px; text-align: center;">
+					<button name="clear" value="all"><?php echo $lang->get('acpcm_btn_clear_all'); ?></button>
+				</td>
+				<td class="row2">
+					<?php echo $lang->get('acpcm_hint_clear_all'); ?>
+				</td>
+			</tr>
+			
+			<?php
+			// if caching is disabled, might as well break off here
+			if ( getConfig('cache_thumbs') == '1' ):
+			?>
+			
+			<!-- REFRESH ALL -->
+			<tr>
+				<td class="row1" style="text-align: center;">
+					<button name="refresh" value="all"><?php echo $lang->get('acpcm_btn_refresh_all'); ?></button>
+				</td>
+				<td class="row1">
+					<?php echo $lang->get('acpcm_hint_refresh_all'); ?>
+				</td>
+			</tr>
+			
+			<!-- INDIVIDUAL CACHES -->
+			<tr>
+				<th class="subhead" colspan="2">
+					<?php echo $lang->get('acpcm_th_individual_caches'); ?>
+				</th>
+			</tr>
+			
+			<?php
+			$class = 'row2';
+			$cache_list = array('page', 'ranks', 'sidebar', 'plugins', 'template', 'aes', 'lang', 'js', 'thumbs', 'wikieditnotice');
+			$code = $plugins->setHook('acp_cache_manager_list_caches');
+			foreach ( $code as $cmd )
+			{
+				eval($cmd);
+			}
+			foreach ( $cache_list as $target )
+			{
+				$class = ( $class == 'row1' ) ? 'row2' : 'row1';
+				?><tr>
+				<td class="<?php echo $class; ?>" style="text-align: center;">
+					<button name="refresh" value="<?php echo $target; ?>"<?php if ( in_array($target, array('template', 'sidebar', 'aes', 'js', 'thumbs')) ) echo ' disabled="disabled"'; ?>>
+						<?php echo $lang->get('acpcm_btn_refresh'); ?>
+					</button>
+					<button name="clear" value="<?php echo $target; ?>">
+						<?php echo $lang->get('acpcm_btn_clear'); ?>
+					</button>
+				</td>
+				<td class="<?php echo $class; ?>">
+					<b><?php echo $lang->get("acpcm_cache_{$target}_desc_title"); ?></b> &ndash;
+					<?php echo $lang->get("acpcm_cache_{$target}_desc_body"); ?>
+				</td>
+				</tr>
+			<?php
+			}
+			
+			// getConfig('cache_thumbs') == '1'
+			endif;
+			?>
+			
+			<!-- SAVE CHANGES -->
+			<tr>
+				<th colspan="2" class="subhead">
+					<input type="submit" name="save" value="<?php echo $lang->get('etc_save_changes'); ?>" style="font-weight: bold;" />
+					<input type="submit" name="cancel" value="<?php echo $lang->get('etc_cancel'); ?>" />
+				</th>
+			</tr>
+		</table>
+	</div>
+	<?php
+	echo '</form>';
 }
 
 ?>
--- a/plugins/admin/GroupManager.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/GroupManager.php	Sun Mar 28 23:10:46 2010 -0400
@@ -15,398 +15,398 @@
 
 function page_Admin_GroupManager()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  if(isset($_POST['do_create_stage1']))
-  {
-    if(!preg_match('/^([A-z0-9 -]+)$/', $_POST['create_group_name']))
-    {
-      echo '<p>' . $lang->get('acpug_err_group_name_invalid') . '</p>';
-      return;
-    }
-    echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-    echo '<div class="tblholder">
-          <table border="0" style="width:100%;" cellspacing="1" cellpadding="4">
-          <tr><th colspan="2">' . $lang->get('acpug_heading_creating_group') . ' '.htmlspecialchars($_POST['create_group_name']).'</th></tr>
-          <tr>
-            <td class="row1">' . $lang->get('acpug_field_group_mod') . '</td><td class="row1">' . $template->username_field('group_mod') . '</td>
-          </tr>
-          <tr><td class="row2">' . $lang->get('acpug_field_group_type') . '</td><td class="row2">
-            <label><input type="radio" name="group_status" value="'.GROUP_CLOSED.'" checked="checked" /> ' . $lang->get('groupcp_type_hidden') . '</label><br />
-            <label><input type="radio" name="group_status" value="'.GROUP_REQUEST.'" /> ' . $lang->get('groupcp_type_closed') . '</label><br />
-            <label><input type="radio" name="group_status" value="'.GROUP_OPEN.'" /> ' . $lang->get('groupcp_type_request') . '</label><br />
-            <label><input type="radio" name="group_status" value="'.GROUP_HIDDEN.'" /> ' . $lang->get('groupcp_type_open') . '</label>
-          </td></tr>
-          <tr>
-            <th class="subhead" colspan="2">
-              <input type="hidden" name="create_group_name" value="'.htmlspecialchars($_POST['create_group_name']).'" />
-              <input type="submit" name="do_create_stage2" value="' . $lang->get('acpug_btn_create_stage2') . '" />
-            </th>
-          </tr>
-          </table>
-          </div>';
-    echo '</form>';
-    return;
-  }
-  elseif(isset($_POST['do_create_stage2']))
-  {
-    if(!preg_match('/^([A-z0-9 -]+)$/', $_POST['create_group_name']))
-    {
-      echo '<p>' . $lang->get('acpug_err_group_name_invalid') . '</p>';
-      return;
-    }
-    if(!in_array(intval($_POST['group_status']), Array(GROUP_CLOSED, GROUP_OPEN, GROUP_HIDDEN, GROUP_REQUEST)))
-    {
-      echo '<p>Hacking attempt</p>';
-      return;
-    }
-    $e = $db->sql_query('SELECT group_id FROM '.table_prefix.'groups WHERE group_name=\''.$db->escape($_POST['create_group_name']).'\';');
-    if(!$e)
-    {
-      echo $db->get_error();
-      return;
-    }
-    if($db->numrows() > 0)
-    {
-      echo '<p>' . $lang->get('acpug_err_already_exist') . '</p>';
-      return;
-    }
-    $db->free_result();
-    $q = $db->sql_query('INSERT INTO '.table_prefix.'groups(group_name,group_type) VALUES( \''.$db->escape($_POST['create_group_name']).'\', ' . intval($_POST['group_status']) . ' )');
-    if(!$q)
-    {
-      echo $db->get_error();
-      return;
-    }
-    $e = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($_POST['group_mod']).'\';');
-    if(!$e)
-    {
-      echo $db->get_error();
-      return;
-    }
-    if($db->numrows() < 1)
-    {
-      echo '<p>' . $lang->get('acpug_err_bad_username') . '</p>';
-      return;
-    }
-    $row = $db->fetchrow();
-    $id = $row['user_id'];
-    $db->free_result();
-    $e = $db->sql_query('SELECT group_id FROM '.table_prefix.'groups WHERE group_name=\''.$db->escape($_POST['create_group_name']).'\';');
-    if(!$e)
-    {
-      echo $db->get_error();
-      return;
-    }
-    if($db->numrows() < 1)
-    {
-      echo '<p>' . $lang->get('acpug_err_bad_insert_id') . '</p>';
-      return;
-    }
-    $row = $db->fetchrow();
-    $gid = $row['group_id'];
-    $db->free_result();
-    $e = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id,is_mod) VALUES('.$gid.', '.$id.', 1);');
-    if(!$e)
-    {
-      echo $db->get_error();
-      return;
-    }
-    $g_name = htmlspecialchars($_POST['create_group_name']);
-    echo "<div class='info-box'>
-            <b>" . $lang->get('acpug_heading_info') . "</b><br />
-            " . $lang->get('acpug_msg_create_success', array('g_name' => $g_name)) . "
-          </div>";
-  }
-  if(isset($_POST['do_edit']) || isset($_POST['edit_do']))
-  {
-    // Fetch the group name
-    $q = $db->sql_query('SELECT group_name,system_group,group_rank FROM '.table_prefix.'groups WHERE group_id='.intval($_POST['group_edit_id']).';');
-    if(!$q)
-    {
-      echo $db->get_error();
-      return;
-    }
-    if($db->numrows() < 1)
-    {
-      echo '<p>Error: couldn\'t look up group name</p>';
-    }
-    $row = $db->fetchrow();
-    $name = htmlspecialchars($row['group_name']);
-    $db->free_result();
-    if(isset($_POST['edit_do']))
-    {
-      if(isset($_POST['edit_do']['del_group']))
-      {
-        if ( $row['system_group'] == 1 )
-        {
-          echo '<div class="error-box">' . $lang->get('acpug_err_nodelete_system_group', array('g_name' => $name)) . '</div>';
-        }
-        else
-        {
-          $q = $db->sql_query('DELETE FROM '.table_prefix.'group_members WHERE group_id='.intval($_POST['group_edit_id']).';');
-          if(!$q)
-          {
-            echo $db->get_error();
-            return;
-          }
-          $q = $db->sql_query('DELETE FROM '.table_prefix.'groups WHERE group_id='.intval($_POST['group_edit_id']).';');
-          if(!$q)
-          {
-            echo $db->get_error();
-            return;
-          }
-          echo '<div class="info-box">' . $lang->get('acpug_msg_delete_success', array('g_name' => $name, 'a_flags' => 'href="javascript:ajaxPage(\'' . $paths->nslist['Admin'] . 'GroupManager\');"')) . '</div>';
-          return;
-        }
-      }
-      if(isset($_POST['edit_do']['save_name']))
-      {
-        if(!preg_match('/^([A-z0-9 -]+)$/', $_POST['group_name']))
-        {
-          echo '<p>' . $lang->get('acpug_err_group_name_invalid') . '</p>';
-          return;
-        }
-        // determine rank
-        $group_rank =& $_POST['group_rank'];
-        if ( $_POST['group_rank'] !== 'NULL' )
-        {
-          $group_rank = intval($group_rank);
-          if ( empty($group_rank) )
-          {
-            echo '<p>Hacked rank ID</p>';
-            return;
-          }
-        }
-        $row['group_rank'] = $group_rank;
-        $q = $db->sql_query('UPDATE '.table_prefix.'groups SET group_name=\''.$db->escape($_POST['group_name']).'\',group_rank = ' . $group_rank . '
-            WHERE group_id='.intval($_POST['group_edit_id']).';');
-        if(!$q)
-        {
-          echo $db->get_error();
-          return;
-        }
-        else
-        {
-          echo '<div class="info-box" style="margin: 0 0 10px 0;"">
-                  ' . $lang->get('acpug_msg_name_update_success') . '
-                </div>';
-        }
-        $name = htmlspecialchars($_POST['group_name']);
-        
-      }
-      $q = $db->sql_query('SELECT member_id FROM '.table_prefix.'group_members
-                             WHERE group_id='.intval($_POST['group_edit_id']).';');
-      if(!$q)
-      {
-        echo $db->get_error();
-        return;
-      }
-      if($db->numrows() > 0)
-      {
-        while($delrow = $db->fetchrow($q))
-        {
-          if(isset($_POST['edit_do']['del_' . $delrow['member_id']]))
-          {
-            $e = $db->sql_query('DELETE FROM '.table_prefix.'group_members WHERE member_id='.$delrow['member_id']);
-            if(!$e)
-            {
-              echo $db->get_error();
-              return;
-            }
-          }
-        }
-      }
-      $db->free_result();
-      if(isset($_POST['edit_do']['add_member']))
-      {
-        $q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($_POST['edit_add_username']).'\';');
-        if(!$q)
-        {
-          echo $db->get_error();
-          return;
-        }
-        if($db->numrows() > 0)
-        {
-          $row = $db->fetchrow();
-          $user_id = $row['user_id'];
-          $is_mod = ( isset( $_POST['add_mod'] ) ) ? '1' : '0';
-          $q = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id,is_mod) VALUES('.intval($_POST['group_edit_id']).','.$user_id.','.$is_mod.');');
-          if(!$q)
-          {
-            echo $db->get_error();
-            return;
-          }
-          else
-          {
-            
-            echo '<div class="info-box" style="margin: 0 0 10px 0;"">
-                    ' . $lang->get('acpug_msg_user_added', array('username' => htmlspecialchars($_POST['edit_add_username']))) . '
-                  </div>';
-          }
-        }
-        else
-          echo '<div class="warning-box">' . $lang->get('acpug_err_username_not_exist', array('username' => htmlspecialchars($_POST['edit_add_username']))) . '</div>';
-      }
-      generate_cache_userranks();
-    }
-    $sg_disabled = ( $row['system_group'] == 1 ) ?
-             ' value="' . $lang->get('acpug_btn_cant_delete') . '" disabled="disabled" style="color: #FF9773" ' :
-             ' value="' . $lang->get('acpug_btn_delete_group') . '" style="color: #FF3713" ';
-    
-    // build rank list
-    $q = $db->sql_query('SELECT rank_id, rank_title FROM ' . table_prefix . 'ranks');
-    if ( !$q )
-      $db->_die();
-    $rank_list = '<option value="NULL"' . ( $row['group_rank'] === NULL ? ' selected="selected"' : '' ) . '>--</option>' . "\n";
-    while ( $rank_row = $db->fetchrow() )
-    {
-      $rank_list .= '<option value="' . $rank_row['rank_id'] . '"' . ( $rank_row['rank_id'] == $row['group_rank'] ? ' selected="selected"' : '' ) . '>' . htmlspecialchars($lang->get($rank_row['rank_title'])) . '</option>' . "\n";
-    }
-             
-    echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-    echo '<div class="tblholder">
-          <table border="0" style="width:100%;" cellspacing="1" cellpadding="4">
-          <tr><th>' . $lang->get('acpug_heading_edit_name') . '</th></tr>
-          <tr>
-            <td class="row1">
-              ' . $lang->get('acpug_field_group_name') . ' <input type="text" name="group_name" value="'.$name.'" />
-            </td>
-          </tr>
-          <tr>
-            <td class="row1">
-              ' . $lang->get('acpug_field_group_rank') . ' <select name="group_rank" />' . $rank_list . '</select>
-            </td>
-          </tr>
-          <tr>
-            <th class="subhead">
-              <input type="submit" name="edit_do[save_name]" value="' . $lang->get('acpug_btn_save_name') . '" />
-              <input type="submit" name="edit_do[del_group]" '.$sg_disabled.' />
-            </th>
-          </tr>
-          </table>
-          </div>
-          <input type="hidden" name="group_edit_id" value="'.htmlspecialchars($_POST['group_edit_id']).'" />';
-    echo '</form>';
-    echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-    echo '<div class="tblholder">
-          <table border="0" style="width:100%;" cellspacing="1" cellpadding="4">
-          <tr><th colspan="3">' . $lang->get('acpug_heading_edit_members') . '</th></tr>';
-    $q = $db->sql_query('SELECT m.member_id,m.is_mod,u.username FROM '.table_prefix.'group_members AS m
-                           LEFT JOIN '.table_prefix.'users AS u
-                             ON u.user_id=m.user_id
-                             WHERE m.group_id='.intval($_POST['group_edit_id']).'
-                           ORDER BY m.is_mod DESC, u.username ASC;');
-    if(!$q)
-    {
-      echo $db->get_error();
-      return;
-    }
-    if($db->numrows() < 1)
-    {
-      echo '<tr><td colspan="3" class="row1">' . $lang->get('acpug_msg_no_members') . '</td></tr>';
-    }
-    else
-    {
-      $cls = 'row2';
-      while($row = $db->fetchrow())
-      {
-        $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-        $mod = ( $row['is_mod'] == 1 ) ? $lang->get('acpug_lbl_member_mod') : '';
-        echo '<tr>
-                <td class="'.$cls.'" style="width: 100%;">
-                  ' . $row['username'] . '
-                </td>
-                <td class="'.$cls.'">
-                  '.$mod.'
-                </td>
-                <td class="'.$cls.'">
-                  <input type="submit" name="edit_do[del_'.$row['member_id'].']" value="' . $lang->get('acpug_btn_remove_member') . '" />
-                </td>
-              </tr>';
-      }
-    }
-    $db->free_result();
-    echo '</table>
-          </div>
-          <input type="hidden" name="group_edit_id" value="'.htmlspecialchars($_POST['group_edit_id']).'" />';
-    echo '</form>';
-    echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-    echo '<div class="tblholder">
-          <table border="0" style="width:100%;" cellspacing="1" cellpadding="4">
-            <tr>
-              <th>' . $lang->get('acpug_heading_add_member') . '</th>
-            </tr>
-            <tr>
-              <td class="row1">
-                ' . $lang->get('acpug_field_username') . ' ' . $template->username_field('edit_add_username') . '
-              </td>
-            </tr>
-            <tr>
-              <td class="row2">
-                <label><input type="checkbox" name="add_mod" /> ' . $lang->get('acpug_field_make_mod') . '</label>
-                ' . $lang->get('acpug_field_make_mod_hint') . '
-              </td>
-            </tr>
-            <tr>
-              <th class="subhead">
-                <input type="submit" name="edit_do[add_member]" value="' . $lang->get('acpug_btn_add_user') . '" />
-              </th>
-            </tr>
-          </table>
-          </div>
-          <input type="hidden" name="group_edit_id" value="'.htmlspecialchars($_POST['group_edit_id']).'" />';
-    echo '</form>';
-    return;
-  }
-  echo '<h3>' . $lang->get('acpug_heading_main') . '</h3>';
-  echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-  $q = $db->sql_query('SELECT group_id,group_name FROM '.table_prefix.'groups ORDER BY group_name ASC;');
-  if(!$q)
-  {
-    echo $db->get_error();
-  }
-  else
-  {
-    echo '<div class="tblholder">
-          <table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
-          <tr>
-          <th>' . $lang->get('acpug_heading_edit_existing') . '</th>
-          </tr>';
-    echo '<tr><td class="row2"><select name="group_edit_id">';
-    while ( $row = $db->fetchrow() )
-    {
-      if ( $row['group_name'] != 'Everyone' )
-      {
-        echo '<option value="' . $row['group_id'] . '">' . htmlspecialchars( $row['group_name'] ) . '</option>';
-      }
-    }
-    $db->free_result();
-    echo '</select></td></tr>';
-    echo '<tr><td class="row1" style="text-align: center;"><input type="submit" name="do_edit" value="' . $lang->get('acpug_btn_edit_stage1') . '" /></td></tr>
-          </table>
-          </div>
-          </form><br />';
-  }
-  echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-  echo '<div class="tblholder">
-        <table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
-        <tr>
-        <th colspan="2">' . $lang->get('acpug_heading_create_new') . '</th>
-        </tr>';
-  echo '<tr><td class="row2">' . $lang->get('acpug_field_group_name') . '</td><td class="row2"><input type="text" name="create_group_name" /></td></tr>';
-  echo '<tr><td colspan="2" class="row1" style="text-align: center;"><input type="submit" name="do_create_stage1" value="' . $lang->get('acpug_btn_create_stage1') . ' &raquo;" /></td></tr>
-        </table>
-        </div>';
-  echo '</form>';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	if(isset($_POST['do_create_stage1']))
+	{
+		if(!preg_match('/^([A-z0-9 -]+)$/', $_POST['create_group_name']))
+		{
+			echo '<p>' . $lang->get('acpug_err_group_name_invalid') . '</p>';
+			return;
+		}
+		echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+		echo '<div class="tblholder">
+					<table border="0" style="width:100%;" cellspacing="1" cellpadding="4">
+					<tr><th colspan="2">' . $lang->get('acpug_heading_creating_group') . ' '.htmlspecialchars($_POST['create_group_name']).'</th></tr>
+					<tr>
+						<td class="row1">' . $lang->get('acpug_field_group_mod') . '</td><td class="row1">' . $template->username_field('group_mod') . '</td>
+					</tr>
+					<tr><td class="row2">' . $lang->get('acpug_field_group_type') . '</td><td class="row2">
+						<label><input type="radio" name="group_status" value="'.GROUP_CLOSED.'" checked="checked" /> ' . $lang->get('groupcp_type_hidden') . '</label><br />
+						<label><input type="radio" name="group_status" value="'.GROUP_REQUEST.'" /> ' . $lang->get('groupcp_type_closed') . '</label><br />
+						<label><input type="radio" name="group_status" value="'.GROUP_OPEN.'" /> ' . $lang->get('groupcp_type_request') . '</label><br />
+						<label><input type="radio" name="group_status" value="'.GROUP_HIDDEN.'" /> ' . $lang->get('groupcp_type_open') . '</label>
+					</td></tr>
+					<tr>
+						<th class="subhead" colspan="2">
+							<input type="hidden" name="create_group_name" value="'.htmlspecialchars($_POST['create_group_name']).'" />
+							<input type="submit" name="do_create_stage2" value="' . $lang->get('acpug_btn_create_stage2') . '" />
+						</th>
+					</tr>
+					</table>
+					</div>';
+		echo '</form>';
+		return;
+	}
+	elseif(isset($_POST['do_create_stage2']))
+	{
+		if(!preg_match('/^([A-z0-9 -]+)$/', $_POST['create_group_name']))
+		{
+			echo '<p>' . $lang->get('acpug_err_group_name_invalid') . '</p>';
+			return;
+		}
+		if(!in_array(intval($_POST['group_status']), Array(GROUP_CLOSED, GROUP_OPEN, GROUP_HIDDEN, GROUP_REQUEST)))
+		{
+			echo '<p>Hacking attempt</p>';
+			return;
+		}
+		$e = $db->sql_query('SELECT group_id FROM '.table_prefix.'groups WHERE group_name=\''.$db->escape($_POST['create_group_name']).'\';');
+		if(!$e)
+		{
+			echo $db->get_error();
+			return;
+		}
+		if($db->numrows() > 0)
+		{
+			echo '<p>' . $lang->get('acpug_err_already_exist') . '</p>';
+			return;
+		}
+		$db->free_result();
+		$q = $db->sql_query('INSERT INTO '.table_prefix.'groups(group_name,group_type) VALUES( \''.$db->escape($_POST['create_group_name']).'\', ' . intval($_POST['group_status']) . ' )');
+		if(!$q)
+		{
+			echo $db->get_error();
+			return;
+		}
+		$e = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($_POST['group_mod']).'\';');
+		if(!$e)
+		{
+			echo $db->get_error();
+			return;
+		}
+		if($db->numrows() < 1)
+		{
+			echo '<p>' . $lang->get('acpug_err_bad_username') . '</p>';
+			return;
+		}
+		$row = $db->fetchrow();
+		$id = $row['user_id'];
+		$db->free_result();
+		$e = $db->sql_query('SELECT group_id FROM '.table_prefix.'groups WHERE group_name=\''.$db->escape($_POST['create_group_name']).'\';');
+		if(!$e)
+		{
+			echo $db->get_error();
+			return;
+		}
+		if($db->numrows() < 1)
+		{
+			echo '<p>' . $lang->get('acpug_err_bad_insert_id') . '</p>';
+			return;
+		}
+		$row = $db->fetchrow();
+		$gid = $row['group_id'];
+		$db->free_result();
+		$e = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id,is_mod) VALUES('.$gid.', '.$id.', 1);');
+		if(!$e)
+		{
+			echo $db->get_error();
+			return;
+		}
+		$g_name = htmlspecialchars($_POST['create_group_name']);
+		echo "<div class='info-box'>
+						<b>" . $lang->get('acpug_heading_info') . "</b><br />
+						" . $lang->get('acpug_msg_create_success', array('g_name' => $g_name)) . "
+					</div>";
+	}
+	if(isset($_POST['do_edit']) || isset($_POST['edit_do']))
+	{
+		// Fetch the group name
+		$q = $db->sql_query('SELECT group_name,system_group,group_rank FROM '.table_prefix.'groups WHERE group_id='.intval($_POST['group_edit_id']).';');
+		if(!$q)
+		{
+			echo $db->get_error();
+			return;
+		}
+		if($db->numrows() < 1)
+		{
+			echo '<p>Error: couldn\'t look up group name</p>';
+		}
+		$row = $db->fetchrow();
+		$name = htmlspecialchars($row['group_name']);
+		$db->free_result();
+		if(isset($_POST['edit_do']))
+		{
+			if(isset($_POST['edit_do']['del_group']))
+			{
+				if ( $row['system_group'] == 1 )
+				{
+					echo '<div class="error-box">' . $lang->get('acpug_err_nodelete_system_group', array('g_name' => $name)) . '</div>';
+				}
+				else
+				{
+					$q = $db->sql_query('DELETE FROM '.table_prefix.'group_members WHERE group_id='.intval($_POST['group_edit_id']).';');
+					if(!$q)
+					{
+						echo $db->get_error();
+						return;
+					}
+					$q = $db->sql_query('DELETE FROM '.table_prefix.'groups WHERE group_id='.intval($_POST['group_edit_id']).';');
+					if(!$q)
+					{
+						echo $db->get_error();
+						return;
+					}
+					echo '<div class="info-box">' . $lang->get('acpug_msg_delete_success', array('g_name' => $name, 'a_flags' => 'href="javascript:ajaxPage(\'' . $paths->nslist['Admin'] . 'GroupManager\');"')) . '</div>';
+					return;
+				}
+			}
+			if(isset($_POST['edit_do']['save_name']))
+			{
+				if(!preg_match('/^([A-z0-9 -]+)$/', $_POST['group_name']))
+				{
+					echo '<p>' . $lang->get('acpug_err_group_name_invalid') . '</p>';
+					return;
+				}
+				// determine rank
+				$group_rank =& $_POST['group_rank'];
+				if ( $_POST['group_rank'] !== 'NULL' )
+				{
+					$group_rank = intval($group_rank);
+					if ( empty($group_rank) )
+					{
+						echo '<p>Hacked rank ID</p>';
+						return;
+					}
+				}
+				$row['group_rank'] = $group_rank;
+				$q = $db->sql_query('UPDATE '.table_prefix.'groups SET group_name=\''.$db->escape($_POST['group_name']).'\',group_rank = ' . $group_rank . '
+						WHERE group_id='.intval($_POST['group_edit_id']).';');
+				if(!$q)
+				{
+					echo $db->get_error();
+					return;
+				}
+				else
+				{
+					echo '<div class="info-box" style="margin: 0 0 10px 0;"">
+									' . $lang->get('acpug_msg_name_update_success') . '
+								</div>';
+				}
+				$name = htmlspecialchars($_POST['group_name']);
+				
+			}
+			$q = $db->sql_query('SELECT member_id FROM '.table_prefix.'group_members
+ 														WHERE group_id='.intval($_POST['group_edit_id']).';');
+			if(!$q)
+			{
+				echo $db->get_error();
+				return;
+			}
+			if($db->numrows() > 0)
+			{
+				while($delrow = $db->fetchrow($q))
+				{
+					if(isset($_POST['edit_do']['del_' . $delrow['member_id']]))
+					{
+						$e = $db->sql_query('DELETE FROM '.table_prefix.'group_members WHERE member_id='.$delrow['member_id']);
+						if(!$e)
+						{
+							echo $db->get_error();
+							return;
+						}
+					}
+				}
+			}
+			$db->free_result();
+			if(isset($_POST['edit_do']['add_member']))
+			{
+				$q = $db->sql_query('SELECT user_id FROM '.table_prefix.'users WHERE username=\''.$db->escape($_POST['edit_add_username']).'\';');
+				if(!$q)
+				{
+					echo $db->get_error();
+					return;
+				}
+				if($db->numrows() > 0)
+				{
+					$row = $db->fetchrow();
+					$user_id = $row['user_id'];
+					$is_mod = ( isset( $_POST['add_mod'] ) ) ? '1' : '0';
+					$q = $db->sql_query('INSERT INTO '.table_prefix.'group_members(group_id,user_id,is_mod) VALUES('.intval($_POST['group_edit_id']).','.$user_id.','.$is_mod.');');
+					if(!$q)
+					{
+						echo $db->get_error();
+						return;
+					}
+					else
+					{
+						
+						echo '<div class="info-box" style="margin: 0 0 10px 0;"">
+										' . $lang->get('acpug_msg_user_added', array('username' => htmlspecialchars($_POST['edit_add_username']))) . '
+									</div>';
+					}
+				}
+				else
+					echo '<div class="warning-box">' . $lang->get('acpug_err_username_not_exist', array('username' => htmlspecialchars($_POST['edit_add_username']))) . '</div>';
+			}
+			generate_cache_userranks();
+		}
+		$sg_disabled = ( $row['system_group'] == 1 ) ?
+ 						' value="' . $lang->get('acpug_btn_cant_delete') . '" disabled="disabled" style="color: #FF9773" ' :
+ 						' value="' . $lang->get('acpug_btn_delete_group') . '" style="color: #FF3713" ';
+		
+		// build rank list
+		$q = $db->sql_query('SELECT rank_id, rank_title FROM ' . table_prefix . 'ranks');
+		if ( !$q )
+			$db->_die();
+		$rank_list = '<option value="NULL"' . ( $row['group_rank'] === NULL ? ' selected="selected"' : '' ) . '>--</option>' . "\n";
+		while ( $rank_row = $db->fetchrow() )
+		{
+			$rank_list .= '<option value="' . $rank_row['rank_id'] . '"' . ( $rank_row['rank_id'] == $row['group_rank'] ? ' selected="selected"' : '' ) . '>' . htmlspecialchars($lang->get($rank_row['rank_title'])) . '</option>' . "\n";
+		}
+ 						
+		echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+		echo '<div class="tblholder">
+					<table border="0" style="width:100%;" cellspacing="1" cellpadding="4">
+					<tr><th>' . $lang->get('acpug_heading_edit_name') . '</th></tr>
+					<tr>
+						<td class="row1">
+							' . $lang->get('acpug_field_group_name') . ' <input type="text" name="group_name" value="'.$name.'" />
+						</td>
+					</tr>
+					<tr>
+						<td class="row1">
+							' . $lang->get('acpug_field_group_rank') . ' <select name="group_rank" />' . $rank_list . '</select>
+						</td>
+					</tr>
+					<tr>
+						<th class="subhead">
+							<input type="submit" name="edit_do[save_name]" value="' . $lang->get('acpug_btn_save_name') . '" />
+							<input type="submit" name="edit_do[del_group]" '.$sg_disabled.' />
+						</th>
+					</tr>
+					</table>
+					</div>
+					<input type="hidden" name="group_edit_id" value="'.htmlspecialchars($_POST['group_edit_id']).'" />';
+		echo '</form>';
+		echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+		echo '<div class="tblholder">
+					<table border="0" style="width:100%;" cellspacing="1" cellpadding="4">
+					<tr><th colspan="3">' . $lang->get('acpug_heading_edit_members') . '</th></tr>';
+		$q = $db->sql_query('SELECT m.member_id,m.is_mod,u.username FROM '.table_prefix.'group_members AS m
+ 													LEFT JOIN '.table_prefix.'users AS u
+ 														ON u.user_id=m.user_id
+ 														WHERE m.group_id='.intval($_POST['group_edit_id']).'
+ 													ORDER BY m.is_mod DESC, u.username ASC;');
+		if(!$q)
+		{
+			echo $db->get_error();
+			return;
+		}
+		if($db->numrows() < 1)
+		{
+			echo '<tr><td colspan="3" class="row1">' . $lang->get('acpug_msg_no_members') . '</td></tr>';
+		}
+		else
+		{
+			$cls = 'row2';
+			while($row = $db->fetchrow())
+			{
+				$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+				$mod = ( $row['is_mod'] == 1 ) ? $lang->get('acpug_lbl_member_mod') : '';
+				echo '<tr>
+								<td class="'.$cls.'" style="width: 100%;">
+									' . $row['username'] . '
+								</td>
+								<td class="'.$cls.'">
+									'.$mod.'
+								</td>
+								<td class="'.$cls.'">
+									<input type="submit" name="edit_do[del_'.$row['member_id'].']" value="' . $lang->get('acpug_btn_remove_member') . '" />
+								</td>
+							</tr>';
+			}
+		}
+		$db->free_result();
+		echo '</table>
+					</div>
+					<input type="hidden" name="group_edit_id" value="'.htmlspecialchars($_POST['group_edit_id']).'" />';
+		echo '</form>';
+		echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+		echo '<div class="tblholder">
+					<table border="0" style="width:100%;" cellspacing="1" cellpadding="4">
+						<tr>
+							<th>' . $lang->get('acpug_heading_add_member') . '</th>
+						</tr>
+						<tr>
+							<td class="row1">
+								' . $lang->get('acpug_field_username') . ' ' . $template->username_field('edit_add_username') . '
+							</td>
+						</tr>
+						<tr>
+							<td class="row2">
+								<label><input type="checkbox" name="add_mod" /> ' . $lang->get('acpug_field_make_mod') . '</label>
+								' . $lang->get('acpug_field_make_mod_hint') . '
+							</td>
+						</tr>
+						<tr>
+							<th class="subhead">
+								<input type="submit" name="edit_do[add_member]" value="' . $lang->get('acpug_btn_add_user') . '" />
+							</th>
+						</tr>
+					</table>
+					</div>
+					<input type="hidden" name="group_edit_id" value="'.htmlspecialchars($_POST['group_edit_id']).'" />';
+		echo '</form>';
+		return;
+	}
+	echo '<h3>' . $lang->get('acpug_heading_main') . '</h3>';
+	echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+	$q = $db->sql_query('SELECT group_id,group_name FROM '.table_prefix.'groups ORDER BY group_name ASC;');
+	if(!$q)
+	{
+		echo $db->get_error();
+	}
+	else
+	{
+		echo '<div class="tblholder">
+					<table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
+					<tr>
+					<th>' . $lang->get('acpug_heading_edit_existing') . '</th>
+					</tr>';
+		echo '<tr><td class="row2"><select name="group_edit_id">';
+		while ( $row = $db->fetchrow() )
+		{
+			if ( $row['group_name'] != 'Everyone' )
+			{
+				echo '<option value="' . $row['group_id'] . '">' . htmlspecialchars( $row['group_name'] ) . '</option>';
+			}
+		}
+		$db->free_result();
+		echo '</select></td></tr>';
+		echo '<tr><td class="row1" style="text-align: center;"><input type="submit" name="do_edit" value="' . $lang->get('acpug_btn_edit_stage1') . '" /></td></tr>
+					</table>
+					</div>
+					</form><br />';
+	}
+	echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+	echo '<div class="tblholder">
+				<table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
+				<tr>
+				<th colspan="2">' . $lang->get('acpug_heading_create_new') . '</th>
+				</tr>';
+	echo '<tr><td class="row2">' . $lang->get('acpug_field_group_name') . '</td><td class="row2"><input type="text" name="create_group_name" /></td></tr>';
+	echo '<tr><td colspan="2" class="row1" style="text-align: center;"><input type="submit" name="do_create_stage1" value="' . $lang->get('acpug_btn_create_stage1') . ' &raquo;" /></td></tr>
+				</table>
+				</div>';
+	echo '</form>';
 }
 
 ?>
--- a/plugins/admin/Home.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/Home.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,435 +13,435 @@
 
 function page_Admin_Home()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  if ( $paths->getParam(0) == 'updates.xml' )
-  {
-    return acphome_process_updates();
-  }
-  
-  // Welcome
-  echo '<h2>' . $lang->get('acphome_heading_main') . '</h2>';
-  echo '<p>' . $lang->get('acphome_welcome_line1') . '</p>';
-  
-  // Stats
-  acphome_show_stats();
-  
-  //
-  // Alerts
-  //
-  
-  echo '<h3>' . $lang->get('acphome_heading_alerts') . '</h3>';
-  
-  // Demo mode
-  if ( defined('ENANO_DEMO_MODE') )
-  {
-    echo '<div class="acphome-box info">';
-      echo '<h3>' . $lang->get('acphome_msg_demo_title') . '</h3>
-            <p>' . $lang->get('acphome_msg_demo_body', array('reset_url' => makeUrlNS('Special', 'DemoReset', false, true))) . '</p>';
-    echo '</div>';
-  }
-  
-  // Check for the installer scripts
-  if( file_exists(ENANO_ROOT.'/install/install.php') && !defined('ENANO_DEMO_MODE') )
-  {
-    echo '<div class="acphome-box warning">
-            <h3>' . $lang->get('acphome_msg_install_files_title') . '</h3>
-            <p>' . $lang->get('acphome_msg_install_files_body') . '</p>
-          </div>';
-  }
-  
-  // Inactive users
-  $q = $db->sql_query('SELECT time_id FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\';');
-  if ( $q )
-  {
-    if ( $db->numrows() > 0 )
-    {
-      $n = $db->numrows();
-      $um_flags = 'href="#" onclick="ajaxPage(\''.$paths->nslist['Admin'].'UserManager\'); return false;"';
-      if ( $n == 1 )
-        $s = $lang->get('acphome_msg_inactive_users_one', array('um_flags' => $um_flags));
-      else
-        $s = $lang->get('acphome_msg_inactive_users_plural', array('um_flags' => $um_flags, 'num_users' => $n));
-      echo '<div class="acphome-box notice">
-              <h3>' . $lang->get('acphome_heading_inactive_users') . '</h3>
-              ' . $s . '
-            </div>';
-    }
-  }
-  $db->free_result();
-  
-  acp_usermanager_lockouts(true);
-  
-  // Update checker
-  echo '<div class="acphome-box info">';
-    echo '<h3>' . $lang->get('acphome_heading_updates') . '</h3>';
-    echo '<p>' . $lang->get('acphome_msg_updates_info', array('updates_url' => 'http://ktulu.enanocms.org/meta/updates.xml')) . '</p>';
-    echo '<div id="update_check_container"><input type="button" onclick="ajaxUpdateCheck(this.parentNode.id);" value="' . $lang->get('acphome_btn_check_updates') . '" /></div>';
-  echo '</div>';
-  
-  // Docs
-  echo '<div class="acphome-box info halfwidth">';
-  echo '<h3>' . $lang->get('acphome_heading_docs') . '</h3>';
-  echo '<p>' . $lang->get('acphome_msg_docs_info') . '</p>';
-  echo '</div>';
-  
-  // Support
-  echo '<div class="acphome-box info halfwidth">';
-  echo '<h3>' . $lang->get('acphome_heading_support') . '</h3>';
-  echo '<p>' . $lang->get('acphome_msg_support_info') . '</p>';
-  echo '</div>';
-  
-  echo '<span class="menuclear"></span>';
-  
-  //
-  // Stats
-  //
-  
-  if(getConfig('log_hits') == '1')
-  {
-    require_once(ENANO_ROOT . '/includes/stats.php');
-    $stats = stats_top_pages(10);
-    //die('<pre>'.print_r($stats,true).'</pre>');
-    $c = 0;
-    $cls = 'row2';
-    echo '<h3>' . $lang->get('acphome_heading_top_pages') . '</h3>
-          <div class="tblholder">
-            <table style="width: 100%;" border="0" cellspacing="1" cellpadding="4">
-              <tr>
-                <th>' . $lang->get('acphome_th_toppages_page') . '</th>
-                <th>' . $lang->get('acphome_th_toppages_hits') . '</th>
-              </tr>';
-    foreach($stats as $data)
-    {
-      echo   '<tr>';
-      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-      echo     '<td class="'.$cls.'">
-                  <a href="'.makeUrl($data['page_urlname']).'">'.$data['page_title'].'</a></td><td style="text-align: center;" class="'.$cls.'">'.$data['num_hits']
-             . '</td>';
-      echo   '</tr>';
-    }
-    echo '  </table>
-          </div>';
-  }
-  
-  // Any hooks?
-  $code = $plugins->setHook('acp_home');
-  foreach ( $code as $cmd )
-  {
-    eval($cmd);
-  }
-  
-  //
-  // Security log
-  //
-  
-  echo '<h3>' . $lang->get('acphome_heading_seclog') . '</h3>';
-  echo '<p>' . $lang->get('acphome_msg_seclog_info') . '</p>';
-  $seclog = get_security_log(5);
-  echo $seclog;
-  
-  echo '<p><a href="#" onclick="ajaxPage(\''.$paths->nslist['Admin'].'SecurityLog\'); return false;">' . $lang->get('acphome_btn_seclog_full') . '</a></p>';
-  
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	if ( $paths->getParam(0) == 'updates.xml' )
+	{
+		return acphome_process_updates();
+	}
+	
+	// Welcome
+	echo '<h2>' . $lang->get('acphome_heading_main') . '</h2>';
+	echo '<p>' . $lang->get('acphome_welcome_line1') . '</p>';
+	
+	// Stats
+	acphome_show_stats();
+	
+	//
+	// Alerts
+	//
+	
+	echo '<h3>' . $lang->get('acphome_heading_alerts') . '</h3>';
+	
+	// Demo mode
+	if ( defined('ENANO_DEMO_MODE') )
+	{
+		echo '<div class="acphome-box info">';
+			echo '<h3>' . $lang->get('acphome_msg_demo_title') . '</h3>
+						<p>' . $lang->get('acphome_msg_demo_body', array('reset_url' => makeUrlNS('Special', 'DemoReset', false, true))) . '</p>';
+		echo '</div>';
+	}
+	
+	// Check for the installer scripts
+	if( file_exists(ENANO_ROOT.'/install/install.php') && !defined('ENANO_DEMO_MODE') )
+	{
+		echo '<div class="acphome-box warning">
+						<h3>' . $lang->get('acphome_msg_install_files_title') . '</h3>
+						<p>' . $lang->get('acphome_msg_install_files_body') . '</p>
+					</div>';
+	}
+	
+	// Inactive users
+	$q = $db->sql_query('SELECT time_id FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\';');
+	if ( $q )
+	{
+		if ( $db->numrows() > 0 )
+		{
+			$n = $db->numrows();
+			$um_flags = 'href="#" onclick="ajaxPage(\''.$paths->nslist['Admin'].'UserManager\'); return false;"';
+			if ( $n == 1 )
+				$s = $lang->get('acphome_msg_inactive_users_one', array('um_flags' => $um_flags));
+			else
+				$s = $lang->get('acphome_msg_inactive_users_plural', array('um_flags' => $um_flags, 'num_users' => $n));
+			echo '<div class="acphome-box notice">
+							<h3>' . $lang->get('acphome_heading_inactive_users') . '</h3>
+							' . $s . '
+						</div>';
+		}
+	}
+	$db->free_result();
+	
+	acp_usermanager_lockouts(true);
+	
+	// Update checker
+	echo '<div class="acphome-box info">';
+		echo '<h3>' . $lang->get('acphome_heading_updates') . '</h3>';
+		echo '<p>' . $lang->get('acphome_msg_updates_info', array('updates_url' => 'http://ktulu.enanocms.org/meta/updates.xml')) . '</p>';
+		echo '<div id="update_check_container"><input type="button" onclick="ajaxUpdateCheck(this.parentNode.id);" value="' . $lang->get('acphome_btn_check_updates') . '" /></div>';
+	echo '</div>';
+	
+	// Docs
+	echo '<div class="acphome-box info halfwidth">';
+	echo '<h3>' . $lang->get('acphome_heading_docs') . '</h3>';
+	echo '<p>' . $lang->get('acphome_msg_docs_info') . '</p>';
+	echo '</div>';
+	
+	// Support
+	echo '<div class="acphome-box info halfwidth">';
+	echo '<h3>' . $lang->get('acphome_heading_support') . '</h3>';
+	echo '<p>' . $lang->get('acphome_msg_support_info') . '</p>';
+	echo '</div>';
+	
+	echo '<span class="menuclear"></span>';
+	
+	//
+	// Stats
+	//
+	
+	if(getConfig('log_hits') == '1')
+	{
+		require_once(ENANO_ROOT . '/includes/stats.php');
+		$stats = stats_top_pages(10);
+		//die('<pre>'.print_r($stats,true).'</pre>');
+		$c = 0;
+		$cls = 'row2';
+		echo '<h3>' . $lang->get('acphome_heading_top_pages') . '</h3>
+					<div class="tblholder">
+						<table style="width: 100%;" border="0" cellspacing="1" cellpadding="4">
+							<tr>
+								<th>' . $lang->get('acphome_th_toppages_page') . '</th>
+								<th>' . $lang->get('acphome_th_toppages_hits') . '</th>
+							</tr>';
+		foreach($stats as $data)
+		{
+			echo   '<tr>';
+			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+			echo     '<td class="'.$cls.'">
+									<a href="'.makeUrl($data['page_urlname']).'">'.$data['page_title'].'</a></td><td style="text-align: center;" class="'.$cls.'">'.$data['num_hits']
+ 						. '</td>';
+			echo   '</tr>';
+		}
+		echo '  </table>
+					</div>';
+	}
+	
+	// Any hooks?
+	$code = $plugins->setHook('acp_home');
+	foreach ( $code as $cmd )
+	{
+		eval($cmd);
+	}
+	
+	//
+	// Security log
+	//
+	
+	echo '<h3>' . $lang->get('acphome_heading_seclog') . '</h3>';
+	echo '<p>' . $lang->get('acphome_msg_seclog_info') . '</p>';
+	$seclog = get_security_log(5);
+	echo $seclog;
+	
+	echo '<p><a href="#" onclick="ajaxPage(\''.$paths->nslist['Admin'].'SecurityLog\'); return false;">' . $lang->get('acphome_btn_seclog_full') . '</a></p>';
+	
 }
 
 function acphome_process_updates()
 {
-  require_once(ENANO_ROOT . '/includes/http.php');
-  
-  try
-  {
-    $req = new Request_HTTP('ktulu.enanocms.org', '/meta/updates.xml');
-    $response = $req->get_response_body();
-    header('Content-type: application/xml');
-  }
-  catch ( Exception $e )
-  {
-    header('Content-type: application/xml');
-    echo '<enano><error><![CDATA[
+	require_once(ENANO_ROOT . '/includes/http.php');
+	
+	try
+	{
+		$req = new Request_HTTP('ktulu.enanocms.org', '/meta/updates.xml');
+		$response = $req->get_response_body();
+		header('Content-type: application/xml');
+	}
+	catch ( Exception $e )
+	{
+		header('Content-type: application/xml');
+		echo '<enano><error><![CDATA[
 Cannot connect to server: ' . $e->getMessage() . '
 ]]></error></enano>';
-    return true;
-  }
-  if ( $req->response_code != HTTP_OK )
-  {
-    // Error in response
-    echo '<enano><error><![CDATA[
+		return true;
+	}
+	if ( $req->response_code != HTTP_OK )
+	{
+		// Error in response
+		echo '<enano><error><![CDATA[
 Did not properly receive response from server. Response code: ' . $req->response_code . ' ' . $req->response_string . '
 ]]></error></enano>';
-  }
-  else
-  {
-    // Retrieve first update
-    $first_update = preg_match('/<release tag="([^"]+)" version="([^"]+)" (codename="([^"]+)" )?relnotes="([^"]+)" ?\/>/', $response, $match);
-    if ( !$first_update )
-    {
-      echo '<enano><error><![CDATA[
+	}
+	else
+	{
+		// Retrieve first update
+		$first_update = preg_match('/<release tag="([^"]+)" version="([^"]+)" (codename="([^"]+)" )?relnotes="([^"]+)" ?\/>/', $response, $match);
+		if ( !$first_update )
+		{
+			echo '<enano><error><![CDATA[
 Received invalid XML response.
 ]]></error></enano>';
-    }
-    else
-    {
-      if ( version_compare(enano_version(true), $match[2], '<') )
-      {
-        $response = str_replace_once('</latest>', "  <haveupdates />\n  </latest>", $response);
-      }
-      echo $response;
-    }
-  }
-  return true;
+		}
+		else
+		{
+			if ( version_compare(enano_version(true), $match[2], '<') )
+			{
+				$response = str_replace_once('</latest>', "  <haveupdates />\n  </latest>", $response);
+			}
+			echo $response;
+		}
+	}
+	return true;
 }
 
 function acphome_show_stats()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  // Page count
-  $q = $db->sql_query('SELECT COUNT(*) FROM ' . table_prefix . "pages");
-  if ( !$q )
-    $db->_die();
-  list($page_count) = $db->fetchrow_num();
-  $db->free_result();
-  
-  // Edits per day
-  $q = $db->sql_query('SELECT ( COUNT(*) - 1 ) AS edit_count, MIN(time_id) AS install_date FROM ' . table_prefix . 'logs WHERE ( log_type = \'page\' AND action = \'edit\' ) OR ( log_type = \'security\' AND action = \'install_enano\' );');
-  if ( !$q )
-    $db->_die();
-  $edit_info = $db->fetchrow();
-  $install_date =& $edit_info['install_date'];
-  $db->free_result();
-  
-  $days_installed = round( (time() / 86400) - ($install_date / 86400) );
-  if ( $days_installed < 1 )
-    $days_installed = 1;
-  
-  // Comments
-  $q = $db->sql_query('SELECT COUNT(*) FROM ' . table_prefix . "comments");
-  if ( !$q )
-    $db->_die();
-  list($comment_count) = $db->fetchrow_num();
-  $db->free_result();
-  
-  // Users
-  $q = $db->sql_query('SELECT ( COUNT(*) - 1 ) FROM ' . table_prefix . "users");
-  if ( !$q )
-    $db->_die();
-  list($user_count) = $db->fetchrow_num();
-  $db->free_result();
-  
-  // Cache size
-  $cache_size = 0;
-  if ( $dr = @opendir(ENANO_ROOT . '/cache/') )
-  {
-    while ( $dh = @readdir($dr) )
-    {
-      $file = ENANO_ROOT . "/cache/$dh";
-      if ( @is_file($file) )
-        $cache_size += filesize($file);
-    }
-    closedir($dr);
-  }
-  $cache_size = humanize_filesize($cache_size);
-  
-  // Files directory size
-  $files_size = 0;
-  if ( $dr = @opendir(ENANO_ROOT . '/files/') )
-  {
-    while ( $dh = @readdir($dr) )
-    {
-      $file = ENANO_ROOT . "/files/$dh";
-      if ( @is_file($file) )
-        $files_size += filesize($file);
-    }
-    closedir($dr);
-  }
-  $files_size = humanize_filesize($files_size);
-  
-  // Avatar directory size
-  $avatar_size = 0;
-  if ( $dr = @opendir(ENANO_ROOT . '/files/avatars/') )
-  {
-    while ( $dh = @readdir($dr) )
-    {
-      $file = ENANO_ROOT . "/files/avatars/$dh";
-      if ( @is_file($file) )
-        $avatar_size += filesize($file);
-    }
-    closedir($dr);
-  }
-  $avatar_size = humanize_filesize($avatar_size);
-  
-  // Database size
-  $db_size = $lang->get('acphome_stat_dbsize_unsupported');
-  if ( ENANO_DBLAYER == 'MYSQL' )
-  {
-    $q = $db->sql_query('SHOW TABLE STATUS;');
-    if ( $q )
-    {
-      $db_size = 0;
-      while ( $row = $db->fetchrow() )
-      {
-        if ( preg_match('/^' . table_prefix . '/', $row['Name']) )
-        {
-          $db_size += $row['Data_length'] + $row['Index_length'];
-        }
-      }
-      $db_size = humanize_filesize($db_size);
-    }
-  }
-  else if ( ENANO_DBLAYER == 'PGSQL' )
-  {
-    require(ENANO_ROOT . '/config.php');
-    global $dbname, $dbuser, $dbpasswd;
-    $dbuser = false;
-    $dbpasswd = false;
-    
-    $q = $db->sql_query('SELECT pg_database_size(\'' . $db->escape($dbname) . '\');');
-    if ( $q )
-    {
-      list($db_size) = $db->fetchrow_num();
-      $db_size = humanize_filesize($db_size);
-      $db->free_result();
-    }
-  }
-  
-  // Install date
-  $site_age = floor((time() - $install_date) / 86400);
-  $install_date_human = MemberlistFormatter::format_date($install_date);
-  if ( $site_age > 7 )
-  {
-    $install_date_human .= ' ' . $lang->get('acphome_stat_installdate_ago', array(
-        'days' => number_format($site_age)
-      ));
-  }
-  
-  // Last upgrade
-  $q = $db->sql_query('SELECT time_id FROM ' . table_prefix . "logs WHERE log_type = 'security' AND action = 'upgrade_enano' ORDER BY time_id DESC LIMIT 1;");
-  if ( !$q )
-    $db->_die();
-  
-  if ( $db->numrows() < 1 )
-  {
-    $last_upgrade = $lang->get('acphome_stat_lastupdate_never');
-  }
-  else
-  {
-    list($last_upgrade) = $db->fetchrow_num();
-    $ver_age = floor((time() - $last_upgrade) / 86400);
-    $last_upgrade = MemberlistFormatter::format_date($last_upgrade);
-    if ( $ver_age > 7 )
-    {
-      $last_upgrade .= ' ' . $lang->get('acphome_stat_installdate_ago', array(
-          'days' => number_format($ver_age)
-        ));
-    }
-  }
-  $db->free_result();
-  
-  ?>
-  <div class="tblholder">
-  <table border="0" cellspacing="1" cellpadding="4">
-    <tr>
-      <th colspan="4">
-        <?php echo $lang->get('acphome_stat_header'); ?>
-      </th>
-    </tr>
-    
-    <tr>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_numpages'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo strval($page_count); ?>
-      </td>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_edits'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_edits_data', array('edit_count' => $edit_info['edit_count'], 'per_day' => number_format($edit_info['edit_count'] / $days_installed, 2))); ?>
-      </td>
-    </tr>
-    
-    <tr>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_comments'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_comments_data', array('comment_count' => $comment_count, 'per_day' => number_format($comment_count / $days_installed, 2))); ?>
-      </td>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_users'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo strval($user_count); ?>
-      </td>
-    </tr>
-    
-    <tr>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_filesize'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo $files_size; ?>
-      </td>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_cachesize'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo $cache_size; ?>
-      </td>
-    </tr>
-    
-    <tr>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_avatarsize'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo $avatar_size; ?>
-      </td>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_dbsize'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo $db_size; ?>
-      </td>
-    </tr>
-    
-    <tr>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_installdate'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo $install_date_human; ?>
-      </td>
-      <td class="row2" style="width: 25%;">
-        <?php echo $lang->get('acphome_stat_lastupdate'); ?>
-      </td>
-      <td class="row1" style="width: 25%;">
-        <?php echo $last_upgrade; ?>
-      </td>
-    </tr>
-    
-    <tr>
-      <th colspan="4" class="subhead systemversion">
-        <?php echo $lang->get('acphome_stat_enano_version', array(
-            'version' => enano_version(true),
-            'releasename' => enano_codename(),
-            'aboutlink' => makeUrlNS('Special', 'About_Enano')
-          )); ?>
-      </th>
-    </tr>
-    
-  </table>
-  </div>
-  <?php
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	// Page count
+	$q = $db->sql_query('SELECT COUNT(*) FROM ' . table_prefix . "pages");
+	if ( !$q )
+		$db->_die();
+	list($page_count) = $db->fetchrow_num();
+	$db->free_result();
+	
+	// Edits per day
+	$q = $db->sql_query('SELECT ( COUNT(*) - 1 ) AS edit_count, MIN(time_id) AS install_date FROM ' . table_prefix . 'logs WHERE ( log_type = \'page\' AND action = \'edit\' ) OR ( log_type = \'security\' AND action = \'install_enano\' );');
+	if ( !$q )
+		$db->_die();
+	$edit_info = $db->fetchrow();
+	$install_date =& $edit_info['install_date'];
+	$db->free_result();
+	
+	$days_installed = round( (time() / 86400) - ($install_date / 86400) );
+	if ( $days_installed < 1 )
+		$days_installed = 1;
+	
+	// Comments
+	$q = $db->sql_query('SELECT COUNT(*) FROM ' . table_prefix . "comments");
+	if ( !$q )
+		$db->_die();
+	list($comment_count) = $db->fetchrow_num();
+	$db->free_result();
+	
+	// Users
+	$q = $db->sql_query('SELECT ( COUNT(*) - 1 ) FROM ' . table_prefix . "users");
+	if ( !$q )
+		$db->_die();
+	list($user_count) = $db->fetchrow_num();
+	$db->free_result();
+	
+	// Cache size
+	$cache_size = 0;
+	if ( $dr = @opendir(ENANO_ROOT . '/cache/') )
+	{
+		while ( $dh = @readdir($dr) )
+		{
+			$file = ENANO_ROOT . "/cache/$dh";
+			if ( @is_file($file) )
+				$cache_size += filesize($file);
+		}
+		closedir($dr);
+	}
+	$cache_size = humanize_filesize($cache_size);
+	
+	// Files directory size
+	$files_size = 0;
+	if ( $dr = @opendir(ENANO_ROOT . '/files/') )
+	{
+		while ( $dh = @readdir($dr) )
+		{
+			$file = ENANO_ROOT . "/files/$dh";
+			if ( @is_file($file) )
+				$files_size += filesize($file);
+		}
+		closedir($dr);
+	}
+	$files_size = humanize_filesize($files_size);
+	
+	// Avatar directory size
+	$avatar_size = 0;
+	if ( $dr = @opendir(ENANO_ROOT . '/files/avatars/') )
+	{
+		while ( $dh = @readdir($dr) )
+		{
+			$file = ENANO_ROOT . "/files/avatars/$dh";
+			if ( @is_file($file) )
+				$avatar_size += filesize($file);
+		}
+		closedir($dr);
+	}
+	$avatar_size = humanize_filesize($avatar_size);
+	
+	// Database size
+	$db_size = $lang->get('acphome_stat_dbsize_unsupported');
+	if ( ENANO_DBLAYER == 'MYSQL' )
+	{
+		$q = $db->sql_query('SHOW TABLE STATUS;');
+		if ( $q )
+		{
+			$db_size = 0;
+			while ( $row = $db->fetchrow() )
+			{
+				if ( preg_match('/^' . table_prefix . '/', $row['Name']) )
+				{
+					$db_size += $row['Data_length'] + $row['Index_length'];
+				}
+			}
+			$db_size = humanize_filesize($db_size);
+		}
+	}
+	else if ( ENANO_DBLAYER == 'PGSQL' )
+	{
+		require(ENANO_ROOT . '/config.php');
+		global $dbname, $dbuser, $dbpasswd;
+		$dbuser = false;
+		$dbpasswd = false;
+		
+		$q = $db->sql_query('SELECT pg_database_size(\'' . $db->escape($dbname) . '\');');
+		if ( $q )
+		{
+			list($db_size) = $db->fetchrow_num();
+			$db_size = humanize_filesize($db_size);
+			$db->free_result();
+		}
+	}
+	
+	// Install date
+	$site_age = floor((time() - $install_date) / 86400);
+	$install_date_human = MemberlistFormatter::format_date($install_date);
+	if ( $site_age > 7 )
+	{
+		$install_date_human .= ' ' . $lang->get('acphome_stat_installdate_ago', array(
+				'days' => number_format($site_age)
+			));
+	}
+	
+	// Last upgrade
+	$q = $db->sql_query('SELECT time_id FROM ' . table_prefix . "logs WHERE log_type = 'security' AND action = 'upgrade_enano' ORDER BY time_id DESC LIMIT 1;");
+	if ( !$q )
+		$db->_die();
+	
+	if ( $db->numrows() < 1 )
+	{
+		$last_upgrade = $lang->get('acphome_stat_lastupdate_never');
+	}
+	else
+	{
+		list($last_upgrade) = $db->fetchrow_num();
+		$ver_age = floor((time() - $last_upgrade) / 86400);
+		$last_upgrade = MemberlistFormatter::format_date($last_upgrade);
+		if ( $ver_age > 7 )
+		{
+			$last_upgrade .= ' ' . $lang->get('acphome_stat_installdate_ago', array(
+					'days' => number_format($ver_age)
+				));
+		}
+	}
+	$db->free_result();
+	
+	?>
+	<div class="tblholder">
+	<table border="0" cellspacing="1" cellpadding="4">
+		<tr>
+			<th colspan="4">
+				<?php echo $lang->get('acphome_stat_header'); ?>
+			</th>
+		</tr>
+		
+		<tr>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_numpages'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo strval($page_count); ?>
+			</td>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_edits'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_edits_data', array('edit_count' => $edit_info['edit_count'], 'per_day' => number_format($edit_info['edit_count'] / $days_installed, 2))); ?>
+			</td>
+		</tr>
+		
+		<tr>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_comments'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_comments_data', array('comment_count' => $comment_count, 'per_day' => number_format($comment_count / $days_installed, 2))); ?>
+			</td>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_users'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo strval($user_count); ?>
+			</td>
+		</tr>
+		
+		<tr>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_filesize'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo $files_size; ?>
+			</td>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_cachesize'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo $cache_size; ?>
+			</td>
+		</tr>
+		
+		<tr>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_avatarsize'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo $avatar_size; ?>
+			</td>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_dbsize'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo $db_size; ?>
+			</td>
+		</tr>
+		
+		<tr>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_installdate'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo $install_date_human; ?>
+			</td>
+			<td class="row2" style="width: 25%;">
+				<?php echo $lang->get('acphome_stat_lastupdate'); ?>
+			</td>
+			<td class="row1" style="width: 25%;">
+				<?php echo $last_upgrade; ?>
+			</td>
+		</tr>
+		
+		<tr>
+			<th colspan="4" class="subhead systemversion">
+				<?php echo $lang->get('acphome_stat_enano_version', array(
+						'version' => enano_version(true),
+						'releasename' => enano_codename(),
+						'aboutlink' => makeUrlNS('Special', 'About_Enano')
+					)); ?>
+			</th>
+		</tr>
+		
+	</table>
+	</div>
+	<?php
 }
--- a/plugins/admin/LangManager.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/LangManager.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,620 +13,620 @@
 
 function page_Admin_LangManager()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $cache;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  if ( isset($_POST['action']) )
-  {
-    $action =& $_POST['action'];
-    // Parse parameters
-    if ( strpos($action, ';') )
-    {
-      // Parameter section
-      $parms = substr($action, strpos($action, ';') + 1);
-      
-      // Action name section
-      $action = substr($action, 0, strpos($action, ';'));
-      
-      // Match all parameters
-      preg_match_all('/([a-z0-9_]+)=(.+?)(;|$)/', $parms, $matches);
-      $parms = array();
-      
-      // For each full parameter, assign $parms an associative value
-      foreach ( $matches[0] as $i => $_ )
-      {
-        $parm = $matches[2][$i];
-        
-        // Is this parameter in the form of an integer?
-        // (designed to ease validation later)
-        if ( ctype_digit($parm) )
-          // Yes, run intval(), this enabling is_int()-ish checks
-          $parm = intval($parm);
-        
-        $parms[$matches[1][$i]] = $parm;
-      }
-    }
-    switch ( $action )
-    {
-      case 'install_language':
-        
-        if ( defined('ENANO_DEMO_MODE') )
-        {
-          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
-          break;
-        }
-        
-        $lang_list = list_available_languages();
-        // Verify that we have this language's metadata
-        if ( isset($lang_list[@$parms['iso639']]) )
-        {
-          // From here it's all downhill :-)
-          $lang_code =& $parms['iso639'];
-          $lang_data =& $lang_list[$lang_code];
-          
-          $result = install_language($lang_code, $lang_data['name_eng'], $lang_data['name']);
-          if ( $result )
-          {
-            // Language installed. Import the language files.
-            $lang_local = new Language($lang_code);
-            if ( file_exists(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json") )
-            {
-              $lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json");
-            }
-            else
-            {
-              foreach ( array('core', 'admin', 'tools', 'user') as $file )
-              {
-                $lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/$file.json");
-              }
-            }
-            unset($lang_local);
-            
-            echo '<div class="info-box">' . $lang->get('acplm_msg_lang_install_success', array('lang_name' => htmlspecialchars($lang_data['name_eng']))) . '</div>';
-          }
-        }
-        break;
-      case 'modify_language':
-        
-        $lang_id =& $parms['lang_id'];
-        if ( !is_int($lang_id) )
-        {
-          echo 'Hacking attempt';
-          break;
-        }
-        
-        if ( isset($parms['finish']) && !empty($_POST['lang_name_native']) && !empty($_POST['lang_name_english']) && !defined('ENANO_DEMO_MODE') )
-        {
-          // We just did validation above, it's safe to save.
-          $name_native = $db->escape($_POST['lang_name_native']);
-          $name_english = $db->escape($_POST['lang_name_english']);
-          
-          $q = $db->sql_query('UPDATE ' . table_prefix . "language SET lang_name_native = '$name_native', lang_name_default = '$name_english' WHERE lang_id = $lang_id;");
-          if ( !$q )
-            $db->_die();
-          
-          echo '<div class="info-box">' . $lang->get('acplm_msg_basic_save_success') . '</div>';
-        }
-        else if ( isset($parms['finish']) && defined('ENANO_DEMO_MODE') )
-        {
-          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
-        }
-        
-        // Select language data
-        $q = $db->sql_query('SELECT lang_name_native, lang_name_default, lang_code FROM ' . table_prefix . "language WHERE lang_id = $lang_id;");
-        if ( !$q )
-          $db->_die();
-        
-        list($name_native, $name_english, $lang_code) = $db->fetchrow_num();
-        
-        // Output properties table
-        echo '<h3>' . $lang->get('acplm_heading_modify') . '</h3>';
-        
-        acp_start_form();
-        
-        ?>
-          <div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">
-              <tr>
-                <th colspan="2">
-                  <?php
-                    echo $lang->get('acplm_th_lang_basic');
-                  ?>
-                </th>
-              </tr>
-              <tr>
-              <td class="row2" style="width: 50%;">
-                  <?php
-                    echo str_replace('"', '', $lang->get('acplm_field_lang_name_native'));
-                  ?>
-                </td>
-                <td class="row1">
-                  <input type="text" name="lang_name_native" value="<?php echo htmlspecialchars($name_native); ?>" />
-                </td>
-              </tr>
-              <tr>
-                <td class="row2">
-                  <?php
-                    echo $lang->get('acplm_field_lang_name_english');
-                  ?>
-                </td>
-                <td class="row1">
-                  <input type="text" name="lang_name_english" value="<?php echo htmlspecialchars($name_english); ?>" />
-                </td>
-              </tr>
-              <tr>
-                <td class="row2">
-                  <?php
-                    echo $lang->get('acplm_field_lang_code') . '<br />'
-                       . '<small>' . $lang->get('acplm_field_lang_code_hint') . '</small>';
-                  ?>
-                </td>
-                <td class="row1">
-                  <?php
-                    echo $lang_code;
-                  ?>
-                </td>
-              </tr>
-              <tr>
-                <th class="subhead" colspan="2">
-                  <button name="action" value="modify_language;finish=1;lang_id=<?php echo $lang_id; ?>"><?php echo $lang->get('etc_save_changes'); ?></button>
-                </th>
-              </tr>
-            </table>
-          </div>
-        </form>
-        
-        <?php
-        acp_start_form();
-        ?>
-        
-        <h3><?php echo $lang->get('acplm_heading_edit_strings_portal'); ?></h3>
-        <p><?php echo $lang->get('acplm_msg_edit_strings_portal_intro'); ?></p>
-        
-        <p>
-        
-        <?php
-        
-        // Grab a Language object
-        if ( $lang->lang_id == $lang_id )
-        {
-          $lang_local =& $lang;
-        }
-        else
-        {
-          $lang_local = new Language($lang_id);
-          $lang_local->fetch();
-        }
-        
-        $categories_loc = array();
-        
-        // Using the & here ensures that a reference is created, thus avoiding wasting memory
-        foreach ( $lang_local->strings as $cat => &$_ )
-        {
-          unset($_);
-          $categories_loc[$cat] = htmlspecialchars($lang->get("meta_$cat"));
-        }
-        
-        asort($categories_loc);
-        
-        echo '<select name="cat_id">';
-        foreach ( $categories_loc as $cat_id => $cat_name)
-        {
-          echo "<option value=\"$cat_id\">$cat_name</option>";
-        }
-        echo '</select>';
-        
-        ?>
-        <button name="action" value="edit_strings;lang_id=<?php echo $lang_id; ?>">
-          <?php echo $lang->get('acplm_btn_edit_strings_portal'); ?>
-        </button>
-        </p>
-        
-        <h3><?php echo $lang->get('acplm_heading_reimport_portal'); ?></h3>
-        <p><?php echo $lang->get('acplm_msg_reimport_portal_intro'); ?></p>
-        
-        <p>
-          <button name="action" value="reimport;iso639=<?php echo $lang_code; ?>;lang_id=<?php echo $lang_id; ?>">
-            <?php echo $lang->get('acplm_btn_reimport'); ?>
-          </button>
-        </p>
-        
-        </form>
-        
-        <?php
-        
-        echo '<h3>' . $lang->get('acplm_heading_backup') . '</h3>';
-        echo '<p>' . $lang->get('acplm_backup_intro') . '</p>';
-        
-        echo '<form action="' . makeUrlNS('Admin', 'LangManager') . '" method="post">';
-        echo '<button name="action" value="backup_language;lang_id=' . $lang_id . '">' . $lang->get('acplm_btn_create_backup') . '</button>';
-        echo '</form>';
-        
-        return true;
-      case 'edit_strings':
-        
-        $cat_id = @$_POST['cat_id'];
-        if ( !preg_match('/^[a-z0-9]+$/', $cat_id) || !is_int(@$parms['lang_id']) )
-          break;
-        
-        $lang_id =& $parms['lang_id'];
-        
-        if ( isset($parms['save']) && !defined('ENANO_DEMO_MODE') )
-        {
-          // Grab a Language object
-          if ( $lang->lang_id == $lang_id )
-          {
-            $lang_local =& $lang;
-          }
-          else
-          {
-            $lang_local = new Language($lang_id);
-          }
-          // Main save loop
-          // Trying to minimize queries as much as possible here, but you know how that goes.
-          $count_upd = 0;
-          foreach ( $_POST['string'] as $string_id => $user_content )
-          {
-            $curr_content = $lang_local->get_uncensored("{$cat_id}_{$string_id}");
-            if ( $curr_content != $user_content )
-            {
-              $count_upd++;
-              $user_content = $db->escape($user_content);
-              $string_id = $db->escape($string_id);
-              $q = $db->sql_query('UPDATE ' . table_prefix . "language_strings SET string_content = '$user_content' WHERE lang_id = $lang_id AND string_category = '$cat_id' AND string_name = '$string_id';");
-              if ( !$q )
-                $db->_die();
-            }
-          }
-          if ( $count_upd > 0 )
-          {
-            // Update the cache
-            $lang_local->regen_caches();
-            
-            // Update modification time
-            $q = $db->sql_query('UPDATE ' . table_prefix . "language SET last_changed = " . time() . " WHERE lang_id = $lang_id;");
-            if ( !$q )
-              $db->_die();
-          }
-          
-          echo '<div class="info-box">' . $lang->get('acplm_msg_string_save_success') . '</div>';
-        }
-        else if ( isset($parms['save']) && defined('ENANO_DEMO_MODE') )
-        {
-          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
-        }
-        
-        acp_start_form();
-        
-        $cat_name = $lang->get("meta_$cat_id");
-        echo '<h3>' . $lang->get('acplm_editor_heading', array('cat_name' => $cat_name)) . '</h3>';
-        
-        // Fetch all strings
-        // This is more efficient than iterating through $lang->strings, I think.
-        $q = $db->sql_query('SELECT string_id, string_name, string_content FROM ' . table_prefix . "language_strings WHERE string_category = '$cat_id' AND lang_id = $lang_id;");
-        if ( !$q )
-          $db->_die();
-        
-        ?>
-        <div class="tblholder">
-          <table border="0" cellspacing="1" cellpadding="4">
-            <tr>
-              <th style="width: 3%;"><?php echo $lang->get('acplm_editor_col_string_name'); ?></th>
-              <th><?php echo $lang->get('acplm_editor_col_string_content'); ?></th>
-            </tr>
-        <?php
-        
-        while ( $row = $db->fetchrow_num() )
-        {
-          list($string_id, $string_name, $string_content) = $row;
-          unset($row);
-          
-          echo '<tr>';
-          
-          if ( strpos($string_content, "\n") )
-          {
-            $editor = '<textarea rows="' . get_line_count($string_content) . '" cols="50" style="width: 99%;" ';
-            $editor .= 'name="string[' . htmlspecialchars($string_name) . ']" ';
-            $editor .= '>' . htmlspecialchars($string_content);
-            $editor .= '</textarea>';
-          }
-          else
-          {
-            $editor = '<input type="text" size="50" style="width: 99%;" ';
-            $editor .= 'name="string[' . htmlspecialchars($string_name) . ']" ';
-            $editor .= 'value="' . htmlspecialchars($string_content) . '" ';
-            $editor .= '/>';
-          }
-          
-          echo '<td class="row2">' . htmlspecialchars($string_name) . '</td>';
-          echo '<td class="row1">' . $editor . '</td>';
-          
-          
-          echo '</tr>';
-          echo "\n";
-        }
-        
-        echo '<tr>
-                <th class="subhead" colspan="2">';
-                
-        echo '<input type="hidden" name="cat_id" value="' . $cat_id . '" />';
-                
-        // Button: save
-        echo '<button name="action" value="edit_strings;lang_id=' . $lang_id . ';save=1" style="font-weight: bold;">' . $lang->get('etc_save_changes') . '</button> ';
-        // Button: revert
-        echo '<button name="action" value="edit_strings;lang_id=' . $lang_id . '" style="font-weight: normal;">' . $lang->get('acplm_editor_btn_revert') . '</button> ';
-        // Button: cancel
-        echo '<button name="action" value="modify_language;lang_id=' . $lang_id . '" style="font-weight: normal;">' . $lang->get('acplm_editor_btn_cancel') . '</button>';
-                
-        echo '  </th>
-              </tr>';
-        
-        ?>
-          </table>
-        </div>
-        <?php
-        echo '</form>';
-        
-        return true;
-      case 'reimport':
-        if ( !isset($parms['iso639']) || !is_int(@$parms['lang_id']) )
-          break;
-        
-        if ( defined('ENANO_DEMO_MODE') )
-        {
-          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
-          break;
-        }
-        
-        $lang_code =& $parms['iso639'];
-        $lang_id =& $parms['lang_id'];
-        
-        $lang_list = list_available_languages();
-        
-        if ( !isset($lang_list[$lang_code]) )
-          break;
-        
-        // Grab a Language object
-        if ( $lang->lang_id == $lang_id )
-        {
-          $lang_local =& $lang;
-        }
-        else
-        {
-          $lang_local = new Language($lang_id);
-        }
-        
-        $lang_data =& $lang_list[$lang_code];
-        
-        // This is the big re-import loop
-        if ( file_exists(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json") )
-        {
-          $lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json");
-        }
-        else
-        {
-          foreach ( array('core', 'admin', 'tools', 'user') as $file )
-          {
-            $lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/$file.json");
-          }
-        }
-        
-        echo '<div class="info-box">' . $lang->get('acplm_msg_reimport_success') . '</div>';
-        
-        break;
-      case 'backup_language':
-        if ( !is_int(@$parms['lang_id']) )
-          break;
-        
-        $lang_id =& $parms['lang_id'];
-        
-        // Grab a Language object
-        if ( $lang->lang_id == $lang_id )
-        {
-          $lang_local =& $lang;
-        }
-        else
-        {
-          $lang_local = new Language($lang_id);
-        }
-        
-        $filename = 'enano_lang_' . $lang_local->lang_code . '_' . enano_date('ymd') . '.json';
-        
-        // Free as much memory as possible
-        $db->close();
-        unset($GLOBALS['session'], $GLOBALS['paths'], $GLOBALS['template'], $GLOBALS['plugins']);
-        
-        // HTTP headers
-        header('Content-type: application/json');
-        header('Content-disposition: attachment; filename=' . $filename);
-        
-        // Export to JSON
-        $lang_struct = array(
-            'categories' => array_keys($lang_local->strings),
-            'strings' => $lang_local->strings
-          );
-        
-        $lang_struct = enano_json_encode($lang_struct);
-        
-        header('Content-length: ' . strlen($lang_struct));
-        echo $lang_struct;
-        
-        exit;
-        
-      case 'uninstall_language':
-        if ( !is_int(@$parms['lang_id']) )
-          break;
-        
-        if ( defined('ENANO_DEMO_MODE') )
-        {
-          echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
-          break;
-        }
-        
-        $lang_id =& $parms['lang_id'];
-        
-        if ( isset($parms['confirm']) )
-        {
-          $lang_default = intval(getConfig('default_language'));
-          if ( $lang_default == $lang_id )
-          {
-            echo '<div class="error-box">' . $lang->get('acplm_err_cant_uninstall_default') . '</div>';
-            break;
-          }
-          if ( $lang_id == $lang->lang_id )
-          {
-            // Unload the current language since it's about to be uninstalled
-            unset($lang, $GLOBALS['lang']);
-            $GLOBALS['lang'] = new Language($lang_default);
-            global $lang;
-          }
-          // We're clear
-          
-          // Remove cache files
-          $cache_file = ENANO_ROOT . "/cache/lang_{$lang_id}.php";
-          if ( file_exists($cache_file) )
-            @unlink($cache_file);
-          
-          $cache->purge("lang_{$lang_id}");
-          
-          // Remove strings
-          $q = $db->sql_query('DELETE FROM ' . table_prefix . "language_strings WHERE lang_id = $lang_id;");
-          if ( !$q )
-            $db->_die();
-          
-          // Delete the language
-          $q = $db->sql_query('DELETE FROM ' . table_prefix . "language WHERE lang_id = $lang_id;");
-          if ( !$q )
-            $db->_die();
-          
-          echo '<div class="info-box">' . $lang->get('acplm_msg_uninstall_success') . '</div>';
-          break;
-        }
-        
-        acp_start_form();
-        
-        echo '<h3>' . $lang->get('acplm_uninstall_confirm_title') . '</h3>';
-        echo '<p>' . $lang->get('acplm_uninstall_confirm_body') . '</p>';
-        
-        echo '<p><button name="action" style="font-weight: bold;" value="uninstall_language;lang_id=' . $lang_id . ';confirm=1">' . $lang->get('acplm_btn_uninstall_confirm') . '</button> ';
-        echo '<button name="action" value="home">' . $lang->get('acplm_btn_uninstall_cancel') . '</button></p>';
-        
-        echo '</form>';
-        return true;
-    }
-  }
-  
-  acp_start_form();
-  
-  // Select current languages
-  $q = $db->sql_query('SELECT lang_code, lang_name_native, lang_name_default, lang_id FROM ' . table_prefix . "language ORDER BY lang_id ASC;");
-  if ( !$q )
-    $db->_die();
-  
-  // Language properties/edit/delete portal table
-  echo '<h3>' . $lang->get('acplm_heading_editor_portal') . '</h3>';
-  
-  echo '<div class="tblholder">';
-  echo '<table border="0" cellspacing="1" cellpadding="4">';
-  echo '<tr>
-          <th>' . $lang->get('acplm_col_lang_id') . '</th>
-          <th>' . $lang->get('acplm_col_lang_code') . '</th>
-          <th>' . $lang->get('acplm_col_lang_name') . '</th>
-          <th>' . $lang->get('acplm_col_lang_name_eng') . '</th>
-          <th></th>
-        </tr>';
-  
-  $cls = 'row2';
-  
-  $btn_edit = $lang->get('acplm_portal_btn_edit');
-  $btn_unin = $lang->get('acplm_portal_btn_unin');
-  
-  while ( $row = $db->fetchrow($q) )
-  {
-    $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-    
-    echo '<tr>';
-    
-    $lang_code = htmlspecialchars($row['lang_code']);
-    $lang_name_native  = htmlspecialchars($row['lang_name_native']);
-    $lang_name_english = htmlspecialchars($row['lang_name_default']);
-    
-    echo "<td class=\"$cls\" style=\"text-align: center;\">{$row['lang_id']}</td>";
-    echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_code}</td>";
-    echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_name_native}</td>";
-    echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_name_english}</td>";
-    echo "<td class=\"$cls\" style=\"text-align: center;\"><button name=\"action\" value=\"modify_language;lang_id={$row['lang_id']}\">$btn_edit</button> <button name=\"action\" value=\"uninstall_language;lang_id={$row['lang_id']}\">$btn_unin</button></td>";
-    
-    echo '</tr>';
-  }
-  
-  echo '</table></div>';
-  
-  // Reset the result pointer to zero so we can fetch that list of languages again
-  if ( !$db->sql_data_seek(0, $q) )
-  {
-    $db->_die('LangManager doing seek back to zero for installation blacklist');
-  }
-  
-  // $lang_list is fetched by the posthandler sometimes
-  if ( !isset($lang_list) )
-  {
-    // Build a list of languages in the languages/ directory, then
-    // eliminate the ones that are already installed.
-    $lang_list = list_available_languages();
-  }
-  
-  while ( $row = $db->fetchrow($q) )
-  {
-    $lang_code =& $row['lang_code'];
-    if ( isset($lang_list[$lang_code]) )
-    {
-      unset($lang_list[$lang_code]);
-      unset($lang_list[$lang_code]); // PHP <5.1.4 Zend bug
-    }
-  }
-  
-  if ( count($lang_list) > 0 )
-  {
-    echo '<h3>' . $lang->get('acplm_heading_install') . '</h3>';
-    echo '<div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">
-              <tr>
-                <th>' . $lang->get('acplm_col_lang_code') . '</th>
-                <th>' . $lang->get('acplm_col_lang_name') . '</th>
-                <th>' . $lang->get('acplm_col_lang_name_eng') . '</th>
-                <th></th>
-              </tr>';
-              
-    $cls = 'row2';
-    foreach ( $lang_list as $lang_code => $lang_data )
-    {
-      $cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
-      
-      echo '<tr>';
-      
-      $lang_code = htmlspecialchars($lang_code);
-      $lang_data['name'] = htmlspecialchars($lang_data['name']);
-      $lang_data['name_eng'] = htmlspecialchars($lang_data['name_eng']);
-      
-      echo "<td class=\"$cls\" style=\"text-align: center;\">$lang_code</td>";
-      echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_data['name']}</td>";
-      echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_data['name_eng']}</td>";
-      echo "<td class=\"$cls\" style=\"text-align: center;\"><button name=\"action\" value=\"install_language;iso639=$lang_code\">" . $lang->get('acplm_btn_install_language') . "</button></td>";
-      
-      echo '</tr>';
-    }
-    echo '    </tr>
-            </table>
-          </div>';
-  }
-  echo '</form>';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $cache;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	if ( isset($_POST['action']) )
+	{
+		$action =& $_POST['action'];
+		// Parse parameters
+		if ( strpos($action, ';') )
+		{
+			// Parameter section
+			$parms = substr($action, strpos($action, ';') + 1);
+			
+			// Action name section
+			$action = substr($action, 0, strpos($action, ';'));
+			
+			// Match all parameters
+			preg_match_all('/([a-z0-9_]+)=(.+?)(;|$)/', $parms, $matches);
+			$parms = array();
+			
+			// For each full parameter, assign $parms an associative value
+			foreach ( $matches[0] as $i => $_ )
+			{
+				$parm = $matches[2][$i];
+				
+				// Is this parameter in the form of an integer?
+				// (designed to ease validation later)
+				if ( ctype_digit($parm) )
+					// Yes, run intval(), this enabling is_int()-ish checks
+					$parm = intval($parm);
+				
+				$parms[$matches[1][$i]] = $parm;
+			}
+		}
+		switch ( $action )
+		{
+			case 'install_language':
+				
+				if ( defined('ENANO_DEMO_MODE') )
+				{
+					echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
+					break;
+				}
+				
+				$lang_list = list_available_languages();
+				// Verify that we have this language's metadata
+				if ( isset($lang_list[@$parms['iso639']]) )
+				{
+					// From here it's all downhill :-)
+					$lang_code =& $parms['iso639'];
+					$lang_data =& $lang_list[$lang_code];
+					
+					$result = install_language($lang_code, $lang_data['name_eng'], $lang_data['name']);
+					if ( $result )
+					{
+						// Language installed. Import the language files.
+						$lang_local = new Language($lang_code);
+						if ( file_exists(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json") )
+						{
+							$lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json");
+						}
+						else
+						{
+							foreach ( array('core', 'admin', 'tools', 'user') as $file )
+							{
+								$lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/$file.json");
+							}
+						}
+						unset($lang_local);
+						
+						echo '<div class="info-box">' . $lang->get('acplm_msg_lang_install_success', array('lang_name' => htmlspecialchars($lang_data['name_eng']))) . '</div>';
+					}
+				}
+				break;
+			case 'modify_language':
+				
+				$lang_id =& $parms['lang_id'];
+				if ( !is_int($lang_id) )
+				{
+					echo 'Hacking attempt';
+					break;
+				}
+				
+				if ( isset($parms['finish']) && !empty($_POST['lang_name_native']) && !empty($_POST['lang_name_english']) && !defined('ENANO_DEMO_MODE') )
+				{
+					// We just did validation above, it's safe to save.
+					$name_native = $db->escape($_POST['lang_name_native']);
+					$name_english = $db->escape($_POST['lang_name_english']);
+					
+					$q = $db->sql_query('UPDATE ' . table_prefix . "language SET lang_name_native = '$name_native', lang_name_default = '$name_english' WHERE lang_id = $lang_id;");
+					if ( !$q )
+						$db->_die();
+					
+					echo '<div class="info-box">' . $lang->get('acplm_msg_basic_save_success') . '</div>';
+				}
+				else if ( isset($parms['finish']) && defined('ENANO_DEMO_MODE') )
+				{
+					echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
+				}
+				
+				// Select language data
+				$q = $db->sql_query('SELECT lang_name_native, lang_name_default, lang_code FROM ' . table_prefix . "language WHERE lang_id = $lang_id;");
+				if ( !$q )
+					$db->_die();
+				
+				list($name_native, $name_english, $lang_code) = $db->fetchrow_num();
+				
+				// Output properties table
+				echo '<h3>' . $lang->get('acplm_heading_modify') . '</h3>';
+				
+				acp_start_form();
+				
+				?>
+					<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">
+							<tr>
+								<th colspan="2">
+									<?php
+										echo $lang->get('acplm_th_lang_basic');
+									?>
+								</th>
+							</tr>
+							<tr>
+							<td class="row2" style="width: 50%;">
+									<?php
+										echo str_replace('"', '', $lang->get('acplm_field_lang_name_native'));
+									?>
+								</td>
+								<td class="row1">
+									<input type="text" name="lang_name_native" value="<?php echo htmlspecialchars($name_native); ?>" />
+								</td>
+							</tr>
+							<tr>
+								<td class="row2">
+									<?php
+										echo $lang->get('acplm_field_lang_name_english');
+									?>
+								</td>
+								<td class="row1">
+									<input type="text" name="lang_name_english" value="<?php echo htmlspecialchars($name_english); ?>" />
+								</td>
+							</tr>
+							<tr>
+								<td class="row2">
+									<?php
+										echo $lang->get('acplm_field_lang_code') . '<br />'
+ 											. '<small>' . $lang->get('acplm_field_lang_code_hint') . '</small>';
+									?>
+								</td>
+								<td class="row1">
+									<?php
+										echo $lang_code;
+									?>
+								</td>
+							</tr>
+							<tr>
+								<th class="subhead" colspan="2">
+									<button name="action" value="modify_language;finish=1;lang_id=<?php echo $lang_id; ?>"><?php echo $lang->get('etc_save_changes'); ?></button>
+								</th>
+							</tr>
+						</table>
+					</div>
+				</form>
+				
+				<?php
+				acp_start_form();
+				?>
+				
+				<h3><?php echo $lang->get('acplm_heading_edit_strings_portal'); ?></h3>
+				<p><?php echo $lang->get('acplm_msg_edit_strings_portal_intro'); ?></p>
+				
+				<p>
+				
+				<?php
+				
+				// Grab a Language object
+				if ( $lang->lang_id == $lang_id )
+				{
+					$lang_local =& $lang;
+				}
+				else
+				{
+					$lang_local = new Language($lang_id);
+					$lang_local->fetch();
+				}
+				
+				$categories_loc = array();
+				
+				// Using the & here ensures that a reference is created, thus avoiding wasting memory
+				foreach ( $lang_local->strings as $cat => &$_ )
+				{
+					unset($_);
+					$categories_loc[$cat] = htmlspecialchars($lang->get("meta_$cat"));
+				}
+				
+				asort($categories_loc);
+				
+				echo '<select name="cat_id">';
+				foreach ( $categories_loc as $cat_id => $cat_name)
+				{
+					echo "<option value=\"$cat_id\">$cat_name</option>";
+				}
+				echo '</select>';
+				
+				?>
+				<button name="action" value="edit_strings;lang_id=<?php echo $lang_id; ?>">
+					<?php echo $lang->get('acplm_btn_edit_strings_portal'); ?>
+				</button>
+				</p>
+				
+				<h3><?php echo $lang->get('acplm_heading_reimport_portal'); ?></h3>
+				<p><?php echo $lang->get('acplm_msg_reimport_portal_intro'); ?></p>
+				
+				<p>
+					<button name="action" value="reimport;iso639=<?php echo $lang_code; ?>;lang_id=<?php echo $lang_id; ?>">
+						<?php echo $lang->get('acplm_btn_reimport'); ?>
+					</button>
+				</p>
+				
+				</form>
+				
+				<?php
+				
+				echo '<h3>' . $lang->get('acplm_heading_backup') . '</h3>';
+				echo '<p>' . $lang->get('acplm_backup_intro') . '</p>';
+				
+				echo '<form action="' . makeUrlNS('Admin', 'LangManager') . '" method="post">';
+				echo '<button name="action" value="backup_language;lang_id=' . $lang_id . '">' . $lang->get('acplm_btn_create_backup') . '</button>';
+				echo '</form>';
+				
+				return true;
+			case 'edit_strings':
+				
+				$cat_id = @$_POST['cat_id'];
+				if ( !preg_match('/^[a-z0-9]+$/', $cat_id) || !is_int(@$parms['lang_id']) )
+					break;
+				
+				$lang_id =& $parms['lang_id'];
+				
+				if ( isset($parms['save']) && !defined('ENANO_DEMO_MODE') )
+				{
+					// Grab a Language object
+					if ( $lang->lang_id == $lang_id )
+					{
+						$lang_local =& $lang;
+					}
+					else
+					{
+						$lang_local = new Language($lang_id);
+					}
+					// Main save loop
+					// Trying to minimize queries as much as possible here, but you know how that goes.
+					$count_upd = 0;
+					foreach ( $_POST['string'] as $string_id => $user_content )
+					{
+						$curr_content = $lang_local->get_uncensored("{$cat_id}_{$string_id}");
+						if ( $curr_content != $user_content )
+						{
+							$count_upd++;
+							$user_content = $db->escape($user_content);
+							$string_id = $db->escape($string_id);
+							$q = $db->sql_query('UPDATE ' . table_prefix . "language_strings SET string_content = '$user_content' WHERE lang_id = $lang_id AND string_category = '$cat_id' AND string_name = '$string_id';");
+							if ( !$q )
+								$db->_die();
+						}
+					}
+					if ( $count_upd > 0 )
+					{
+						// Update the cache
+						$lang_local->regen_caches();
+						
+						// Update modification time
+						$q = $db->sql_query('UPDATE ' . table_prefix . "language SET last_changed = " . time() . " WHERE lang_id = $lang_id;");
+						if ( !$q )
+							$db->_die();
+					}
+					
+					echo '<div class="info-box">' . $lang->get('acplm_msg_string_save_success') . '</div>';
+				}
+				else if ( isset($parms['save']) && defined('ENANO_DEMO_MODE') )
+				{
+					echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
+				}
+				
+				acp_start_form();
+				
+				$cat_name = $lang->get("meta_$cat_id");
+				echo '<h3>' . $lang->get('acplm_editor_heading', array('cat_name' => $cat_name)) . '</h3>';
+				
+				// Fetch all strings
+				// This is more efficient than iterating through $lang->strings, I think.
+				$q = $db->sql_query('SELECT string_id, string_name, string_content FROM ' . table_prefix . "language_strings WHERE string_category = '$cat_id' AND lang_id = $lang_id;");
+				if ( !$q )
+					$db->_die();
+				
+				?>
+				<div class="tblholder">
+					<table border="0" cellspacing="1" cellpadding="4">
+						<tr>
+							<th style="width: 3%;"><?php echo $lang->get('acplm_editor_col_string_name'); ?></th>
+							<th><?php echo $lang->get('acplm_editor_col_string_content'); ?></th>
+						</tr>
+				<?php
+				
+				while ( $row = $db->fetchrow_num() )
+				{
+					list($string_id, $string_name, $string_content) = $row;
+					unset($row);
+					
+					echo '<tr>';
+					
+					if ( strpos($string_content, "\n") )
+					{
+						$editor = '<textarea rows="' . get_line_count($string_content) . '" cols="50" style="width: 99%;" ';
+						$editor .= 'name="string[' . htmlspecialchars($string_name) . ']" ';
+						$editor .= '>' . htmlspecialchars($string_content);
+						$editor .= '</textarea>';
+					}
+					else
+					{
+						$editor = '<input type="text" size="50" style="width: 99%;" ';
+						$editor .= 'name="string[' . htmlspecialchars($string_name) . ']" ';
+						$editor .= 'value="' . htmlspecialchars($string_content) . '" ';
+						$editor .= '/>';
+					}
+					
+					echo '<td class="row2">' . htmlspecialchars($string_name) . '</td>';
+					echo '<td class="row1">' . $editor . '</td>';
+					
+					
+					echo '</tr>';
+					echo "\n";
+				}
+				
+				echo '<tr>
+								<th class="subhead" colspan="2">';
+								
+				echo '<input type="hidden" name="cat_id" value="' . $cat_id . '" />';
+								
+				// Button: save
+				echo '<button name="action" value="edit_strings;lang_id=' . $lang_id . ';save=1" style="font-weight: bold;">' . $lang->get('etc_save_changes') . '</button> ';
+				// Button: revert
+				echo '<button name="action" value="edit_strings;lang_id=' . $lang_id . '" style="font-weight: normal;">' . $lang->get('acplm_editor_btn_revert') . '</button> ';
+				// Button: cancel
+				echo '<button name="action" value="modify_language;lang_id=' . $lang_id . '" style="font-weight: normal;">' . $lang->get('acplm_editor_btn_cancel') . '</button>';
+								
+				echo '  </th>
+							</tr>';
+				
+				?>
+					</table>
+				</div>
+				<?php
+				echo '</form>';
+				
+				return true;
+			case 'reimport':
+				if ( !isset($parms['iso639']) || !is_int(@$parms['lang_id']) )
+					break;
+				
+				if ( defined('ENANO_DEMO_MODE') )
+				{
+					echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
+					break;
+				}
+				
+				$lang_code =& $parms['iso639'];
+				$lang_id =& $parms['lang_id'];
+				
+				$lang_list = list_available_languages();
+				
+				if ( !isset($lang_list[$lang_code]) )
+					break;
+				
+				// Grab a Language object
+				if ( $lang->lang_id == $lang_id )
+				{
+					$lang_local =& $lang;
+				}
+				else
+				{
+					$lang_local = new Language($lang_id);
+				}
+				
+				$lang_data =& $lang_list[$lang_code];
+				
+				// This is the big re-import loop
+				if ( file_exists(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json") )
+				{
+					$lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/backup.json");
+				}
+				else
+				{
+					foreach ( array('core', 'admin', 'tools', 'user') as $file )
+					{
+						$lang_local->import(ENANO_ROOT . "/language/{$lang_data['dir']}/$file.json");
+					}
+				}
+				
+				echo '<div class="info-box">' . $lang->get('acplm_msg_reimport_success') . '</div>';
+				
+				break;
+			case 'backup_language':
+				if ( !is_int(@$parms['lang_id']) )
+					break;
+				
+				$lang_id =& $parms['lang_id'];
+				
+				// Grab a Language object
+				if ( $lang->lang_id == $lang_id )
+				{
+					$lang_local =& $lang;
+				}
+				else
+				{
+					$lang_local = new Language($lang_id);
+				}
+				
+				$filename = 'enano_lang_' . $lang_local->lang_code . '_' . enano_date('ymd') . '.json';
+				
+				// Free as much memory as possible
+				$db->close();
+				unset($GLOBALS['session'], $GLOBALS['paths'], $GLOBALS['template'], $GLOBALS['plugins']);
+				
+				// HTTP headers
+				header('Content-type: application/json');
+				header('Content-disposition: attachment; filename=' . $filename);
+				
+				// Export to JSON
+				$lang_struct = array(
+						'categories' => array_keys($lang_local->strings),
+						'strings' => $lang_local->strings
+					);
+				
+				$lang_struct = enano_json_encode($lang_struct);
+				
+				header('Content-length: ' . strlen($lang_struct));
+				echo $lang_struct;
+				
+				exit;
+				
+			case 'uninstall_language':
+				if ( !is_int(@$parms['lang_id']) )
+					break;
+				
+				if ( defined('ENANO_DEMO_MODE') )
+				{
+					echo '<div class="error-box">' . $lang->get('acplm_err_lang_install_demo') . '</div>';
+					break;
+				}
+				
+				$lang_id =& $parms['lang_id'];
+				
+				if ( isset($parms['confirm']) )
+				{
+					$lang_default = intval(getConfig('default_language'));
+					if ( $lang_default == $lang_id )
+					{
+						echo '<div class="error-box">' . $lang->get('acplm_err_cant_uninstall_default') . '</div>';
+						break;
+					}
+					if ( $lang_id == $lang->lang_id )
+					{
+						// Unload the current language since it's about to be uninstalled
+						unset($lang, $GLOBALS['lang']);
+						$GLOBALS['lang'] = new Language($lang_default);
+						global $lang;
+					}
+					// We're clear
+					
+					// Remove cache files
+					$cache_file = ENANO_ROOT . "/cache/lang_{$lang_id}.php";
+					if ( file_exists($cache_file) )
+						@unlink($cache_file);
+					
+					$cache->purge("lang_{$lang_id}");
+					
+					// Remove strings
+					$q = $db->sql_query('DELETE FROM ' . table_prefix . "language_strings WHERE lang_id = $lang_id;");
+					if ( !$q )
+						$db->_die();
+					
+					// Delete the language
+					$q = $db->sql_query('DELETE FROM ' . table_prefix . "language WHERE lang_id = $lang_id;");
+					if ( !$q )
+						$db->_die();
+					
+					echo '<div class="info-box">' . $lang->get('acplm_msg_uninstall_success') . '</div>';
+					break;
+				}
+				
+				acp_start_form();
+				
+				echo '<h3>' . $lang->get('acplm_uninstall_confirm_title') . '</h3>';
+				echo '<p>' . $lang->get('acplm_uninstall_confirm_body') . '</p>';
+				
+				echo '<p><button name="action" style="font-weight: bold;" value="uninstall_language;lang_id=' . $lang_id . ';confirm=1">' . $lang->get('acplm_btn_uninstall_confirm') . '</button> ';
+				echo '<button name="action" value="home">' . $lang->get('acplm_btn_uninstall_cancel') . '</button></p>';
+				
+				echo '</form>';
+				return true;
+		}
+	}
+	
+	acp_start_form();
+	
+	// Select current languages
+	$q = $db->sql_query('SELECT lang_code, lang_name_native, lang_name_default, lang_id FROM ' . table_prefix . "language ORDER BY lang_id ASC;");
+	if ( !$q )
+		$db->_die();
+	
+	// Language properties/edit/delete portal table
+	echo '<h3>' . $lang->get('acplm_heading_editor_portal') . '</h3>';
+	
+	echo '<div class="tblholder">';
+	echo '<table border="0" cellspacing="1" cellpadding="4">';
+	echo '<tr>
+					<th>' . $lang->get('acplm_col_lang_id') . '</th>
+					<th>' . $lang->get('acplm_col_lang_code') . '</th>
+					<th>' . $lang->get('acplm_col_lang_name') . '</th>
+					<th>' . $lang->get('acplm_col_lang_name_eng') . '</th>
+					<th></th>
+				</tr>';
+	
+	$cls = 'row2';
+	
+	$btn_edit = $lang->get('acplm_portal_btn_edit');
+	$btn_unin = $lang->get('acplm_portal_btn_unin');
+	
+	while ( $row = $db->fetchrow($q) )
+	{
+		$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+		
+		echo '<tr>';
+		
+		$lang_code = htmlspecialchars($row['lang_code']);
+		$lang_name_native  = htmlspecialchars($row['lang_name_native']);
+		$lang_name_english = htmlspecialchars($row['lang_name_default']);
+		
+		echo "<td class=\"$cls\" style=\"text-align: center;\">{$row['lang_id']}</td>";
+		echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_code}</td>";
+		echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_name_native}</td>";
+		echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_name_english}</td>";
+		echo "<td class=\"$cls\" style=\"text-align: center;\"><button name=\"action\" value=\"modify_language;lang_id={$row['lang_id']}\">$btn_edit</button> <button name=\"action\" value=\"uninstall_language;lang_id={$row['lang_id']}\">$btn_unin</button></td>";
+		
+		echo '</tr>';
+	}
+	
+	echo '</table></div>';
+	
+	// Reset the result pointer to zero so we can fetch that list of languages again
+	if ( !$db->sql_data_seek(0, $q) )
+	{
+		$db->_die('LangManager doing seek back to zero for installation blacklist');
+	}
+	
+	// $lang_list is fetched by the posthandler sometimes
+	if ( !isset($lang_list) )
+	{
+		// Build a list of languages in the languages/ directory, then
+		// eliminate the ones that are already installed.
+		$lang_list = list_available_languages();
+	}
+	
+	while ( $row = $db->fetchrow($q) )
+	{
+		$lang_code =& $row['lang_code'];
+		if ( isset($lang_list[$lang_code]) )
+		{
+			unset($lang_list[$lang_code]);
+			unset($lang_list[$lang_code]); // PHP <5.1.4 Zend bug
+		}
+	}
+	
+	if ( count($lang_list) > 0 )
+	{
+		echo '<h3>' . $lang->get('acplm_heading_install') . '</h3>';
+		echo '<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">
+							<tr>
+								<th>' . $lang->get('acplm_col_lang_code') . '</th>
+								<th>' . $lang->get('acplm_col_lang_name') . '</th>
+								<th>' . $lang->get('acplm_col_lang_name_eng') . '</th>
+								<th></th>
+							</tr>';
+							
+		$cls = 'row2';
+		foreach ( $lang_list as $lang_code => $lang_data )
+		{
+			$cls = ( $cls == 'row1' ) ? 'row2' : 'row1';
+			
+			echo '<tr>';
+			
+			$lang_code = htmlspecialchars($lang_code);
+			$lang_data['name'] = htmlspecialchars($lang_data['name']);
+			$lang_data['name_eng'] = htmlspecialchars($lang_data['name_eng']);
+			
+			echo "<td class=\"$cls\" style=\"text-align: center;\">$lang_code</td>";
+			echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_data['name']}</td>";
+			echo "<td class=\"$cls\" style=\"text-align: center;\">{$lang_data['name_eng']}</td>";
+			echo "<td class=\"$cls\" style=\"text-align: center;\"><button name=\"action\" value=\"install_language;iso639=$lang_code\">" . $lang->get('acplm_btn_install_language') . "</button></td>";
+			
+			echo '</tr>';
+		}
+		echo '    </tr>
+						</table>
+					</div>';
+	}
+	echo '</form>';
 }
 
--- a/plugins/admin/PageEditor.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/PageEditor.php	Sun Mar 28 23:10:46 2010 -0400
@@ -15,162 +15,162 @@
 
 function page_Admin_PageEditor()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  echo '<h3>' . $lang->get('acped_heading_main') . '</h3>';
-  $show_select = true;
-  
-  if ( isset($_REQUEST['action']) || isset($_REQUEST['source']) )
-  {
-    if ( isset($_REQUEST['action']) )
-    {
-      $act =& $_REQUEST['action'];
-      $act = strtolower($act);
-    }
-    else if ( isset($_REQUEST['source']) && $_REQUEST['source'] == 'ajax' )
-    {
-      $act = 'select';
-    }
-    switch ( $act )
-    {
-      case 'save':
-      case 'select':
-        // First step is to determine the page ID and namespace
-        
-        if ( isset($_REQUEST['pid_search']) )
-        {
-          list($page_id, $namespace) = RenderMan::strToPageID($_REQUEST['page_id']);
-          $name = $db->escape(dirtify_page_id($page_id));
-          $page_id = $db->escape(sanitize_page_id($page_id));
-          $namespace = $db->escape($namespace);
-          $name = strtolower($name);
-          $page_id = strtolower($page_id);
-          $sql = "SELECT * FROM " . table_prefix . "pages WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(urlname) LIKE '%$page_id%' OR " . ENANO_SQLFUNC_LOWERCASE . "(name) LIKE '%$name%' ) ORDER BY name ASC;";
-        }
-        else
-        {
-          // pid_search was not set, assume absolute page ID
-          list($page_id, $namespace) = RenderMan::strToPageID($_REQUEST['page_id']);
-          $page_id = $db->escape(sanitize_page_id($page_id));
-          $namespace = $db->escape($namespace);
-          
-          $sql = "SELECT * FROM " . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$namespace';";
-        }
-        
-        if ( !($q = $db->sql_query($sql)) )
-        {
-          $db->_die('PageManager selecting dataset for page');
-        }
-        
-        if ( $db->numrows() < 1 )
-        {
-          echo '<div class="error-box">
-                  ' . $lang->get('acped_err_page_not_found') . '
-                </div>';
-          break;
-        }
-        
-        if ( $db->numrows() > 1 )
-        {
-          // Ambiguous results
-          if ( isset($_REQUEST['pid_search']) )
-          {
-            echo '<h3>' . $lang->get('acped_msg_results_ambiguous_title') . '</h3>';
-            echo '<p>' . $lang->get('acped_msg_results_ambiguous_body') . '</p>';
-            echo '<ul>';
-            while ( $row = $db->fetchrow($q) )
-            {
-              echo '<li>';
-              $pathskey = $paths->nslist[$row['namespace']] . $row['urlname'];
-              $edit_url = makeUrlNS($row['namespace'], $row['urlname']) . '#do:edit';
-              $view_url = makeUrlNS($row['namespace'], $row['urlname']);
-              $page_name = htmlspecialchars(get_page_title_ns( $row['urlname'], $row['namespace'] ));
-              $view_link = $lang->get('acped_ambig_btn_viewpage');
-              echo "<a href=\"$edit_url\">$page_name</a> (<a onclick=\"window.open(this.href); return false;\" href=\"$view_url\">$view_link</a>)";
-              echo '</li>';
-            }
-            echo '</ul>';
-            $show_select = false;
-            break;
-          }
-          else
-          {
-            echo '<p>' . $lang->get('acped_err_ambig_absolute') . '</p>';
-            break;
-          }
-        }
-        
-        // From this point on we can assume that exactly one matching page was found.
-        $dataset = $db->fetchrow();
-        $page_id = $dataset['urlname'];
-        $namespace = $dataset['namespace'];
-        $url = makeUrlNS($namespace, $page_id, false, true) . '#do:edit';
-        $url = addslashes($url);
-        echo '<script type="text/javascript">
-                window.location = \'' . $url . '\';
-              </script>';
-        
-        $show_select = false;
-        break;
-    }
-  }
-  
-  if ( $show_select )
-  {
-    echo '<p>' . $lang->get('acped_hint') . '</p>';
-    
-    // Show the search form
-    
-    $form_action = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageEditor", true);
-    echo "<form action=\"$form_action\" method=\"post\">";
-    echo $lang->get('acped_lbl_field_search') . ' ';
-    echo $template->pagename_field('page_id') . ' ';
-    echo '<input type="hidden" name="action" value="select" />';
-    echo '<input type="submit" name="pid_search" value="' . $lang->get('search_btn_search') . '" />';
-    echo "</form>";
-    
-    // Grab all pages from the database and show a list of pages on the site
-    
-    echo '<h3>' . $lang->get('acped_heading_select_page_from_list') . '</h3>';
-    echo '<p>' . $lang->get('acped_hint_select_page_from_list') . '</p>';
-    
-    $q = $db->sql_query('SELECT COUNT(name) AS num_pages FROM ' . table_prefix . 'pages;');
-    if ( !$q )
-      $db->_die('PageManager doing initial page count');
-    list($num_pages) = $db->fetchrow_num();
-    $db->free_result();
-    
-    $pg_start = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0;
-    
-    $q = $db->sql_query('SELECT urlname, name, namespace, ' . $num_pages . ' AS num_pages, ' . $pg_start . ' AS offset, \'edit\' AS mode FROM ' . table_prefix . 'pages ORDER BY name ASC;');
-    if ( !$q )
-      $db->_die('PageManager doing main select query for page list');
-    
-    // Paginate results
-    $html = paginate(
-        $q,
-        '{urlname}',
-        $num_pages,
-        makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageEditor&offset=%s", false),
-        $pg_start,
-        99,
-        array('urlname' => 'admin_pagemanager_format_listing'),
-        '<div class="tblholder" style="height: 300px; clip: rect(0px, auto, auto, 0px); overflow: auto;">
-        <table border="0" cellspacing="1" cellpadding="4">',
-        '  </table>
-         </div>'
-      );
-    echo $html;
-  }
-  
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	echo '<h3>' . $lang->get('acped_heading_main') . '</h3>';
+	$show_select = true;
+	
+	if ( isset($_REQUEST['action']) || isset($_REQUEST['source']) )
+	{
+		if ( isset($_REQUEST['action']) )
+		{
+			$act =& $_REQUEST['action'];
+			$act = strtolower($act);
+		}
+		else if ( isset($_REQUEST['source']) && $_REQUEST['source'] == 'ajax' )
+		{
+			$act = 'select';
+		}
+		switch ( $act )
+		{
+			case 'save':
+			case 'select':
+				// First step is to determine the page ID and namespace
+				
+				if ( isset($_REQUEST['pid_search']) )
+				{
+					list($page_id, $namespace) = RenderMan::strToPageID($_REQUEST['page_id']);
+					$name = $db->escape(dirtify_page_id($page_id));
+					$page_id = $db->escape(sanitize_page_id($page_id));
+					$namespace = $db->escape($namespace);
+					$name = strtolower($name);
+					$page_id = strtolower($page_id);
+					$sql = "SELECT * FROM " . table_prefix . "pages WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(urlname) LIKE '%$page_id%' OR " . ENANO_SQLFUNC_LOWERCASE . "(name) LIKE '%$name%' ) ORDER BY name ASC;";
+				}
+				else
+				{
+					// pid_search was not set, assume absolute page ID
+					list($page_id, $namespace) = RenderMan::strToPageID($_REQUEST['page_id']);
+					$page_id = $db->escape(sanitize_page_id($page_id));
+					$namespace = $db->escape($namespace);
+					
+					$sql = "SELECT * FROM " . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$namespace';";
+				}
+				
+				if ( !($q = $db->sql_query($sql)) )
+				{
+					$db->_die('PageManager selecting dataset for page');
+				}
+				
+				if ( $db->numrows() < 1 )
+				{
+					echo '<div class="error-box">
+									' . $lang->get('acped_err_page_not_found') . '
+								</div>';
+					break;
+				}
+				
+				if ( $db->numrows() > 1 )
+				{
+					// Ambiguous results
+					if ( isset($_REQUEST['pid_search']) )
+					{
+						echo '<h3>' . $lang->get('acped_msg_results_ambiguous_title') . '</h3>';
+						echo '<p>' . $lang->get('acped_msg_results_ambiguous_body') . '</p>';
+						echo '<ul>';
+						while ( $row = $db->fetchrow($q) )
+						{
+							echo '<li>';
+							$pathskey = $paths->nslist[$row['namespace']] . $row['urlname'];
+							$edit_url = makeUrlNS($row['namespace'], $row['urlname']) . '#do:edit';
+							$view_url = makeUrlNS($row['namespace'], $row['urlname']);
+							$page_name = htmlspecialchars(get_page_title_ns( $row['urlname'], $row['namespace'] ));
+							$view_link = $lang->get('acped_ambig_btn_viewpage');
+							echo "<a href=\"$edit_url\">$page_name</a> (<a onclick=\"window.open(this.href); return false;\" href=\"$view_url\">$view_link</a>)";
+							echo '</li>';
+						}
+						echo '</ul>';
+						$show_select = false;
+						break;
+					}
+					else
+					{
+						echo '<p>' . $lang->get('acped_err_ambig_absolute') . '</p>';
+						break;
+					}
+				}
+				
+				// From this point on we can assume that exactly one matching page was found.
+				$dataset = $db->fetchrow();
+				$page_id = $dataset['urlname'];
+				$namespace = $dataset['namespace'];
+				$url = makeUrlNS($namespace, $page_id, false, true) . '#do:edit';
+				$url = addslashes($url);
+				echo '<script type="text/javascript">
+								window.location = \'' . $url . '\';
+							</script>';
+				
+				$show_select = false;
+				break;
+		}
+	}
+	
+	if ( $show_select )
+	{
+		echo '<p>' . $lang->get('acped_hint') . '</p>';
+		
+		// Show the search form
+		
+		$form_action = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageEditor", true);
+		echo "<form action=\"$form_action\" method=\"post\">";
+		echo $lang->get('acped_lbl_field_search') . ' ';
+		echo $template->pagename_field('page_id') . ' ';
+		echo '<input type="hidden" name="action" value="select" />';
+		echo '<input type="submit" name="pid_search" value="' . $lang->get('search_btn_search') . '" />';
+		echo "</form>";
+		
+		// Grab all pages from the database and show a list of pages on the site
+		
+		echo '<h3>' . $lang->get('acped_heading_select_page_from_list') . '</h3>';
+		echo '<p>' . $lang->get('acped_hint_select_page_from_list') . '</p>';
+		
+		$q = $db->sql_query('SELECT COUNT(name) AS num_pages FROM ' . table_prefix . 'pages;');
+		if ( !$q )
+			$db->_die('PageManager doing initial page count');
+		list($num_pages) = $db->fetchrow_num();
+		$db->free_result();
+		
+		$pg_start = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0;
+		
+		$q = $db->sql_query('SELECT urlname, name, namespace, ' . $num_pages . ' AS num_pages, ' . $pg_start . ' AS offset, \'edit\' AS mode FROM ' . table_prefix . 'pages ORDER BY name ASC;');
+		if ( !$q )
+			$db->_die('PageManager doing main select query for page list');
+		
+		// Paginate results
+		$html = paginate(
+				$q,
+				'{urlname}',
+				$num_pages,
+				makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageEditor&offset=%s", false),
+				$pg_start,
+				99,
+				array('urlname' => 'admin_pagemanager_format_listing'),
+				'<div class="tblholder" style="height: 300px; clip: rect(0px, auto, auto, 0px); overflow: auto;">
+				<table border="0" cellspacing="1" cellpadding="4">',
+				'  </table>
+ 				</div>'
+			);
+		echo $html;
+	}
+	
 }
 
 ?>
--- a/plugins/admin/PageGroups.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/PageGroups.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,1040 +13,1040 @@
 
 function page_Admin_PageGroups()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  if ( isset($_POST['action']) )
-  {
-    if ( isset($_POST['action']['create']) || isset($_POST['action']['create_stage2']) )
-    {
-      switch ( isset($_POST['action']['create_stage2']) )
-      {
-        case true:
-          if ( empty($_POST['pg_name']) || empty($_POST['group_type']) )
-          {
-            echo '<div class="error-box">' . $lang->get('acppg_err_need_name') . '</div>';
-            return;
-          }
-          if ( $_POST['group_type'] == PAGE_GRP_TAGGED && empty($_POST['member_tag']) )
-          {
-            echo '<div class="error-box">' . $lang->get('acppg_err_need_tag') . '</div>';
-            return;
-          }
-          if ( $_POST['group_type'] == PAGE_GRP_CATLINK && empty($_POST['member_cat']) )
-          {
-            echo '<div class="error-box">' . $lang->get('acppg_err_need_cat') . '</div>';
-            return;
-          }
-          if ( $_POST['group_type'] == PAGE_GRP_NORMAL && empty($_POST['member_page_0']) )
-          {
-            echo '<div class="error-box">' . $lang->get('acppg_err_need_page') . '</div>';
-            return;
-          }
-          if ( $_POST['group_type'] == PAGE_GRP_REGEX && empty($_POST['regex']) )
-          {
-            echo '<div class="error-box">' . $lang->get('acppg_err_need_regex') . '</div>';
-            return;
-          }
-          if ( $_POST['group_type'] != PAGE_GRP_TAGGED && $_POST['group_type'] != PAGE_GRP_CATLINK && $_POST['group_type'] != PAGE_GRP_NORMAL && $_POST['group_type'] != PAGE_GRP_REGEX )
-          {
-            echo '<div class="error-box">Umm, you sent an invalid group type. I\'d put a real error message here but this will only be shown if you try to hack the system.</div>';
-            return;
-          }
-          // All checks passed, create the group
-          switch($_POST['group_type'])
-          {
-            case PAGE_GRP_TAGGED:
-              $name = $db->escape($_POST['pg_name']);
-              $tag  = $db->escape($_POST['member_tag']);
-              $sql = 'INSERT INTO '.table_prefix.'page_groups(pg_type,pg_name,pg_target) VALUES(' . PAGE_GRP_TAGGED . ', \'' . $name . '\', \'' . $tag . '\');';
-              $q = $db->sql_query($sql);
-              if ( !$q )
-                $db->_die();
-              break;
-            case PAGE_GRP_CATLINK:
-              $name = $db->escape($_POST['pg_name']);
-              $cat  = $db->escape($_POST['member_cat']);
-              $sql = 'INSERT INTO '.table_prefix.'page_groups(pg_type,pg_name,pg_target) VALUES(' . PAGE_GRP_CATLINK . ', \'' . $name . '\', \'' . $cat . '\');';
-              $q = $db->sql_query($sql);
-              if ( !$q )
-                $db->_die();
-              break;
-            case PAGE_GRP_NORMAL:
-              $name = $db->escape($_POST['pg_name']);
-              $sql = 'INSERT INTO '.table_prefix.'page_groups(pg_type,pg_name) VALUES(' . PAGE_GRP_NORMAL . ', \'' . $name . '\');';
-              $q = $db->sql_query($sql);
-              if ( !$q )
-                $db->_die();
-              
-              $ins_id = $db->insert_id();
-              
-              // Page list
-              $keys = array_keys($_POST);
-              $arr_pages = array();
-              foreach ( $keys as $val )
-              {
-                if ( preg_match('/^member_page_([0-9]+?)$/', $val) && !empty($_POST[$val]) && isPage($_POST[$val]) )
-                {
-                  $arr_pages[] = $_POST[$val];
-                }
-              }
-              $arr_sql = array();
-              foreach ( $arr_pages as $page )
-              {
-                list($id, $ns) = RenderMan::strToPageID($page);
-                $id = sanitize_page_id($id);
-                $arr_sql[] = '(' . $ins_id . ',\'' . $db->escape($id) . '\', \'' . $ns . '\')';
-              }
-              $sql = 'INSERT INTO '.table_prefix.'page_group_members(pg_id,page_id,namespace) VALUES' . implode(',', $arr_sql) . ';';
-              $q = $db->sql_query($sql);
-              if ( !$q )
-                $db->_die();
-              break;
-            case PAGE_GRP_REGEX:
-              $name  = $db->escape($_POST['pg_name']);
-              $regex = $db->escape($_POST['regex']);
-              $sql = 'INSERT INTO '.table_prefix.'page_groups(pg_type,pg_name,pg_target) VALUES(' . PAGE_GRP_REGEX . ', \'' . $name . '\', \'' . $regex . '\');';
-              $q = $db->sql_query($sql);
-              if ( !$q )
-                $db->_die();
-              break;
-          }
-          echo '<div class="info-box">' . $lang->get('acppg_msg_create_success', array('group_name' => htmlspecialchars($_POST['pg_name']))) . '</div>';
-          break;
-      }
-      // A little Javascript magic
-      ?>
-      <script language="javascript" type="text/javascript">
-        function pg_create_typeset(selector)
-        {
-          var pg_normal  = <?php echo PAGE_GRP_NORMAL; ?>;
-          var pg_tagged  = <?php echo PAGE_GRP_TAGGED; ?>;
-          var pg_catlink = <?php echo PAGE_GRP_CATLINK; ?>;
-          var pg_regex   = <?php echo PAGE_GRP_REGEX; ?>;
-          var selection = false;
-          // Get selection
-          for ( var i = 0; i < selector.childNodes.length; i++ )
-          {
-            var child = selector.childNodes[i];
-            if ( !child || child.tagName != 'OPTION' )
-            {
-              continue;
-            }
-            if ( child.selected )
-            {
-              selection = child.value;
-            }
-          }
-          if ( !selection )
-          {
-            alert('Cannot get field value');
-            return true;
-          }
-          selection = parseInt(selection);
-          if ( selection != pg_normal && selection != pg_tagged && selection != pg_catlink && selection != pg_regex )
-          {
-            alert('Invalid field value');
-            return true;
-          }
-          
-          // We have the selection and it's validated; show the appropriate field group
-          
-          if ( selection == pg_normal )
-          {
-            document.getElementById('pg_create_title_catlink').style.display = 'none';
-            document.getElementById('pg_create_catlink_1').style.display = 'none';
-            document.getElementById('pg_create_catlink_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_tagged').style.display = 'none';
-            document.getElementById('pg_create_tagged_1').style.display = 'none';
-            document.getElementById('pg_create_tagged_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_normal').style.display = 'inline';
-            document.getElementById('pg_create_normal_1').style.display = 'block';
-            document.getElementById('pg_create_normal_2').style.display = 'block';
-            
-            document.getElementById('pg_create_title_regex').style.display = 'none';
-            document.getElementById('pg_create_regex_1').style.display = 'none';
-            document.getElementById('pg_create_regex_2').style.display = 'none';
-          }
-          else if ( selection == pg_catlink )
-          {
-            document.getElementById('pg_create_title_catlink').style.display = 'inline';
-            document.getElementById('pg_create_catlink_1').style.display = 'block';
-            document.getElementById('pg_create_catlink_2').style.display = 'block';
-            
-            document.getElementById('pg_create_title_tagged').style.display = 'none';
-            document.getElementById('pg_create_tagged_1').style.display = 'none';
-            document.getElementById('pg_create_tagged_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_normal').style.display = 'none';
-            document.getElementById('pg_create_normal_1').style.display = 'none';
-            document.getElementById('pg_create_normal_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_regex').style.display = 'none';
-            document.getElementById('pg_create_regex_1').style.display = 'none';
-            document.getElementById('pg_create_regex_2').style.display = 'none';
-          }
-          else if ( selection == pg_tagged )
-          {
-            document.getElementById('pg_create_title_catlink').style.display = 'none';
-            document.getElementById('pg_create_catlink_1').style.display = 'none';
-            document.getElementById('pg_create_catlink_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_tagged').style.display = 'inline';
-            document.getElementById('pg_create_tagged_1').style.display = 'block';
-            document.getElementById('pg_create_tagged_2').style.display = 'block';
-            
-            document.getElementById('pg_create_title_normal').style.display = 'none';
-            document.getElementById('pg_create_normal_1').style.display = 'none';
-            document.getElementById('pg_create_normal_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_regex').style.display = 'none';
-            document.getElementById('pg_create_regex_1').style.display = 'none';
-            document.getElementById('pg_create_regex_2').style.display = 'none';
-          }
-          else if ( selection == pg_regex )
-          {
-            document.getElementById('pg_create_title_catlink').style.display = 'none';
-            document.getElementById('pg_create_catlink_1').style.display = 'none';
-            document.getElementById('pg_create_catlink_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_tagged').style.display = 'none';
-            document.getElementById('pg_create_tagged_1').style.display = 'none';
-            document.getElementById('pg_create_tagged_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_normal').style.display = 'none';
-            document.getElementById('pg_create_normal_1').style.display = 'none';
-            document.getElementById('pg_create_normal_2').style.display = 'none';
-            
-            document.getElementById('pg_create_title_regex').style.display = 'inline';
-            document.getElementById('pg_create_regex_1').style.display = 'block';
-            document.getElementById('pg_create_regex_2').style.display = 'block';
-          }
-        
-        }
-        
-        // Set to pg_normal on page load
-        var pg_createform_init = function()
-        {
-          document.getElementById('pg_create_title_catlink').style.display = 'none';
-          document.getElementById('pg_create_catlink_1').style.display = 'none';
-          document.getElementById('pg_create_catlink_2').style.display = 'none';
-          
-          document.getElementById('pg_create_title_tagged').style.display = 'none';
-          document.getElementById('pg_create_tagged_1').style.display = 'none';
-          document.getElementById('pg_create_tagged_2').style.display = 'none';
-          
-          document.getElementById('pg_create_title_regex').style.display = 'none';
-          document.getElementById('pg_create_regex_1').style.display = 'none';
-          document.getElementById('pg_create_regex_2').style.display = 'none';
-          
-          document.getElementById('pg_create_title_normal').style.display = 'inline';
-          document.getElementById('pg_create_normal_1').style.display = 'block';
-          document.getElementById('pg_create_normal_2').style.display = 'block';
-        }
-        
-        function pg_create_more_fields()
-        {
-          var targettd = document.getElementById('pg_create_normal_2');
-          var id = 0;
-          for ( var i = 0; i < targettd.childNodes.length; i++ )
-          {
-            var child = targettd.childNodes[i];
-            if ( child.tagName == 'INPUT' )
-            {
-              if ( child.type == 'button' )
-              {
-                var newInp = document.createElement('input');
-                // <input type="text" name="member_page_1" id="pg_create_member_1" onkeyup="return ajaxPageNameComplete(this);" size="30" /><br />
-                newInp.type    = 'text';
-                newInp.name    = 'member_page_' + id;
-                newInp.id      = 'pg_create_member_' + id;
-                newInp.onkeyup = function(e) { return ajaxPageNameComplete(this); };
-                newInp.size    = '30';
-                newInp.style.marginTop = '3px';
-                targettd.insertBefore(newInp, child);
-                targettd.insertBefore(document.createElement('br'), child);
-                break;
-              }
-              else // if ( child.type == 'text' )
-              {
-                id++;
-              }
-            }
-          }
-        }
-        
-      </script>
-      <?php
-      
-      // Build category list
-      $q = $db->sql_query('SELECT name,urlname FROM '.table_prefix.'pages WHERE namespace=\'Category\';');
-      if ( !$q )
-        $db->_die();
-      
-      if ( $db->numrows() < 1 )
-      {
-        $catlist = $lang->get('acppg_err_no_cats');
-      }
-      else
-      {
-        $catlist = '<select name="member_cat">';
-        while ( $row = $db->fetchrow() )
-        {
-          $catlist .= '<option value="' . htmlspecialchars($row['urlname']) . '">' . htmlspecialchars($row['name']) . '</option>';
-        }
-        $catlist .= '</select>';
-      }
-      
-      echo '<script type="text/javascript">
-              var __pg_edit_submitAuthorized = true;
-            </script>';
-      
-      echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized || !__pg_edit_submitAuthorized) return false;" enctype="multipart/form-data">';
-      
-      echo '<div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">
-              <tr>
-              <th colspan="2">' . $lang->get('acppg_th_create') . '</th>
-              </tr>';
-      
-      // Name
-      echo '<tr>
-              <td class="row2">
-              ' . $lang->get('acppg_field_group_name') . '<br />
-              <small>' . $lang->get('acppg_field_group_name_hint') . '</small>
-              </td>
-              <td class="row1">
-              <input type="text" name="pg_name" size="30" />
-              </td>
-            </tr>';
-            
-      // Group type
-      echo '<tr>
-              <td class="row2">
-              ' . $lang->get('acppg_field_group_type') . '
-              </td>
-              <td class="row1">
-              <select name="group_type" onchange="pg_create_typeset(this);">
-                <option value="' . PAGE_GRP_NORMAL  . '" selected="selected">' . $lang->get('acppg_gtype_static') . '</option>
-                <option value="' . PAGE_GRP_TAGGED  . '">' . $lang->get('acppg_gtype_tagged') . '</option>
-                <option value="' . PAGE_GRP_CATLINK . '">' . $lang->get('acppg_gtype_catlink') . '</option>
-                <option value="' . PAGE_GRP_REGEX   . '">' . $lang->get('acppg_gtype_regex_long') . '</option>
-              </select>
-              </td>
-            </tr>';
-            
-      // Titles
-      echo '<tr>
-              <th colspan="2">
-                <span id="pg_create_title_normal">
-                  ' . $lang->get('acppg_gtype_static') . '
-                </span>
-                <span id="pg_create_title_tagged">
-                  ' . $lang->get('acppg_gtype_tagged') . '
-                </span>
-                <span id="pg_create_title_catlink">
-                  ' . $lang->get('acppg_gtype_catlink') . '
-                </span>
-                <span id="pg_create_title_regex">
-                  ' . $lang->get('acppg_gtype_regex') . '
-                </span>
-              </th>
-            </tr>';
-      
-      echo '<tr>
-              <td class="row2">
-                <div id="pg_create_normal_1">
-                  ' . $lang->get('acppg_field_member_pages') . '<br />
-                  <small>' . $lang->get('acppg_field_member_pages_hint') . '</small>
-                </div>
-                <div id="pg_create_catlink_1">
-                  ' . $lang->get('acppg_field_target_category') . '<br />
-                  <small>' . $lang->get('acppg_field_target_category_hint') . '</small>
-                </div>
-                <div id="pg_create_tagged_1">
-                  ' . $lang->get('acppg_field_target_tag') . '
-                </div>
-                <div id="pg_create_regex_1">
-                  ' . $lang->get('acppg_field_target_regex') . '<br />
-                  <small>' . $lang->get('acppg_field_target_regex_hint') . '</small>
-              </td>';
-            
-      echo '  <td class="row1">
-                <div id="pg_create_normal_2" />
-                  <input type="text" style="margin-top: 3px;" name="member_page_0" id="pg_create_member_0" class="autofill page" size="30" /><br />
-                  <input type="text" style="margin-top: 3px;" name="member_page_1" id="pg_create_member_1" class="autofill page" size="30" /><br />
-                  <input type="text" style="margin-top: 3px;" name="member_page_2" id="pg_create_member_2" class="autofill page" size="30" /><br />
-                  <input type="text" style="margin-top: 3px;" name="member_page_3" id="pg_create_member_3" class="autofill page" size="30" /><br />
-                  <input type="text" style="margin-top: 3px;" name="member_page_4" id="pg_create_member_4" class="autofill page" size="30" /><br />
-                  <input type="button" onclick="pg_create_more_fields(); return false;" style="margin-top: 5px;" value="&nbsp;&nbsp;+&nbsp;&nbsp;" />
-                </div>
-                <div id="pg_create_tagged_2">
-                  <input type="text" name="member_tag" size="30" />
-                </div>
-                <div id="pg_create_catlink_2">
-                  ' . $catlist . '
-                </div>
-                <div id="pg_create_regex_2">
-                  <input type="text" name="regex" size="60" /> 
-                </div>
-              </td>
-            </tr>';
-            
-      // Submit button
-      echo '<tr>
-              <th class="subhead" colspan="2"><input type="submit" name="action[create_stage2]" value="' . $lang->get('acppg_btn_create_finish') . '" style="font-weight: bold;" /> <input type="submit" name="action[noop]" value="' . $lang->get('etc_cancel') . '" style="font-weight: normal;" /></th>
-            </tr>';
-            
-      echo '</table>
-            </div>';
-      
-      echo '</form>';
-      
-      echo '<script type="text/javascript">pg_createform_init();</script>';
-      return;
-    }
-    else if ( isset($_POST['action']['del']) )
-    {
-      // Confirmation to delete a group (this is really only a stub)
-      
-      $delete_id = array_keys($_POST['action']['del']);
-      $delete_id = intval($delete_id[0]);
-      
-      if ( !empty($delete_id) )
-      {
-        echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">' . "\n";
-        echo '<input type="hidden" name="delete_id" value="' . $delete_id . '" />' . "\n";
-        echo '<div class="tblholder">' . "\n";
-        echo '  <table border="0" cellspacing="1" cellpadding="4">' . "\n";
-        echo '    <tr><th>' . $lang->get('acppg_th_delete_confirm') . '</th></tr>' . "\n";
-        echo '    <tr><td class="row2" style="text-align: center; padding: 20px 0;">' . $lang->get('acppg_msg_delete_confirm') . '</td></tr>' . "\n";
-        echo '    <tr><td class="row1" style="text-align: center;">' . "\n";
-        echo '        <input type="submit" name="action[del_confirm]" value="' . $lang->get('acppg_btn_delete_confirm') . '" style="font-weight: bold;" />' . "\n";
-        echo '        <input type="submit" name="action[noop]" value="' . $lang->get('etc_cancel') . '" style="font-weight: normal;" />' . "\n";
-        echo '        </td></tr>' . "\n";
-        echo '  </table>' . "\n";
-        echo '</form>' . "\n";
-        
-        return;
-      }
-    }
-    else if ( isset($_POST['action']['del_confirm']) )
-    {
-      $delete_id = intval($_POST['delete_id']);
-      if ( empty($delete_id) )
-      {
-        echo 'Hack attempt';
-        return;
-      }
-      // Obtain group name
-      $q = $db->sql_query('SELECT pg_name FROM '.table_prefix.'page_groups WHERE pg_id=' . $delete_id . ';');
-      if ( !$q )
-        $db->_die();
-      if ( $db->numrows() < 1 )
-      {
-        echo 'Page group dun exist.';
-        return;
-      }
-      $row = $db->fetchrow();
-      $db->free_result();
-      $pg_name = $row['pg_name'];
-      unset($row);
-      // Delete the group
-      $q = $db->sql_query('DELETE FROM '.table_prefix.'page_groups WHERE pg_id=' . $delete_id . ';');
-      if ( !$q )
-        $db->_die();
-      $q = $db->sql_query('DELETE FROM '.table_prefix.'page_group_members WHERE pg_id=' . $delete_id . ';');
-      if ( !$q )
-        $db->_die();
-      
-      $del_msg = $lang->get('acppg_msg_delete_success', array('pg_name' => htmlspecialchars($pg_name)));
-      echo "<div class=\"info-box\">$del_msg</div>";
-    }
-    else if ( isset($_POST['action']['edit']) && !isset($_POST['action']['noop']) )
-    {
-      if ( isset($_POST['action']['edit_save']) )
-      {
-      }
-     
-      if ( isset($_POST['action']['edit']['add_page']) && isset($_GET['src']) && $_GET['src'] == 'ajax' )
-      {
-        $return = array('successful' => false);
-        
-        //
-        // Add the specified page to the group
-        //
-        
-        // Get ID of the group
-        $edit_id = intval($_POST['pg_id']);
-        if ( !$edit_id )
-        {
-          $return = array('mode' => 'error', 'text' => 'Hack attempt');
-          echo enano_json_encode($return);
-          return;
-        }
-        
-        // Run some validation - check that page exists and that it's not already in the group
-        $page = $_POST['new_page'];
-        if ( empty($page) )
-        {
-          $return = array('mode' => 'error', 'text' => $lang->get('acppg_err_ajaxadd_need_title'));
-          echo enano_json_encode($return);
-          return;
-        }
-        
-        /*
-        // We're gonna allow adding nonexistent pages for now
-        if ( !isPage($page) )
-        {
-          $return = array('mode' => 'error', 'text' => 'The page you are trying to add (' . htmlspecialchars($page) . ') does not exist.');
-          echo enano_json_encode($return);
-          return;
-        }
-        */
-        
-        list($page_id, $namespace) = RenderMan::strToPageID($page);
-        $page_id = sanitize_page_id($page_id);
-        
-        if ( !isset($paths->namespace[$namespace]) )
-        {
-          $return = array('mode' => 'error', 'text' => 'Invalid namespace return from RenderMan::strToPageID()');
-          echo enano_json_encode($return);
-          return;
-        }
-        
-        $q = $db->sql_query('SELECT "x" FROM '.table_prefix.'page_group_members WHERE pg_id=' . $edit_id . ' AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $namespace . '\';');
-        if ( !$q )
-        {
-          $return = array('mode' => 'error', 'text' => $db->get_error());
-          echo enano_json_encode($return);
-          return;
-        }
-        if ( $db->numrows() > 0 )
-        {
-          $return = array('mode' => 'error', 'text' => $lang->get('acppg_err_ajaxadd_already_in'));
-          echo enano_json_encode($return);
-          return;
-        }
-        
-        $q = $db->sql_query('INSERT INTO '.table_prefix.'page_group_members(pg_id, page_id, namespace) VALUES(' . $edit_id . ', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\');');
-        if ( !$q )
-        {
-          $return = array('mode' => 'error', 'text' => $db->get_error());
-          echo enano_json_encode($return);
-          return;
-        }
-        
-        $title = "($namespace) " . get_page_title($paths->nslist[$namespace] . $page_id);
-        
-        $return = array('mode' => 'info', 'text' => $lang->get('acppg_ajaxadd_success'), 'successful' => true, 'title' => $title, 'member_id' => $db->insert_id());
-        
-        echo enano_json_encode($return);
-        return;
-      }
-      
-      if ( isset($_POST['action']['edit_save']) && isset($_POST['pg_name']) )
-      {
-        $edit_id = $_POST['action']['edit'];
-        $edit_id = intval($edit_id);
-        if ( !empty($edit_id) )
-        {
-          // Update group name
-          $new_name = $_POST['pg_name'];
-          if ( empty($new_name) )
-          {
-            echo '<div class="error-box">' . $lang->get('acppg_err_save_need_name') . '</div>';
-          }
-          else
-          {
-            $q = $db->sql_query('SELECT pg_name FROM '.table_prefix.'page_groups WHERE pg_id=' . $edit_id . ';');
-            if ( !$q )
-              $db->_die();
-            $row = $db->fetchrow();
-            $db->free_result();
-            if ( $new_name != $row['pg_name'] )
-            {
-              $new_name = $db->escape(trim($new_name));
-              $q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_name=\'' . $new_name . '\' WHERE pg_id=' . $edit_id . ';');
-              if ( !$q )
-                $db->_die();
-              else
-                echo '<div class="info-box">' . $lang->get('acppg_msg_save_name_updated') . '</div>';
-            }
-            if ( $_POST['pg_type'] == PAGE_GRP_TAGGED )
-            {
-              $target = $_POST['pg_target'];
-              $target = sanitize_tag($target);
-              if ( empty($target) )
-              {
-                echo '<div class="error-box">' . $lang->get('acppg_err_save_need_tag') . '</div>';
-              }
-              else
-              {
-                $target = $db->escape($target);
-                $q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_target=\'' . $target . '\' WHERE pg_id=' . $edit_id . ';');
-                if ( !$q )
-                  $db->_die();
-                else
-                  echo '<div class="info-box">' . $lang->get('acppg_msg_save_tag_updated') . '</div>';
-              }
-            }
-            else if ( $_POST['pg_type'] == PAGE_GRP_REGEX )
-            {
-              $target = $_POST['pg_target'];
-              if ( empty($target) )
-              {
-                echo '<div class="error-box">' . $lang->get('acppg_err_save_need_regex') . '</div>';
-              }
-              else
-              {
-                $target = $db->escape($target);
-                $q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_target=\'' . $target . '\' WHERE pg_id=' . $edit_id . ';');
-                if ( !$q )
-                  $db->_die();
-                else
-                  echo '<div class="info-box">' . $lang->get('acppg_msg_save_regex_updated') . '</div>';
-              }
-            }
-            else if ( $_POST['pg_type'] == PAGE_GRP_CATLINK )
-            {
-              $target = $_POST['pg_target'];
-              if ( empty($target) )
-              {
-                echo '<div class="error-box">' . $lang->get('acppg_err_save_bad_category') . '</div>';
-              }
-              else
-              {
-                $target = $db->escape($target);
-                $q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_target=\'' . $target . '\' WHERE pg_id=' . $edit_id . ';');
-                if ( !$q )
-                  $db->_die();
-                else
-                  echo '<div class="info-box">' . $lang->get('acppg_msg_save_cat_updated') . '</div>';
-              }
-            }
-          }
-        }
-      }
-      else if ( isset($_POST['action']['edit_save']) )
-      {
-        $edit_id = $_POST['action']['edit'];
-        $edit_id = intval($edit_id);
-      }
-      else
-      {
-        $edit_id = array_keys($_POST['action']['edit']);
-        $edit_id = intval($edit_id[0]);
-      }
-      
-      if ( empty($edit_id) )
-      {
-        echo 'Hack attempt';
-        return;
-      }
-      
-      if ( isset($_POST['action']['edit_save']['do_rm']) && !isset($_POST['pg_name']) )
-      {
-        $vals = array_keys($_POST['action']['edit_save']['rm']);
-        $good = array();
-        foreach ( $vals as $id )
-        {
-          if ( strval(intval($id)) == $id )
-            $good[] = $id;
-        }
-        $subquery = ( count($good) > 0 ) ? 'pg_member_id=' . implode(' OR pg_member_id=', $good) : "'foo'='bar'";
-        if ( $subquery == "'foo'='bar'" )
-        {
-          echo '<div class="warning-box">' . $lang->get('acppg_err_save_no_pages') . '</div>';
-        }
-        else
-        {
-          $sql = 'DELETE FROM '.table_prefix."page_group_members WHERE ( $subquery ) AND pg_id=$edit_id;";
-          if ( !$db->sql_query($sql) )
-          {
-            $db->_die();
-          }
-          echo '<div class="info-box">' . $lang->get('acppg_msg_save_pages_deleted') . '</div>';
-        }
-      }
-      
-      // Fetch information about page group
-      $q = $db->sql_query('SELECT pg_name, pg_type, pg_target FROM '.table_prefix.'page_groups WHERE pg_id=' . $edit_id . ';');
-      if ( !$q )
-        $db->_die();
-      
-      if ( $db->numrows() < 1 )
-      {
-        echo 'Bad request - can\'t load page group from database.';
-        return;
-      }
-      
-      $row = $db->fetchrow();
-      $db->free_result();
-      
-      echo '<form name="pg_edit_frm" action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-      echo '<input type="hidden" name="action[edit]" value="' . $edit_id . '" />';
-      echo '<input type="hidden" name="pg_type" value="' . $row['pg_type'] . '" />';
-      echo '<div class="tblholder">
-              <table border="0" cellspacing="1" cellpadding="4">
-                <tr>
-                  <th colspan="3">' . $lang->get('acppg_th_editing_group') . ' ' . htmlspecialchars($row['pg_name']) . '</th>
-                </tr>';
-      // Group name
-      
-      echo '    <tr>
-                  <td class="row2">' . $lang->get('acppg_field_group_name') . '</td>
-                  <td class="row1" colspan="2"><input type="text" name="pg_name" value="' . htmlspecialchars($row['pg_name']) . '" size="30" /></td>
-                </tr>';
-      
-      $ajax_page_add = false;
-                
-      // This is where the going gets tricky.
-      // For static groups, we need to have each page listed out with a removal button, and a form to add new pages.
-      // For category links, we need a select box with each category in it, and
-      // For tag sets, just a text box to enter a new tag.
-      
-      // You can guess which one I dreaded.
-      
-      switch ( $row['pg_type'] )
-      {
-        case PAGE_GRP_NORMAL:
-          
-          // You have guessed correct.
-          // *Sits in chair for 10 minutes listening to the radio in an effort to put off writing the code you see below*
-          
-          echo '<tr><th colspan="3" class="subhead"><input type="submit" name="action[edit_save]" value="' . $lang->get('acppg_btn_save_name') . '" /></th></tr>';
-          echo '</table></div>';
-          echo '</form>';
-          echo '<form name="pg_static_rm_frm" action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" enctype="multipart/form-data">';
-          echo '<input type="hidden" name="action[edit]" value="' . $edit_id . '" />';
-          echo '<div class="tblholder">
-                  <table border="0" cellspacing="1" cellpadding="4">
-                    <tr>
-                      <th colspan="3">' . $lang->get('acppg_th_remove_selected') . '</th>
-                    </tr>';
-          
-          $q = $db->sql_query('SELECT m.pg_member_id,m.page_id,m.namespace FROM '.table_prefix.'page_group_members AS m
-                                 LEFT JOIN '.table_prefix.'pages AS p
-                                   ON ( p.urlname = m.page_id AND p.namespace = m.namespace )
-                                 WHERE m.pg_id=' . $edit_id . ';');
-          
-          if ( !$q )
-            $db->_die();
-          
-          $delim = ceil( $db->numrows($q) / 2 );
-          if ( $delim < 5 )
-          {
-            $delim = 0xFFFFFFFE;
-            // stupid hack. I'm XSSing my own code.
-            $colspan = '2" id="pg_edit_tackon2me';
-          }
-          else
-          {
-            $colspan = "1";
-          }
-          
-          echo '<tr><td class="row2" rowspan="2">' . $lang->get('acppg_field_remove') . '</td><td class="row1" colspan="' . $colspan . '">';
-          $i = 0;
-          
-          while ( $row = $db->fetchrow($q) )
-          {
-            $i++;
-            if ( $i == $delim )
-            {
-              echo '</td><td class="row1" id="pg_edit_tackon2me">';
-            }
-            $page_name = '(' . $row['namespace'] . ') ' . get_page_title($paths->nslist[$row['namespace']] . $row['page_id']);
-            echo '<label><input type="checkbox" name="action[edit_save][rm][' . $row['pg_member_id'] . ']" /> ' . htmlspecialchars($page_name) . '</label><br />';
-          }
-          
-          echo '</td></tr>';
-          echo '<tr><th colspan="2" class="subhead" style="width: 70%;"><input type="submit" name="action[edit_save][do_rm]" value="' . $lang->get('acppg_btn_do_remove') . '" /></th></tr>';
-          
-          // More javascript magic!
-          ?>
-          <script type="text/javascript">
-            var __pg_edit_submitAuthorized = true;
-            var __ol_pg_edit_setup = function()
-            {
-              var input = document.getElementById('inptext_pg_add_member');
-              input.onkeypress = function(e) {
-                  if ( e.keyCode == 13 )
-                  {
-                    setTimeout('__pg_edit_ajaxadd(document.getElementById(\'' + this.id + '\'));', 500);
-                  } 
-                };
-            }
-            addOnloadHook(__ol_pg_edit_setup);
-            var __pg_edit_objcache = false;
-            function __pg_edit_ajaxadd(obj)
-            {
-              if ( __pg_edit_objcache )
-                return false;
-              __pg_edit_objcache = obj;
-              
-              if ( obj.nextSibling )
-              {
-                if ( obj.nextSibling.tagName == 'DIV' )
-                {
-                  obj.parentNode.removeChild(obj.nextSibling);
-                }
-              }
-              
-              // set width on parent, to prevent wrapping of ajax loading image
-              var w = $dynano(obj).Width();
-              w = w + 24;
-              obj.parentNode.style.width = w + 'px';
-              
-              // append the ajaxy loading image
-              var img = document.createElement('img');
-              img.src = scriptPath + '/images/loading.gif';
-              img.style.marginLeft = '4px';
-              insertAfter(obj.parentNode, img, obj);
-              
-              var url = makeUrlNS('Admin', 'PageGroups', 'src=ajax');
-              var page_add = escape(obj.value);
-              var pg_id = document.forms.pg_edit_frm['action[edit]'].value;
-              
-              ajaxPost(url, 'action[edit][add_page]=&pg_id=' + pg_id + '&new_page=' + page_add, function()
-                {
-                  if ( ajax.readyState == 4 )
-                  {
-                    var obj = __pg_edit_objcache;
-                    __pg_edit_objcache = false;
-                    
-                    // kill the loading graphic
-                    obj.parentNode.removeChild(obj.nextSibling);
-                    
-                    var resptext = String(ajax.responseText + '');
-                    if ( resptext.substr(0, 1) != '{' )
-                    {
-                      // This ain't JSON baby.
-                      alert('Invalid JSON response:\n' + resptext);
-                      return false;
-                    }
-                    var json = parseJSON(resptext);
-                    
-                    var div = document.createElement('div');
-                    if ( json.mode == 'info' )
-                    {
-                      div.className = 'info-box-mini';
-                    }
-                    else if ( json.mode == 'error' )
-                    {
-                      div.className = 'error-box-mini';
-                    }
-                    div.appendChild(document.createTextNode(json.text));
-                    insertAfter(obj.parentNode, div, obj);
-                    
-                    if ( json.successful )
-                    {
-                      var td = document.getElementById('pg_edit_tackon2me');
-                      var lbl = document.createElement('label');
-                      var check = document.createElement('input');
-                      check.type = 'checkbox';
-                      check.name = 'action[edit_save][rm][' + json.member_id + ']';
-                      lbl.appendChild(check);
-                      lbl.appendChild(document.createTextNode(' ' + json.title));
-                      td.appendChild(lbl);
-                      td.appendChild(document.createElement('br'));
-                    }
-                    
-                  }
-                });
-            }
-          </script>
-          <?php
-          
-          $ajax_page_add = true;
-          
-          break;
-        case PAGE_GRP_TAGGED:
-          echo '<tr>
-                  <td class="row2">
-                    ' . $lang->get('acppg_field_target_tag') . '
-                  </td>
-                  <td class="row1">
-                    <input type="text" name="pg_target" value="' . htmlspecialchars($row['pg_target']) . '" size="30" />
-                  </td>
-                </tr>';
-          break;
-        case PAGE_GRP_REGEX:
-          echo '<tr>
-                  <td class="row2">
-                    ' . $lang->get('acppg_field_target_regex') . '<br />
-                    <small>' . $lang->get('acppg_field_target_regex_hint') . '</small>
-                  </td>
-                  <td class="row1">
-                    <input type="text" name="pg_target" value="' . htmlspecialchars($row['pg_target']) . '" size="30" />
-                  </td>
-                </tr>';
-          break;
-        case PAGE_GRP_CATLINK:
-          
-          // Build category list
-          $q = $db->sql_query('SELECT name,urlname FROM '.table_prefix.'pages WHERE namespace=\'Category\';');
-          if ( !$q )
-            $db->_die();
-          
-          if ( $db->numrows() < 1 )
-          {
-            $catlist = 'There aren\'t any categories on this site.';
-          }
-          else
-          {
-            $catlist = '<select name="pg_target">';
-            while ( $catrow = $db->fetchrow() )
-            {
-              $selected = ( $catrow['urlname'] == $row['pg_target'] ) ? ' selected="selected"' : '';
-              $catlist .= '<option value="' . htmlspecialchars($catrow['urlname']) . '"' . $selected . '>' . htmlspecialchars($catrow['name']) . '</option>';
-            }
-            $catlist .= '</select>';
-          }
-          
-          echo '<tr>
-                  <td class="row2">
-                    ' . $lang->get('acppg_field_target_category') . '<br />
-                    <small>' . $lang->get('acppg_field_target_category_hint2') . '</small>
-                  </td>
-                  <td class="row1">
-                    ' . $catlist . '
-                  </td>
-                </tr>';
-          
-          break;
-      }
-      
-      if ( $ajax_page_add )
-      {
-        echo '<tr><th colspan="3"><input type="submit" name="action[noop]" value="' . $lang->get('acppg_btn_cancel_all') . '" /></th></tr>';
-      }
-      else
-      {
-        echo '<tr><th colspan="3" class="subhead">
-                <input type="submit" name="action[edit_save]" value="' . $lang->get('acppg_btn_save_update') . '" />
-                <input type="submit" name="action[noop]" value="' . $lang->get('acppg_btn_cancel_all') . '" />
-              </th></tr>';
-      }
-      
-      echo '  </table>
-            </div>';
-      echo '</form>';
-      
-      if ( $ajax_page_add )
-      {
-        // This needs to be outside of the form.
-        echo '<div class="tblholder"><table border="0" cellspacing="1" cellpadding="4"><tr>';
-        echo '<th colspan="2">' . $lang->get('acppg_th_onthefly') . '</th></tr>';
-        echo '<tr>';
-        // Add pages AJAX form
-        echo '<td class="row2">' . $lang->get('acppg_field_add_page') . '<br /><small>' . $lang->get('acppg_field_add_page_hint') . '</small></td>';
-        echo '<td class="row1"><input type="text" size="30" name="pg_add_member" id="inptext_pg_add_member" class="autofill page" /></td>';
-        echo '</tr></table></div>';
-      }
-      
-      return;
-    }
-    else if ( isset($_POST['action']['noop']) )
-    {
-      // Do nothing - skip to main form (noop is usually invoked by a cancel button in a form above)
-    }
-    else
-    {
-      echo '<div class="error-box">Invalid format of $_POST[action].</div>';
-    }
-  }
-  // No action defined - show default menu
-  
-  echo '<h2>' . $lang->get('acppg_heading_main') . '</h2>';
-  echo '<p>' . $lang->get('acppg_hint_intro') . '</p>';
-  
-  $q = $db->sql_query('SELECT pg_id, pg_type, pg_name, pg_target FROM '.table_prefix.'page_groups;');
-  if ( !$q )
-    $db->_die();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	if ( isset($_POST['action']) )
+	{
+		if ( isset($_POST['action']['create']) || isset($_POST['action']['create_stage2']) )
+		{
+			switch ( isset($_POST['action']['create_stage2']) )
+			{
+				case true:
+					if ( empty($_POST['pg_name']) || empty($_POST['group_type']) )
+					{
+						echo '<div class="error-box">' . $lang->get('acppg_err_need_name') . '</div>';
+						return;
+					}
+					if ( $_POST['group_type'] == PAGE_GRP_TAGGED && empty($_POST['member_tag']) )
+					{
+						echo '<div class="error-box">' . $lang->get('acppg_err_need_tag') . '</div>';
+						return;
+					}
+					if ( $_POST['group_type'] == PAGE_GRP_CATLINK && empty($_POST['member_cat']) )
+					{
+						echo '<div class="error-box">' . $lang->get('acppg_err_need_cat') . '</div>';
+						return;
+					}
+					if ( $_POST['group_type'] == PAGE_GRP_NORMAL && empty($_POST['member_page_0']) )
+					{
+						echo '<div class="error-box">' . $lang->get('acppg_err_need_page') . '</div>';
+						return;
+					}
+					if ( $_POST['group_type'] == PAGE_GRP_REGEX && empty($_POST['regex']) )
+					{
+						echo '<div class="error-box">' . $lang->get('acppg_err_need_regex') . '</div>';
+						return;
+					}
+					if ( $_POST['group_type'] != PAGE_GRP_TAGGED && $_POST['group_type'] != PAGE_GRP_CATLINK && $_POST['group_type'] != PAGE_GRP_NORMAL && $_POST['group_type'] != PAGE_GRP_REGEX )
+					{
+						echo '<div class="error-box">Umm, you sent an invalid group type. I\'d put a real error message here but this will only be shown if you try to hack the system.</div>';
+						return;
+					}
+					// All checks passed, create the group
+					switch($_POST['group_type'])
+					{
+						case PAGE_GRP_TAGGED:
+							$name = $db->escape($_POST['pg_name']);
+							$tag  = $db->escape($_POST['member_tag']);
+							$sql = 'INSERT INTO '.table_prefix.'page_groups(pg_type,pg_name,pg_target) VALUES(' . PAGE_GRP_TAGGED . ', \'' . $name . '\', \'' . $tag . '\');';
+							$q = $db->sql_query($sql);
+							if ( !$q )
+								$db->_die();
+							break;
+						case PAGE_GRP_CATLINK:
+							$name = $db->escape($_POST['pg_name']);
+							$cat  = $db->escape($_POST['member_cat']);
+							$sql = 'INSERT INTO '.table_prefix.'page_groups(pg_type,pg_name,pg_target) VALUES(' . PAGE_GRP_CATLINK . ', \'' . $name . '\', \'' . $cat . '\');';
+							$q = $db->sql_query($sql);
+							if ( !$q )
+								$db->_die();
+							break;
+						case PAGE_GRP_NORMAL:
+							$name = $db->escape($_POST['pg_name']);
+							$sql = 'INSERT INTO '.table_prefix.'page_groups(pg_type,pg_name) VALUES(' . PAGE_GRP_NORMAL . ', \'' . $name . '\');';
+							$q = $db->sql_query($sql);
+							if ( !$q )
+								$db->_die();
+							
+							$ins_id = $db->insert_id();
+							
+							// Page list
+							$keys = array_keys($_POST);
+							$arr_pages = array();
+							foreach ( $keys as $val )
+							{
+								if ( preg_match('/^member_page_([0-9]+?)$/', $val) && !empty($_POST[$val]) && isPage($_POST[$val]) )
+								{
+									$arr_pages[] = $_POST[$val];
+								}
+							}
+							$arr_sql = array();
+							foreach ( $arr_pages as $page )
+							{
+								list($id, $ns) = RenderMan::strToPageID($page);
+								$id = sanitize_page_id($id);
+								$arr_sql[] = '(' . $ins_id . ',\'' . $db->escape($id) . '\', \'' . $ns . '\')';
+							}
+							$sql = 'INSERT INTO '.table_prefix.'page_group_members(pg_id,page_id,namespace) VALUES' . implode(',', $arr_sql) . ';';
+							$q = $db->sql_query($sql);
+							if ( !$q )
+								$db->_die();
+							break;
+						case PAGE_GRP_REGEX:
+							$name  = $db->escape($_POST['pg_name']);
+							$regex = $db->escape($_POST['regex']);
+							$sql = 'INSERT INTO '.table_prefix.'page_groups(pg_type,pg_name,pg_target) VALUES(' . PAGE_GRP_REGEX . ', \'' . $name . '\', \'' . $regex . '\');';
+							$q = $db->sql_query($sql);
+							if ( !$q )
+								$db->_die();
+							break;
+					}
+					echo '<div class="info-box">' . $lang->get('acppg_msg_create_success', array('group_name' => htmlspecialchars($_POST['pg_name']))) . '</div>';
+					break;
+			}
+			// A little Javascript magic
+			?>
+			<script language="javascript" type="text/javascript">
+				function pg_create_typeset(selector)
+				{
+					var pg_normal  = <?php echo PAGE_GRP_NORMAL; ?>;
+					var pg_tagged  = <?php echo PAGE_GRP_TAGGED; ?>;
+					var pg_catlink = <?php echo PAGE_GRP_CATLINK; ?>;
+					var pg_regex   = <?php echo PAGE_GRP_REGEX; ?>;
+					var selection = false;
+					// Get selection
+					for ( var i = 0; i < selector.childNodes.length; i++ )
+					{
+						var child = selector.childNodes[i];
+						if ( !child || child.tagName != 'OPTION' )
+						{
+							continue;
+						}
+						if ( child.selected )
+						{
+							selection = child.value;
+						}
+					}
+					if ( !selection )
+					{
+						alert('Cannot get field value');
+						return true;
+					}
+					selection = parseInt(selection);
+					if ( selection != pg_normal && selection != pg_tagged && selection != pg_catlink && selection != pg_regex )
+					{
+						alert('Invalid field value');
+						return true;
+					}
+					
+					// We have the selection and it's validated; show the appropriate field group
+					
+					if ( selection == pg_normal )
+					{
+						document.getElementById('pg_create_title_catlink').style.display = 'none';
+						document.getElementById('pg_create_catlink_1').style.display = 'none';
+						document.getElementById('pg_create_catlink_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_tagged').style.display = 'none';
+						document.getElementById('pg_create_tagged_1').style.display = 'none';
+						document.getElementById('pg_create_tagged_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_normal').style.display = 'inline';
+						document.getElementById('pg_create_normal_1').style.display = 'block';
+						document.getElementById('pg_create_normal_2').style.display = 'block';
+						
+						document.getElementById('pg_create_title_regex').style.display = 'none';
+						document.getElementById('pg_create_regex_1').style.display = 'none';
+						document.getElementById('pg_create_regex_2').style.display = 'none';
+					}
+					else if ( selection == pg_catlink )
+					{
+						document.getElementById('pg_create_title_catlink').style.display = 'inline';
+						document.getElementById('pg_create_catlink_1').style.display = 'block';
+						document.getElementById('pg_create_catlink_2').style.display = 'block';
+						
+						document.getElementById('pg_create_title_tagged').style.display = 'none';
+						document.getElementById('pg_create_tagged_1').style.display = 'none';
+						document.getElementById('pg_create_tagged_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_normal').style.display = 'none';
+						document.getElementById('pg_create_normal_1').style.display = 'none';
+						document.getElementById('pg_create_normal_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_regex').style.display = 'none';
+						document.getElementById('pg_create_regex_1').style.display = 'none';
+						document.getElementById('pg_create_regex_2').style.display = 'none';
+					}
+					else if ( selection == pg_tagged )
+					{
+						document.getElementById('pg_create_title_catlink').style.display = 'none';
+						document.getElementById('pg_create_catlink_1').style.display = 'none';
+						document.getElementById('pg_create_catlink_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_tagged').style.display = 'inline';
+						document.getElementById('pg_create_tagged_1').style.display = 'block';
+						document.getElementById('pg_create_tagged_2').style.display = 'block';
+						
+						document.getElementById('pg_create_title_normal').style.display = 'none';
+						document.getElementById('pg_create_normal_1').style.display = 'none';
+						document.getElementById('pg_create_normal_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_regex').style.display = 'none';
+						document.getElementById('pg_create_regex_1').style.display = 'none';
+						document.getElementById('pg_create_regex_2').style.display = 'none';
+					}
+					else if ( selection == pg_regex )
+					{
+						document.getElementById('pg_create_title_catlink').style.display = 'none';
+						document.getElementById('pg_create_catlink_1').style.display = 'none';
+						document.getElementById('pg_create_catlink_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_tagged').style.display = 'none';
+						document.getElementById('pg_create_tagged_1').style.display = 'none';
+						document.getElementById('pg_create_tagged_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_normal').style.display = 'none';
+						document.getElementById('pg_create_normal_1').style.display = 'none';
+						document.getElementById('pg_create_normal_2').style.display = 'none';
+						
+						document.getElementById('pg_create_title_regex').style.display = 'inline';
+						document.getElementById('pg_create_regex_1').style.display = 'block';
+						document.getElementById('pg_create_regex_2').style.display = 'block';
+					}
+				
+				}
+				
+				// Set to pg_normal on page load
+				var pg_createform_init = function()
+				{
+					document.getElementById('pg_create_title_catlink').style.display = 'none';
+					document.getElementById('pg_create_catlink_1').style.display = 'none';
+					document.getElementById('pg_create_catlink_2').style.display = 'none';
+					
+					document.getElementById('pg_create_title_tagged').style.display = 'none';
+					document.getElementById('pg_create_tagged_1').style.display = 'none';
+					document.getElementById('pg_create_tagged_2').style.display = 'none';
+					
+					document.getElementById('pg_create_title_regex').style.display = 'none';
+					document.getElementById('pg_create_regex_1').style.display = 'none';
+					document.getElementById('pg_create_regex_2').style.display = 'none';
+					
+					document.getElementById('pg_create_title_normal').style.display = 'inline';
+					document.getElementById('pg_create_normal_1').style.display = 'block';
+					document.getElementById('pg_create_normal_2').style.display = 'block';
+				}
+				
+				function pg_create_more_fields()
+				{
+					var targettd = document.getElementById('pg_create_normal_2');
+					var id = 0;
+					for ( var i = 0; i < targettd.childNodes.length; i++ )
+					{
+						var child = targettd.childNodes[i];
+						if ( child.tagName == 'INPUT' )
+						{
+							if ( child.type == 'button' )
+							{
+								var newInp = document.createElement('input');
+								// <input type="text" name="member_page_1" id="pg_create_member_1" onkeyup="return ajaxPageNameComplete(this);" size="30" /><br />
+								newInp.type    = 'text';
+								newInp.name    = 'member_page_' + id;
+								newInp.id      = 'pg_create_member_' + id;
+								newInp.onkeyup = function(e) { return ajaxPageNameComplete(this); };
+								newInp.size    = '30';
+								newInp.style.marginTop = '3px';
+								targettd.insertBefore(newInp, child);
+								targettd.insertBefore(document.createElement('br'), child);
+								break;
+							}
+							else // if ( child.type == 'text' )
+							{
+								id++;
+							}
+						}
+					}
+				}
+				
+			</script>
+			<?php
+			
+			// Build category list
+			$q = $db->sql_query('SELECT name,urlname FROM '.table_prefix.'pages WHERE namespace=\'Category\';');
+			if ( !$q )
+				$db->_die();
+			
+			if ( $db->numrows() < 1 )
+			{
+				$catlist = $lang->get('acppg_err_no_cats');
+			}
+			else
+			{
+				$catlist = '<select name="member_cat">';
+				while ( $row = $db->fetchrow() )
+				{
+					$catlist .= '<option value="' . htmlspecialchars($row['urlname']) . '">' . htmlspecialchars($row['name']) . '</option>';
+				}
+				$catlist .= '</select>';
+			}
+			
+			echo '<script type="text/javascript">
+							var __pg_edit_submitAuthorized = true;
+						</script>';
+			
+			echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized || !__pg_edit_submitAuthorized) return false;" enctype="multipart/form-data">';
+			
+			echo '<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">
+							<tr>
+							<th colspan="2">' . $lang->get('acppg_th_create') . '</th>
+							</tr>';
+			
+			// Name
+			echo '<tr>
+							<td class="row2">
+							' . $lang->get('acppg_field_group_name') . '<br />
+							<small>' . $lang->get('acppg_field_group_name_hint') . '</small>
+							</td>
+							<td class="row1">
+							<input type="text" name="pg_name" size="30" />
+							</td>
+						</tr>';
+						
+			// Group type
+			echo '<tr>
+							<td class="row2">
+							' . $lang->get('acppg_field_group_type') . '
+							</td>
+							<td class="row1">
+							<select name="group_type" onchange="pg_create_typeset(this);">
+								<option value="' . PAGE_GRP_NORMAL  . '" selected="selected">' . $lang->get('acppg_gtype_static') . '</option>
+								<option value="' . PAGE_GRP_TAGGED  . '">' . $lang->get('acppg_gtype_tagged') . '</option>
+								<option value="' . PAGE_GRP_CATLINK . '">' . $lang->get('acppg_gtype_catlink') . '</option>
+								<option value="' . PAGE_GRP_REGEX   . '">' . $lang->get('acppg_gtype_regex_long') . '</option>
+							</select>
+							</td>
+						</tr>';
+						
+			// Titles
+			echo '<tr>
+							<th colspan="2">
+								<span id="pg_create_title_normal">
+									' . $lang->get('acppg_gtype_static') . '
+								</span>
+								<span id="pg_create_title_tagged">
+									' . $lang->get('acppg_gtype_tagged') . '
+								</span>
+								<span id="pg_create_title_catlink">
+									' . $lang->get('acppg_gtype_catlink') . '
+								</span>
+								<span id="pg_create_title_regex">
+									' . $lang->get('acppg_gtype_regex') . '
+								</span>
+							</th>
+						</tr>';
+			
+			echo '<tr>
+							<td class="row2">
+								<div id="pg_create_normal_1">
+									' . $lang->get('acppg_field_member_pages') . '<br />
+									<small>' . $lang->get('acppg_field_member_pages_hint') . '</small>
+								</div>
+								<div id="pg_create_catlink_1">
+									' . $lang->get('acppg_field_target_category') . '<br />
+									<small>' . $lang->get('acppg_field_target_category_hint') . '</small>
+								</div>
+								<div id="pg_create_tagged_1">
+									' . $lang->get('acppg_field_target_tag') . '
+								</div>
+								<div id="pg_create_regex_1">
+									' . $lang->get('acppg_field_target_regex') . '<br />
+									<small>' . $lang->get('acppg_field_target_regex_hint') . '</small>
+							</td>';
+						
+			echo '  <td class="row1">
+								<div id="pg_create_normal_2" />
+									<input type="text" style="margin-top: 3px;" name="member_page_0" id="pg_create_member_0" class="autofill page" size="30" /><br />
+									<input type="text" style="margin-top: 3px;" name="member_page_1" id="pg_create_member_1" class="autofill page" size="30" /><br />
+									<input type="text" style="margin-top: 3px;" name="member_page_2" id="pg_create_member_2" class="autofill page" size="30" /><br />
+									<input type="text" style="margin-top: 3px;" name="member_page_3" id="pg_create_member_3" class="autofill page" size="30" /><br />
+									<input type="text" style="margin-top: 3px;" name="member_page_4" id="pg_create_member_4" class="autofill page" size="30" /><br />
+									<input type="button" onclick="pg_create_more_fields(); return false;" style="margin-top: 5px;" value="&nbsp;&nbsp;+&nbsp;&nbsp;" />
+								</div>
+								<div id="pg_create_tagged_2">
+									<input type="text" name="member_tag" size="30" />
+								</div>
+								<div id="pg_create_catlink_2">
+									' . $catlist . '
+								</div>
+								<div id="pg_create_regex_2">
+									<input type="text" name="regex" size="60" /> 
+								</div>
+							</td>
+						</tr>';
+						
+			// Submit button
+			echo '<tr>
+							<th class="subhead" colspan="2"><input type="submit" name="action[create_stage2]" value="' . $lang->get('acppg_btn_create_finish') . '" style="font-weight: bold;" /> <input type="submit" name="action[noop]" value="' . $lang->get('etc_cancel') . '" style="font-weight: normal;" /></th>
+						</tr>';
+						
+			echo '</table>
+						</div>';
+			
+			echo '</form>';
+			
+			echo '<script type="text/javascript">pg_createform_init();</script>';
+			return;
+		}
+		else if ( isset($_POST['action']['del']) )
+		{
+			// Confirmation to delete a group (this is really only a stub)
+			
+			$delete_id = array_keys($_POST['action']['del']);
+			$delete_id = intval($delete_id[0]);
+			
+			if ( !empty($delete_id) )
+			{
+				echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">' . "\n";
+				echo '<input type="hidden" name="delete_id" value="' . $delete_id . '" />' . "\n";
+				echo '<div class="tblholder">' . "\n";
+				echo '  <table border="0" cellspacing="1" cellpadding="4">' . "\n";
+				echo '    <tr><th>' . $lang->get('acppg_th_delete_confirm') . '</th></tr>' . "\n";
+				echo '    <tr><td class="row2" style="text-align: center; padding: 20px 0;">' . $lang->get('acppg_msg_delete_confirm') . '</td></tr>' . "\n";
+				echo '    <tr><td class="row1" style="text-align: center;">' . "\n";
+				echo '        <input type="submit" name="action[del_confirm]" value="' . $lang->get('acppg_btn_delete_confirm') . '" style="font-weight: bold;" />' . "\n";
+				echo '        <input type="submit" name="action[noop]" value="' . $lang->get('etc_cancel') . '" style="font-weight: normal;" />' . "\n";
+				echo '        </td></tr>' . "\n";
+				echo '  </table>' . "\n";
+				echo '</form>' . "\n";
+				
+				return;
+			}
+		}
+		else if ( isset($_POST['action']['del_confirm']) )
+		{
+			$delete_id = intval($_POST['delete_id']);
+			if ( empty($delete_id) )
+			{
+				echo 'Hack attempt';
+				return;
+			}
+			// Obtain group name
+			$q = $db->sql_query('SELECT pg_name FROM '.table_prefix.'page_groups WHERE pg_id=' . $delete_id . ';');
+			if ( !$q )
+				$db->_die();
+			if ( $db->numrows() < 1 )
+			{
+				echo 'Page group dun exist.';
+				return;
+			}
+			$row = $db->fetchrow();
+			$db->free_result();
+			$pg_name = $row['pg_name'];
+			unset($row);
+			// Delete the group
+			$q = $db->sql_query('DELETE FROM '.table_prefix.'page_groups WHERE pg_id=' . $delete_id . ';');
+			if ( !$q )
+				$db->_die();
+			$q = $db->sql_query('DELETE FROM '.table_prefix.'page_group_members WHERE pg_id=' . $delete_id . ';');
+			if ( !$q )
+				$db->_die();
+			
+			$del_msg = $lang->get('acppg_msg_delete_success', array('pg_name' => htmlspecialchars($pg_name)));
+			echo "<div class=\"info-box\">$del_msg</div>";
+		}
+		else if ( isset($_POST['action']['edit']) && !isset($_POST['action']['noop']) )
+		{
+			if ( isset($_POST['action']['edit_save']) )
+			{
+			}
+ 		
+			if ( isset($_POST['action']['edit']['add_page']) && isset($_GET['src']) && $_GET['src'] == 'ajax' )
+			{
+				$return = array('successful' => false);
+				
+				//
+				// Add the specified page to the group
+				//
+				
+				// Get ID of the group
+				$edit_id = intval($_POST['pg_id']);
+				if ( !$edit_id )
+				{
+					$return = array('mode' => 'error', 'text' => 'Hack attempt');
+					echo enano_json_encode($return);
+					return;
+				}
+				
+				// Run some validation - check that page exists and that it's not already in the group
+				$page = $_POST['new_page'];
+				if ( empty($page) )
+				{
+					$return = array('mode' => 'error', 'text' => $lang->get('acppg_err_ajaxadd_need_title'));
+					echo enano_json_encode($return);
+					return;
+				}
+				
+				/*
+				// We're gonna allow adding nonexistent pages for now
+				if ( !isPage($page) )
+				{
+					$return = array('mode' => 'error', 'text' => 'The page you are trying to add (' . htmlspecialchars($page) . ') does not exist.');
+					echo enano_json_encode($return);
+					return;
+				}
+				*/
+				
+				list($page_id, $namespace) = RenderMan::strToPageID($page);
+				$page_id = sanitize_page_id($page_id);
+				
+				if ( !isset($paths->namespace[$namespace]) )
+				{
+					$return = array('mode' => 'error', 'text' => 'Invalid namespace return from RenderMan::strToPageID()');
+					echo enano_json_encode($return);
+					return;
+				}
+				
+				$q = $db->sql_query('SELECT "x" FROM '.table_prefix.'page_group_members WHERE pg_id=' . $edit_id . ' AND page_id=\'' . $db->escape($page_id) . '\' AND namespace=\'' . $namespace . '\';');
+				if ( !$q )
+				{
+					$return = array('mode' => 'error', 'text' => $db->get_error());
+					echo enano_json_encode($return);
+					return;
+				}
+				if ( $db->numrows() > 0 )
+				{
+					$return = array('mode' => 'error', 'text' => $lang->get('acppg_err_ajaxadd_already_in'));
+					echo enano_json_encode($return);
+					return;
+				}
+				
+				$q = $db->sql_query('INSERT INTO '.table_prefix.'page_group_members(pg_id, page_id, namespace) VALUES(' . $edit_id . ', \'' . $db->escape($page_id) . '\', \'' . $namespace . '\');');
+				if ( !$q )
+				{
+					$return = array('mode' => 'error', 'text' => $db->get_error());
+					echo enano_json_encode($return);
+					return;
+				}
+				
+				$title = "($namespace) " . get_page_title($paths->nslist[$namespace] . $page_id);
+				
+				$return = array('mode' => 'info', 'text' => $lang->get('acppg_ajaxadd_success'), 'successful' => true, 'title' => $title, 'member_id' => $db->insert_id());
+				
+				echo enano_json_encode($return);
+				return;
+			}
+			
+			if ( isset($_POST['action']['edit_save']) && isset($_POST['pg_name']) )
+			{
+				$edit_id = $_POST['action']['edit'];
+				$edit_id = intval($edit_id);
+				if ( !empty($edit_id) )
+				{
+					// Update group name
+					$new_name = $_POST['pg_name'];
+					if ( empty($new_name) )
+					{
+						echo '<div class="error-box">' . $lang->get('acppg_err_save_need_name') . '</div>';
+					}
+					else
+					{
+						$q = $db->sql_query('SELECT pg_name FROM '.table_prefix.'page_groups WHERE pg_id=' . $edit_id . ';');
+						if ( !$q )
+							$db->_die();
+						$row = $db->fetchrow();
+						$db->free_result();
+						if ( $new_name != $row['pg_name'] )
+						{
+							$new_name = $db->escape(trim($new_name));
+							$q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_name=\'' . $new_name . '\' WHERE pg_id=' . $edit_id . ';');
+							if ( !$q )
+								$db->_die();
+							else
+								echo '<div class="info-box">' . $lang->get('acppg_msg_save_name_updated') . '</div>';
+						}
+						if ( $_POST['pg_type'] == PAGE_GRP_TAGGED )
+						{
+							$target = $_POST['pg_target'];
+							$target = sanitize_tag($target);
+							if ( empty($target) )
+							{
+								echo '<div class="error-box">' . $lang->get('acppg_err_save_need_tag') . '</div>';
+							}
+							else
+							{
+								$target = $db->escape($target);
+								$q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_target=\'' . $target . '\' WHERE pg_id=' . $edit_id . ';');
+								if ( !$q )
+									$db->_die();
+								else
+									echo '<div class="info-box">' . $lang->get('acppg_msg_save_tag_updated') . '</div>';
+							}
+						}
+						else if ( $_POST['pg_type'] == PAGE_GRP_REGEX )
+						{
+							$target = $_POST['pg_target'];
+							if ( empty($target) )
+							{
+								echo '<div class="error-box">' . $lang->get('acppg_err_save_need_regex') . '</div>';
+							}
+							else
+							{
+								$target = $db->escape($target);
+								$q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_target=\'' . $target . '\' WHERE pg_id=' . $edit_id . ';');
+								if ( !$q )
+									$db->_die();
+								else
+									echo '<div class="info-box">' . $lang->get('acppg_msg_save_regex_updated') . '</div>';
+							}
+						}
+						else if ( $_POST['pg_type'] == PAGE_GRP_CATLINK )
+						{
+							$target = $_POST['pg_target'];
+							if ( empty($target) )
+							{
+								echo '<div class="error-box">' . $lang->get('acppg_err_save_bad_category') . '</div>';
+							}
+							else
+							{
+								$target = $db->escape($target);
+								$q = $db->sql_query('UPDATE '.table_prefix.'page_groups SET pg_target=\'' . $target . '\' WHERE pg_id=' . $edit_id . ';');
+								if ( !$q )
+									$db->_die();
+								else
+									echo '<div class="info-box">' . $lang->get('acppg_msg_save_cat_updated') . '</div>';
+							}
+						}
+					}
+				}
+			}
+			else if ( isset($_POST['action']['edit_save']) )
+			{
+				$edit_id = $_POST['action']['edit'];
+				$edit_id = intval($edit_id);
+			}
+			else
+			{
+				$edit_id = array_keys($_POST['action']['edit']);
+				$edit_id = intval($edit_id[0]);
+			}
+			
+			if ( empty($edit_id) )
+			{
+				echo 'Hack attempt';
+				return;
+			}
+			
+			if ( isset($_POST['action']['edit_save']['do_rm']) && !isset($_POST['pg_name']) )
+			{
+				$vals = array_keys($_POST['action']['edit_save']['rm']);
+				$good = array();
+				foreach ( $vals as $id )
+				{
+					if ( strval(intval($id)) == $id )
+						$good[] = $id;
+				}
+				$subquery = ( count($good) > 0 ) ? 'pg_member_id=' . implode(' OR pg_member_id=', $good) : "'foo'='bar'";
+				if ( $subquery == "'foo'='bar'" )
+				{
+					echo '<div class="warning-box">' . $lang->get('acppg_err_save_no_pages') . '</div>';
+				}
+				else
+				{
+					$sql = 'DELETE FROM '.table_prefix."page_group_members WHERE ( $subquery ) AND pg_id=$edit_id;";
+					if ( !$db->sql_query($sql) )
+					{
+						$db->_die();
+					}
+					echo '<div class="info-box">' . $lang->get('acppg_msg_save_pages_deleted') . '</div>';
+				}
+			}
+			
+			// Fetch information about page group
+			$q = $db->sql_query('SELECT pg_name, pg_type, pg_target FROM '.table_prefix.'page_groups WHERE pg_id=' . $edit_id . ';');
+			if ( !$q )
+				$db->_die();
+			
+			if ( $db->numrows() < 1 )
+			{
+				echo 'Bad request - can\'t load page group from database.';
+				return;
+			}
+			
+			$row = $db->fetchrow();
+			$db->free_result();
+			
+			echo '<form name="pg_edit_frm" action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+			echo '<input type="hidden" name="action[edit]" value="' . $edit_id . '" />';
+			echo '<input type="hidden" name="pg_type" value="' . $row['pg_type'] . '" />';
+			echo '<div class="tblholder">
+							<table border="0" cellspacing="1" cellpadding="4">
+								<tr>
+									<th colspan="3">' . $lang->get('acppg_th_editing_group') . ' ' . htmlspecialchars($row['pg_name']) . '</th>
+								</tr>';
+			// Group name
+			
+			echo '    <tr>
+									<td class="row2">' . $lang->get('acppg_field_group_name') . '</td>
+									<td class="row1" colspan="2"><input type="text" name="pg_name" value="' . htmlspecialchars($row['pg_name']) . '" size="30" /></td>
+								</tr>';
+			
+			$ajax_page_add = false;
+								
+			// This is where the going gets tricky.
+			// For static groups, we need to have each page listed out with a removal button, and a form to add new pages.
+			// For category links, we need a select box with each category in it, and
+			// For tag sets, just a text box to enter a new tag.
+			
+			// You can guess which one I dreaded.
+			
+			switch ( $row['pg_type'] )
+			{
+				case PAGE_GRP_NORMAL:
+					
+					// You have guessed correct.
+					// *Sits in chair for 10 minutes listening to the radio in an effort to put off writing the code you see below*
+					
+					echo '<tr><th colspan="3" class="subhead"><input type="submit" name="action[edit_save]" value="' . $lang->get('acppg_btn_save_name') . '" /></th></tr>';
+					echo '</table></div>';
+					echo '</form>';
+					echo '<form name="pg_static_rm_frm" action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" enctype="multipart/form-data">';
+					echo '<input type="hidden" name="action[edit]" value="' . $edit_id . '" />';
+					echo '<div class="tblholder">
+									<table border="0" cellspacing="1" cellpadding="4">
+										<tr>
+											<th colspan="3">' . $lang->get('acppg_th_remove_selected') . '</th>
+										</tr>';
+					
+					$q = $db->sql_query('SELECT m.pg_member_id,m.page_id,m.namespace FROM '.table_prefix.'page_group_members AS m
+ 																LEFT JOIN '.table_prefix.'pages AS p
+ 																	ON ( p.urlname = m.page_id AND p.namespace = m.namespace )
+ 																WHERE m.pg_id=' . $edit_id . ';');
+					
+					if ( !$q )
+						$db->_die();
+					
+					$delim = ceil( $db->numrows($q) / 2 );
+					if ( $delim < 5 )
+					{
+						$delim = 0xFFFFFFFE;
+						// stupid hack. I'm XSSing my own code.
+						$colspan = '2" id="pg_edit_tackon2me';
+					}
+					else
+					{
+						$colspan = "1";
+					}
+					
+					echo '<tr><td class="row2" rowspan="2">' . $lang->get('acppg_field_remove') . '</td><td class="row1" colspan="' . $colspan . '">';
+					$i = 0;
+					
+					while ( $row = $db->fetchrow($q) )
+					{
+						$i++;
+						if ( $i == $delim )
+						{
+							echo '</td><td class="row1" id="pg_edit_tackon2me">';
+						}
+						$page_name = '(' . $row['namespace'] . ') ' . get_page_title($paths->nslist[$row['namespace']] . $row['page_id']);
+						echo '<label><input type="checkbox" name="action[edit_save][rm][' . $row['pg_member_id'] . ']" /> ' . htmlspecialchars($page_name) . '</label><br />';
+					}
+					
+					echo '</td></tr>';
+					echo '<tr><th colspan="2" class="subhead" style="width: 70%;"><input type="submit" name="action[edit_save][do_rm]" value="' . $lang->get('acppg_btn_do_remove') . '" /></th></tr>';
+					
+					// More javascript magic!
+					?>
+					<script type="text/javascript">
+						var __pg_edit_submitAuthorized = true;
+						var __ol_pg_edit_setup = function()
+						{
+							var input = document.getElementById('inptext_pg_add_member');
+							input.onkeypress = function(e) {
+									if ( e.keyCode == 13 )
+									{
+										setTimeout('__pg_edit_ajaxadd(document.getElementById(\'' + this.id + '\'));', 500);
+									} 
+								};
+						}
+						addOnloadHook(__ol_pg_edit_setup);
+						var __pg_edit_objcache = false;
+						function __pg_edit_ajaxadd(obj)
+						{
+							if ( __pg_edit_objcache )
+								return false;
+							__pg_edit_objcache = obj;
+							
+							if ( obj.nextSibling )
+							{
+								if ( obj.nextSibling.tagName == 'DIV' )
+								{
+									obj.parentNode.removeChild(obj.nextSibling);
+								}
+							}
+							
+							// set width on parent, to prevent wrapping of ajax loading image
+							var w = $dynano(obj).Width();
+							w = w + 24;
+							obj.parentNode.style.width = w + 'px';
+							
+							// append the ajaxy loading image
+							var img = document.createElement('img');
+							img.src = scriptPath + '/images/loading.gif';
+							img.style.marginLeft = '4px';
+							insertAfter(obj.parentNode, img, obj);
+							
+							var url = makeUrlNS('Admin', 'PageGroups', 'src=ajax');
+							var page_add = escape(obj.value);
+							var pg_id = document.forms.pg_edit_frm['action[edit]'].value;
+							
+							ajaxPost(url, 'action[edit][add_page]=&pg_id=' + pg_id + '&new_page=' + page_add, function()
+								{
+									if ( ajax.readyState == 4 )
+									{
+										var obj = __pg_edit_objcache;
+										__pg_edit_objcache = false;
+										
+										// kill the loading graphic
+										obj.parentNode.removeChild(obj.nextSibling);
+										
+										var resptext = String(ajax.responseText + '');
+										if ( resptext.substr(0, 1) != '{' )
+										{
+											// This ain't JSON baby.
+											alert('Invalid JSON response:\n' + resptext);
+											return false;
+										}
+										var json = parseJSON(resptext);
+										
+										var div = document.createElement('div');
+										if ( json.mode == 'info' )
+										{
+											div.className = 'info-box-mini';
+										}
+										else if ( json.mode == 'error' )
+										{
+											div.className = 'error-box-mini';
+										}
+										div.appendChild(document.createTextNode(json.text));
+										insertAfter(obj.parentNode, div, obj);
+										
+										if ( json.successful )
+										{
+											var td = document.getElementById('pg_edit_tackon2me');
+											var lbl = document.createElement('label');
+											var check = document.createElement('input');
+											check.type = 'checkbox';
+											check.name = 'action[edit_save][rm][' + json.member_id + ']';
+											lbl.appendChild(check);
+											lbl.appendChild(document.createTextNode(' ' + json.title));
+											td.appendChild(lbl);
+											td.appendChild(document.createElement('br'));
+										}
+										
+									}
+								});
+						}
+					</script>
+					<?php
+					
+					$ajax_page_add = true;
+					
+					break;
+				case PAGE_GRP_TAGGED:
+					echo '<tr>
+									<td class="row2">
+										' . $lang->get('acppg_field_target_tag') . '
+									</td>
+									<td class="row1">
+										<input type="text" name="pg_target" value="' . htmlspecialchars($row['pg_target']) . '" size="30" />
+									</td>
+								</tr>';
+					break;
+				case PAGE_GRP_REGEX:
+					echo '<tr>
+									<td class="row2">
+										' . $lang->get('acppg_field_target_regex') . '<br />
+										<small>' . $lang->get('acppg_field_target_regex_hint') . '</small>
+									</td>
+									<td class="row1">
+										<input type="text" name="pg_target" value="' . htmlspecialchars($row['pg_target']) . '" size="30" />
+									</td>
+								</tr>';
+					break;
+				case PAGE_GRP_CATLINK:
+					
+					// Build category list
+					$q = $db->sql_query('SELECT name,urlname FROM '.table_prefix.'pages WHERE namespace=\'Category\';');
+					if ( !$q )
+						$db->_die();
+					
+					if ( $db->numrows() < 1 )
+					{
+						$catlist = 'There aren\'t any categories on this site.';
+					}
+					else
+					{
+						$catlist = '<select name="pg_target">';
+						while ( $catrow = $db->fetchrow() )
+						{
+							$selected = ( $catrow['urlname'] == $row['pg_target'] ) ? ' selected="selected"' : '';
+							$catlist .= '<option value="' . htmlspecialchars($catrow['urlname']) . '"' . $selected . '>' . htmlspecialchars($catrow['name']) . '</option>';
+						}
+						$catlist .= '</select>';
+					}
+					
+					echo '<tr>
+									<td class="row2">
+										' . $lang->get('acppg_field_target_category') . '<br />
+										<small>' . $lang->get('acppg_field_target_category_hint2') . '</small>
+									</td>
+									<td class="row1">
+										' . $catlist . '
+									</td>
+								</tr>';
+					
+					break;
+			}
+			
+			if ( $ajax_page_add )
+			{
+				echo '<tr><th colspan="3"><input type="submit" name="action[noop]" value="' . $lang->get('acppg_btn_cancel_all') . '" /></th></tr>';
+			}
+			else
+			{
+				echo '<tr><th colspan="3" class="subhead">
+								<input type="submit" name="action[edit_save]" value="' . $lang->get('acppg_btn_save_update') . '" />
+								<input type="submit" name="action[noop]" value="' . $lang->get('acppg_btn_cancel_all') . '" />
+							</th></tr>';
+			}
+			
+			echo '  </table>
+						</div>';
+			echo '</form>';
+			
+			if ( $ajax_page_add )
+			{
+				// This needs to be outside of the form.
+				echo '<div class="tblholder"><table border="0" cellspacing="1" cellpadding="4"><tr>';
+				echo '<th colspan="2">' . $lang->get('acppg_th_onthefly') . '</th></tr>';
+				echo '<tr>';
+				// Add pages AJAX form
+				echo '<td class="row2">' . $lang->get('acppg_field_add_page') . '<br /><small>' . $lang->get('acppg_field_add_page_hint') . '</small></td>';
+				echo '<td class="row1"><input type="text" size="30" name="pg_add_member" id="inptext_pg_add_member" class="autofill page" /></td>';
+				echo '</tr></table></div>';
+			}
+			
+			return;
+		}
+		else if ( isset($_POST['action']['noop']) )
+		{
+			// Do nothing - skip to main form (noop is usually invoked by a cancel button in a form above)
+		}
+		else
+		{
+			echo '<div class="error-box">Invalid format of $_POST[action].</div>';
+		}
+	}
+	// No action defined - show default menu
+	
+	echo '<h2>' . $lang->get('acppg_heading_main') . '</h2>';
+	echo '<p>' . $lang->get('acppg_hint_intro') . '</p>';
+	
+	$q = $db->sql_query('SELECT pg_id, pg_type, pg_name, pg_target FROM '.table_prefix.'page_groups;');
+	if ( !$q )
+		$db->_die();
 
-  echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
-  
-  echo '<div class="tblholder">
-          <table border="0" cellspacing="1" cellpadding="4">
-            <tr>
-              <th>' . $lang->get('acppg_col_group_name') . '</th>
-              <th>' . $lang->get('acppg_col_type') . '</th>
-              <th>' . $lang->get('acppg_col_target') . '</th>
-              <th colspan="2">' . $lang->get('acppg_col_actions') . '</th>
-            </tr>';
-  
-  if ( $row = $db->fetchrow($q) )
-  {
-    do
-    {
-      $name = htmlspecialchars($row['pg_name']);
-      $type = 'Invalid';
-      switch ( $row['pg_type'] )
-      {
-        case PAGE_GRP_CATLINK:
-          $type = $lang->get('acppg_gtype_catlink');
-          break;
-        case PAGE_GRP_TAGGED:
-          $type = $lang->get('acppg_gtype_tagged');
-          break;
-        case PAGE_GRP_NORMAL:
-          $type = $lang->get('acppg_gtype_static');
-          break;
-        case PAGE_GRP_REGEX:
-          $type = $lang->get('acppg_gtype_regex');
-          break;
-      }
-      $target = '';
-      if ( $row['pg_type'] == PAGE_GRP_TAGGED )
-      {
-        $target = $lang->get('acppg_lbl_tag') . ' ' . htmlspecialchars($row['pg_target']);
-      }
-      else if ( $row['pg_type'] == PAGE_GRP_CATLINK )
-      {
-        $target = $lang->get('acppg_lbl_category') . ' ' . htmlspecialchars(get_page_title($paths->nslist['Category'] . sanitize_page_id($row['pg_target'])));
-      }
-      else if ( $row['pg_type'] == PAGE_GRP_REGEX )
-      {
-        $target = $lang->get('acppg_lbl_regex') . ' <tt>' . htmlspecialchars($row['pg_target']) . '</tt>';
-      }
-      $btn_edit = '<input type="submit" name="action[edit][' . $row['pg_id'] . ']" value="' . $lang->get('acppg_btn_edit') . '" />';
-      $btn_del = '<input type="submit" name="action[del][' . $row['pg_id'] . ']" value="' . $lang->get('acppg_btn_delete') . '" />';
-      echo "<tr>
-              <td class=\"row1\">$name</td>
-              <td class=\"row2\">$type</td>
-              <td class=\"row1\">$target</td>
-              <td class=\"row3\" style=\"text-align: center;\">$btn_edit</td>
-              <td class=\"row3\" style=\"text-align: center;\">$btn_del</td>
-            </tr>";
-    }
-    while ( $row = $db->fetchrow($q) );
-  }
-  else
-  {
-    echo '  <tr><td class="row3" colspan="5" style="text-align: center;">' . $lang->get('acppg_msg_no_groups') . '</td></tr>';
-  }
-  
-  echo '    <tr>
-              <th class="subhead" colspan="5">
-                <input type="submit" name="action[create]" value="' . $lang->get('acppg_btn_create_new') . '" />
-              </th>
-            </tr>';
-  
-  echo '  </table>
-        </div>';
-        
-  echo '</form>';          
-    
+	echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', 'module='.$paths->cpage['module']).'" method="post" onsubmit="if(!submitAuthorized) return false;" enctype="multipart/form-data">';
+	
+	echo '<div class="tblholder">
+					<table border="0" cellspacing="1" cellpadding="4">
+						<tr>
+							<th>' . $lang->get('acppg_col_group_name') . '</th>
+							<th>' . $lang->get('acppg_col_type') . '</th>
+							<th>' . $lang->get('acppg_col_target') . '</th>
+							<th colspan="2">' . $lang->get('acppg_col_actions') . '</th>
+						</tr>';
+	
+	if ( $row = $db->fetchrow($q) )
+	{
+		do
+		{
+			$name = htmlspecialchars($row['pg_name']);
+			$type = 'Invalid';
+			switch ( $row['pg_type'] )
+			{
+				case PAGE_GRP_CATLINK:
+					$type = $lang->get('acppg_gtype_catlink');
+					break;
+				case PAGE_GRP_TAGGED:
+					$type = $lang->get('acppg_gtype_tagged');
+					break;
+				case PAGE_GRP_NORMAL:
+					$type = $lang->get('acppg_gtype_static');
+					break;
+				case PAGE_GRP_REGEX:
+					$type = $lang->get('acppg_gtype_regex');
+					break;
+			}
+			$target = '';
+			if ( $row['pg_type'] == PAGE_GRP_TAGGED )
+			{
+				$target = $lang->get('acppg_lbl_tag') . ' ' . htmlspecialchars($row['pg_target']);
+			}
+			else if ( $row['pg_type'] == PAGE_GRP_CATLINK )
+			{
+				$target = $lang->get('acppg_lbl_category') . ' ' . htmlspecialchars(get_page_title($paths->nslist['Category'] . sanitize_page_id($row['pg_target'])));
+			}
+			else if ( $row['pg_type'] == PAGE_GRP_REGEX )
+			{
+				$target = $lang->get('acppg_lbl_regex') . ' <tt>' . htmlspecialchars($row['pg_target']) . '</tt>';
+			}
+			$btn_edit = '<input type="submit" name="action[edit][' . $row['pg_id'] . ']" value="' . $lang->get('acppg_btn_edit') . '" />';
+			$btn_del = '<input type="submit" name="action[del][' . $row['pg_id'] . ']" value="' . $lang->get('acppg_btn_delete') . '" />';
+			echo "<tr>
+							<td class=\"row1\">$name</td>
+							<td class=\"row2\">$type</td>
+							<td class=\"row1\">$target</td>
+							<td class=\"row3\" style=\"text-align: center;\">$btn_edit</td>
+							<td class=\"row3\" style=\"text-align: center;\">$btn_del</td>
+						</tr>";
+		}
+		while ( $row = $db->fetchrow($q) );
+	}
+	else
+	{
+		echo '  <tr><td class="row3" colspan="5" style="text-align: center;">' . $lang->get('acppg_msg_no_groups') . '</td></tr>';
+	}
+	
+	echo '    <tr>
+							<th class="subhead" colspan="5">
+								<input type="submit" name="action[create]" value="' . $lang->get('acppg_btn_create_new') . '" />
+							</th>
+						</tr>';
+	
+	echo '  </table>
+				</div>';
+				
+	echo '</form>';          
+		
 }
 
 ?>
--- a/plugins/admin/PageManager.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/PageManager.php	Sun Mar 28 23:10:46 2010 -0400
@@ -15,606 +15,606 @@
 
 function page_Admin_PageManager()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $cache;
-  
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  require_once(ENANO_ROOT . '/includes/pageutils.php');
-  
-  echo '<h3>' . $lang->get('acppm_heading_main') . '</h3>';
-  $show_select = true;
-  
-  if ( isset($_REQUEST['action']) || isset($_REQUEST['source']) )
-  {
-    if ( isset($_REQUEST['action']) )
-    {
-      $act =& $_REQUEST['action'];
-      $act = strtolower($act);
-    }
-    else if ( isset($_REQUEST['source']) && $_REQUEST['source'] == 'ajax' )
-    {
-      $act = 'select';
-    }
-    switch ( $act )
-    {
-      case 'save':
-      case 'select':
-        // First step is to determine the page ID and namespace
-        
-        if ( isset($_REQUEST['pid_search']) )
-        {
-          list($page_id, $namespace) = RenderMan::strToPageID($_REQUEST['page_id']);
-          $name = $db->escape(dirtify_page_id($page_id));
-          $page_id = $db->escape(sanitize_page_id($page_id));
-          $namespace = $db->escape($namespace);
-          $name = strtolower($name);
-          $page_id = strtolower($page_id);
-          $sql = "SELECT * FROM " . table_prefix . "pages WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(urlname) LIKE '%$page_id%' OR " . ENANO_SQLFUNC_LOWERCASE . "(name) LIKE '%$name%' ) ORDER BY name ASC;";
-        }
-        else
-        {
-          // pid_search was not set, assume absolute page ID
-          list($page_id, $namespace) = RenderMan::strToPageID($_REQUEST['page_id']);
-          $page_id = $db->escape(sanitize_page_id($page_id));
-          $namespace = $db->escape($namespace);
-          
-          $sql = "SELECT * FROM " . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$namespace';";
-        }
-        
-        if ( !($q = $db->sql_query($sql)) )
-        {
-          $db->_die('PageManager selecting dataset for page');
-        }
-        
-        if ( $db->numrows() < 1 )
-        {
-          echo '<div class="error-box">
-                  ' . $lang->get('acppm_err_page_not_found') . '
-                </div>';
-          break;
-        }
-        
-        if ( $db->numrows() > 1 )
-        {
-          // Ambiguous results
-          if ( isset($_REQUEST['pid_search']) )
-          {
-            echo '<h3>' . $lang->get('acppm_msg_results_ambiguous_title') . '</h3>';
-            echo '<p>' . $lang->get('acppm_msg_results_ambiguous_body') . '</p>';
-            echo '<ul>';
-            while ( $row = $db->fetchrow($q) )
-            {
-              echo '<li>';
-              $pathskey = $paths->nslist[$row['namespace']] . $row['urlname'];
-              $edit_url = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager&action=select&page_id=$pathskey", true);
-              $view_url = makeUrlNS($row['namespace'], $row['urlname']);
-              $page_name = htmlspecialchars(get_page_title_ns( $row['urlname'], $row['namespace'] ));
-              $view_link = $lang->get('acppm_ambig_btn_viewpage');
-              echo "<a href=\"$edit_url\">$page_name</a> (<a onclick=\"window.open(this.href); return false;\" href=\"$view_url\">$view_link</a>)";
-              echo '</li>';
-            }
-            echo '</ul>';
-            $show_select = false;
-            break;
-          }
-          else
-          {
-            echo '<p>' . $lang->get('acppm_err_ambig_absolute') . '</p>';
-            break;
-          }
-        }
-        
-        // From this point on we can assume that exactly one matching page was found.
-        $dataset = $db->fetchrow();
-        $page_id = $dataset['urlname'];
-        $namespace = $dataset['namespace'];
-        
-        // This is used to re-determine the page ID after submit.
-        $pathskey = $paths->nslist[$namespace] . sanitize_page_id($page_id);
-        
-        // The extra switch allows us to break out of the save routine if needed
-        switch ( $act )
-        {
-          case 'save':
-            
-            $errors = array();
-            $page_id_changed = false;
-            $namespace_changed = false;
-            
-            // Backup the dataset to avoid redundantly updating values
-            $dataset_backup = $dataset;
-            
-            // We've elected to save the page. The angle of attack here is to validate each form field,
-            // and if the field validates successfully, change the value in $dataset accordingly.
-            
-            // Field: page name
-            $page_name = $_POST['page_name'];
-            $page_name = trim($page_name);
-            if ( empty($page_name) )
-            {
-              $errors[] = $lang->get('acppm_err_invalid_page_name');
-            }
-            else
-            {
-              $dataset['name'] = $page_name;
-            }
-            
-            // Field: page URL string
-            $page_urlname = $_POST['page_urlname'];
-            $page_urlname = trim($_POST['page_urlname']);
-            if ( empty($page_urlname) && !have_blank_urlname_page() )
-            {
-              $errors[] = $lang->get('acppm_err_invalid_url_string');
-            }
-            else
-            {
-              $page_id_changed = ( $_POST['page_urlname'] !== $dataset['urlname'] );
-              $dataset['urlname'] = sanitize_page_id($page_urlname);
-            }
-            
-            // Field: namespace
-            $namespace_new = $_POST['page_namespace'];
-            if ( !isset($paths->nslist[ $namespace ]) )
-            {
-              $errors[] = $lang->get('acppm_err_invalid_namespace');
-            }
-            else
-            {
-              $namespace_changed = ( $_POST['page_namespace'] !== $dataset['namespace'] );
-              $dataset['namespace'] = $namespace_new;
-            }
-            
-            // Field: comments enabled
-            $dataset['comments_on'] = ( isset($_POST['comments_on']) ) ? 1 : 0;
-            
-            // Field: page visible
-            $dataset['visible'] = ( isset($_POST['visible']) ) ? 1 : 0;
-            
-            // Field: standalone page
-            $dataset['special'] = ( isset($_POST['special']) ) ? 1 : 0;
-            
-            // Field: page protection
-            $protect_level = $_POST['protected'];
-            if ( !in_array($protect_level, array('0', '1', '2')) )
-            {
-              $errors[] = $lang->get('acppm_err_invalid_protection');
-            }
-            else
-            {
-              $dataset['protected'] = intval($protect_level);
-            }
-            
-            // Field: wiki mode
-            $wiki_mode = $_POST['wikimode'];
-            if ( !in_array($wiki_mode, array('0', '1', '2')) )
-            {
-              $errors[] = $lang->get('acppm_err_invalid_wiki_mode');
-            }
-            else
-            {
-              $dataset['wiki_mode'] = intval($wiki_mode);
-            }
-            
-            if ( count($errors) < 1 )
-            {
-              // We're free of errors. Build a SQL query to update the page table.
-              $particles = array();
-              
-              foreach ( $dataset as $key => $value )
-              {
-                if ( $value === $dataset_backup[$key] || ( is_int($value) && $value === intval($dataset_backup[$key]) ) )
-                  continue;
-                if ( is_int($value) )
-                {
-                  $particle = "$key = $value";
-                }
-                else
-                {
-                  $value = $db->escape($value);
-                  $particle = "$key = '$value'";
-                }
-                $particles[] = $particle;
-                unset($particle);
-              }
-              
-              $page_id_new = $db->escape($dataset['urlname']);
-              $namespace_new = $db->escape($dataset['namespace']);
-              
-              // Only run the update query if at least one field was changed.
-              if ( count($particles) > 0 )
-              {
-                $particles = implode(', ', $particles);
-                $page_id_db = $db->escape($page_id);
-                $namespace_db = $db->escape($namespace);
-                $sql = 'UPDATE ' . table_prefix . "pages SET $particles WHERE urlname = '$page_id_db' AND namespace = '$namespace_db';";
-                
-                if ( !$db->sql_query($sql) )
-                  $db->_die('PageManager running primary update query');
-                
-                // Did we change the page ID or namespace? If so we need to also change logs, comments, tags, etc.
-                if ( $page_id_changed || $namespace_changed )
-                {
-                  $sql = array(
-                      'UPDATE ' . table_prefix . "logs SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';",
-                      'UPDATE ' . table_prefix . "tags SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';",
-                      'UPDATE ' . table_prefix . "comments SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';",
-                      'UPDATE ' . table_prefix . "page_text SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';",
-                      'UPDATE ' . table_prefix . "categories SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';"
-                    );
-                  foreach ( $sql as $q )
-                  {
-                    if ( !$db->sql_query($q) )
-                      $db->_die('PageManager running slave update query after page ID/namespace change');
-                  }
-                  
-                  // If we're going File -> other, remove files
-                  if ( $namespace_db === 'File' )
-                  {
-                    PageUtils::delete_page_files($page_id);
-                  }
-                }
-                
-                // Did we change the name of the page? If so, make PageProcessor log it
-                if ( $dataset_backup['name'] != $dataset['name'] )
-                {
-                  $page = new PageProcessor($page_id_new, $namespace_new);
-                  $page->rename_page($dataset['name']);
-                }
-                
-                // Finally, clear the metadata cache
-                $cache->purge('page_meta');
-              }
-              
-              // Did the user ask to delete the page?
-              // I know it's a bit pointless to delete the page only after validating and processing the whole form, but what the heck :)
-              if ( isset($_POST['delete']) )
-              {
-                PageUtils::deletepage($page_id_new, $namespace_new, $lang->get('acppm_delete_reason'));
-              }
-              
-              echo '<div class="info-box">' . $lang->get('acppm_msg_save_success', array( 'viewpage_url' => makeUrlNS($dataset['namespace'], $dataset['urlname']) )) . '</div>';
-              break 2;
-            }
-            
-            break;
-        }
-        $tpl_code = <<<TPLCODE
-        <div class="tblholder">
-          <table border="0" cellspacing="1" cellpadding="4">
-            <tr>
-              <th colspan="2">
-                {lang:acppm_heading_editing} "{PAGE_NAME}"
-              </th>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_page_name}
-              </td>
-              <td class="row1">
-                <input type="text" name="page_name" value="{PAGE_NAME}" size="40" />
-              </td>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_page_urlname}<br />
-                <small>{lang:acppm_lbl_page_urlname_hint}</small>
-              </td>
-              <td class="row1">
-                <input type="text" name="page_urlname" value="{PAGE_URLNAME}" size="40" />
-              </td>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_namespace}
-              </td>
-              <td class="row1">
-                <select name="page_namespace">
-                {NAMESPACE_LIST}</select>
-                <!-- BEGIN is_file -->
-                <br />
-                {lang:acppm_msg_file_ns_warning}
-                <!-- END is_file -->
-              </td>
-            </tr>
-            
-            <tr>
-              <th colspan="2" class="subhead">
-                {lang:acppm_heading_advanced}
-              </th>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_enable_comments_title}
-              </td>
-              <td class="row1">
-                <label>
-                  <input type="checkbox" name="comments_on" <!-- BEGIN comments_enabled -->checked="checked" <!-- END comments_enabled -->/>
-                  {lang:acppm_lbl_enable_comments}
-                </label>
-                <br />
-                <small>{lang:acppm_lbl_enable_comments_hint}</small>
-              </td>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_special_title}
-              </td>
-              <td class="row1">
-                <label>
-                  <input type="checkbox" name="special" <!-- BEGIN special -->checked="checked" <!-- END special -->/>
-                  {lang:acppm_lbl_special}
-                </label>
-                <br />
-                <small>{lang:acppm_lbl_special_hint}</small>
-              </td>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_visible_title}
-              </td>
-              <td class="row1">
-                <label>
-                  <input type="checkbox" name="visible" <!-- BEGIN visible -->checked="checked" <!-- END visible -->/>
-                  {lang:acppm_lbl_visible}
-                </label>
-                <br />
-                <small>{lang:acppm_lbl_visible_hint}</small>
-              </td>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_protected_title}
-              </td>
-              <td class="row1">
-                <label>
-                  <input type="radio" name="protected" value="0" <!-- BEGIN protected_off -->checked="checked" <!-- END protected_off -->/>
-                  {lang:acppm_lbl_protected_off}
-                </label>
-                <br />
-                <label>
-                  <input type="radio" name="protected" value="1" <!-- BEGIN protected_on -->checked="checked" <!-- END protected_on -->/>
-                  {lang:acppm_lbl_protected_on}
-                </label>
-                <br />
-                <label>
-                  <input type="radio" name="protected" value="2" <!-- BEGIN protected_semi -->checked="checked" <!-- END protected_semi -->/>
-                  {lang:acppm_lbl_protected_semi}
-                </label>
-                <br />
-                <small>{lang:acppm_lbl_protected_hint}</small>
-              </td>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_wikimode_title}
-              </td>
-              <td class="row1">
-                <label>
-                  <input type="radio" name="wikimode" value="0" <!-- BEGIN wikimode_off -->checked="checked" <!-- END wikimode_off -->/>
-                  {lang:acppm_lbl_wikimode_off}
-                </label>
-                <br />
-                <label>
-                  <input type="radio" name="wikimode" value="1" <!-- BEGIN wikimode_on -->checked="checked" <!-- END wikimode_on -->/>
-                  {lang:acppm_lbl_wikimode_on}
-                </label>
-                <br />
-                <label>
-                  <input type="radio" name="wikimode" value="2" <!-- BEGIN wikimode_global -->checked="checked" <!-- END wikimode_global -->/>
-                  {lang:acppm_lbl_wikimode_global}
-                </label>
-                <br />
-                <small>{lang:acppm_lbl_wikimode_hint}</small>
-              </td>
-            </tr>
-            
-            <tr>
-              <td class="row2">
-                {lang:acppm_lbl_delete_title}
-              </td>
-              <td class="row1">
-                <label>
-                  <input type="checkbox" name="delete" />
-                  {lang:acppm_lbl_delete}
-                </label>
-                <br />
-                <small>{lang:acppm_lbl_delete_hint}</small>
-              </td>
-            </tr>
-            
-            <tr>
-              <th colspan="2" class="subhead">
-                <button name="action" value="save">
-                  <b>{lang:etc_save_changes}</b>
-                </button>
-                <button name="action" value="nil">
-                  <b>{lang:etc_cancel}</b>
-                </button>
-              </th>
-            </tr>
-            
-          </table>
-        </div>
-        
-        <input type="hidden" name="page_id" value="{PATHS_KEY}" />
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $cache;
+	
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	require_once(ENANO_ROOT . '/includes/pageutils.php');
+	
+	echo '<h3>' . $lang->get('acppm_heading_main') . '</h3>';
+	$show_select = true;
+	
+	if ( isset($_REQUEST['action']) || isset($_REQUEST['source']) )
+	{
+		if ( isset($_REQUEST['action']) )
+		{
+			$act =& $_REQUEST['action'];
+			$act = strtolower($act);
+		}
+		else if ( isset($_REQUEST['source']) && $_REQUEST['source'] == 'ajax' )
+		{
+			$act = 'select';
+		}
+		switch ( $act )
+		{
+			case 'save':
+			case 'select':
+				// First step is to determine the page ID and namespace
+				
+				if ( isset($_REQUEST['pid_search']) )
+				{
+					list($page_id, $namespace) = RenderMan::strToPageID($_REQUEST['page_id']);
+					$name = $db->escape(dirtify_page_id($page_id));
+					$page_id = $db->escape(sanitize_page_id($page_id));
+					$namespace = $db->escape($namespace);
+					$name = strtolower($name);
+					$page_id = strtolower($page_id);
+					$sql = "SELECT * FROM " . table_prefix . "pages WHERE ( " . ENANO_SQLFUNC_LOWERCASE . "(urlname) LIKE '%$page_id%' OR " . ENANO_SQLFUNC_LOWERCASE . "(name) LIKE '%$name%' ) ORDER BY name ASC;";
+				}
+				else
+				{
+					// pid_search was not set, assume absolute page ID
+					list($page_id, $namespace) = RenderMan::strToPageID($_REQUEST['page_id']);
+					$page_id = $db->escape(sanitize_page_id($page_id));
+					$namespace = $db->escape($namespace);
+					
+					$sql = "SELECT * FROM " . table_prefix . "pages WHERE urlname = '$page_id' AND namespace = '$namespace';";
+				}
+				
+				if ( !($q = $db->sql_query($sql)) )
+				{
+					$db->_die('PageManager selecting dataset for page');
+				}
+				
+				if ( $db->numrows() < 1 )
+				{
+					echo '<div class="error-box">
+									' . $lang->get('acppm_err_page_not_found') . '
+								</div>';
+					break;
+				}
+				
+				if ( $db->numrows() > 1 )
+				{
+					// Ambiguous results
+					if ( isset($_REQUEST['pid_search']) )
+					{
+						echo '<h3>' . $lang->get('acppm_msg_results_ambiguous_title') . '</h3>';
+						echo '<p>' . $lang->get('acppm_msg_results_ambiguous_body') . '</p>';
+						echo '<ul>';
+						while ( $row = $db->fetchrow($q) )
+						{
+							echo '<li>';
+							$pathskey = $paths->nslist[$row['namespace']] . $row['urlname'];
+							$edit_url = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager&action=select&page_id=$pathskey", true);
+							$view_url = makeUrlNS($row['namespace'], $row['urlname']);
+							$page_name = htmlspecialchars(get_page_title_ns( $row['urlname'], $row['namespace'] ));
+							$view_link = $lang->get('acppm_ambig_btn_viewpage');
+							echo "<a href=\"$edit_url\">$page_name</a> (<a onclick=\"window.open(this.href); return false;\" href=\"$view_url\">$view_link</a>)";
+							echo '</li>';
+						}
+						echo '</ul>';
+						$show_select = false;
+						break;
+					}
+					else
+					{
+						echo '<p>' . $lang->get('acppm_err_ambig_absolute') . '</p>';
+						break;
+					}
+				}
+				
+				// From this point on we can assume that exactly one matching page was found.
+				$dataset = $db->fetchrow();
+				$page_id = $dataset['urlname'];
+				$namespace = $dataset['namespace'];
+				
+				// This is used to re-determine the page ID after submit.
+				$pathskey = $paths->nslist[$namespace] . sanitize_page_id($page_id);
+				
+				// The extra switch allows us to break out of the save routine if needed
+				switch ( $act )
+				{
+					case 'save':
+						
+						$errors = array();
+						$page_id_changed = false;
+						$namespace_changed = false;
+						
+						// Backup the dataset to avoid redundantly updating values
+						$dataset_backup = $dataset;
+						
+						// We've elected to save the page. The angle of attack here is to validate each form field,
+						// and if the field validates successfully, change the value in $dataset accordingly.
+						
+						// Field: page name
+						$page_name = $_POST['page_name'];
+						$page_name = trim($page_name);
+						if ( empty($page_name) )
+						{
+							$errors[] = $lang->get('acppm_err_invalid_page_name');
+						}
+						else
+						{
+							$dataset['name'] = $page_name;
+						}
+						
+						// Field: page URL string
+						$page_urlname = $_POST['page_urlname'];
+						$page_urlname = trim($_POST['page_urlname']);
+						if ( empty($page_urlname) && !have_blank_urlname_page() )
+						{
+							$errors[] = $lang->get('acppm_err_invalid_url_string');
+						}
+						else
+						{
+							$page_id_changed = ( $_POST['page_urlname'] !== $dataset['urlname'] );
+							$dataset['urlname'] = sanitize_page_id($page_urlname);
+						}
+						
+						// Field: namespace
+						$namespace_new = $_POST['page_namespace'];
+						if ( !isset($paths->nslist[ $namespace ]) )
+						{
+							$errors[] = $lang->get('acppm_err_invalid_namespace');
+						}
+						else
+						{
+							$namespace_changed = ( $_POST['page_namespace'] !== $dataset['namespace'] );
+							$dataset['namespace'] = $namespace_new;
+						}
+						
+						// Field: comments enabled
+						$dataset['comments_on'] = ( isset($_POST['comments_on']) ) ? 1 : 0;
+						
+						// Field: page visible
+						$dataset['visible'] = ( isset($_POST['visible']) ) ? 1 : 0;
+						
+						// Field: standalone page
+						$dataset['special'] = ( isset($_POST['special']) ) ? 1 : 0;
+						
+						// Field: page protection
+						$protect_level = $_POST['protected'];
+						if ( !in_array($protect_level, array('0', '1', '2')) )
+						{
+							$errors[] = $lang->get('acppm_err_invalid_protection');
+						}
+						else
+						{
+							$dataset['protected'] = intval($protect_level);
+						}
+						
+						// Field: wiki mode
+						$wiki_mode = $_POST['wikimode'];
+						if ( !in_array($wiki_mode, array('0', '1', '2')) )
+						{
+							$errors[] = $lang->get('acppm_err_invalid_wiki_mode');
+						}
+						else
+						{
+							$dataset['wiki_mode'] = intval($wiki_mode);
+						}
+						
+						if ( count($errors) < 1 )
+						{
+							// We're free of errors. Build a SQL query to update the page table.
+							$particles = array();
+							
+							foreach ( $dataset as $key => $value )
+							{
+								if ( $value === $dataset_backup[$key] || ( is_int($value) && $value === intval($dataset_backup[$key]) ) )
+									continue;
+								if ( is_int($value) )
+								{
+									$particle = "$key = $value";
+								}
+								else
+								{
+									$value = $db->escape($value);
+									$particle = "$key = '$value'";
+								}
+								$particles[] = $particle;
+								unset($particle);
+							}
+							
+							$page_id_new = $db->escape($dataset['urlname']);
+							$namespace_new = $db->escape($dataset['namespace']);
+							
+							// Only run the update query if at least one field was changed.
+							if ( count($particles) > 0 )
+							{
+								$particles = implode(', ', $particles);
+								$page_id_db = $db->escape($page_id);
+								$namespace_db = $db->escape($namespace);
+								$sql = 'UPDATE ' . table_prefix . "pages SET $particles WHERE urlname = '$page_id_db' AND namespace = '$namespace_db';";
+								
+								if ( !$db->sql_query($sql) )
+									$db->_die('PageManager running primary update query');
+								
+								// Did we change the page ID or namespace? If so we need to also change logs, comments, tags, etc.
+								if ( $page_id_changed || $namespace_changed )
+								{
+									$sql = array(
+											'UPDATE ' . table_prefix . "logs SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';",
+											'UPDATE ' . table_prefix . "tags SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';",
+											'UPDATE ' . table_prefix . "comments SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';",
+											'UPDATE ' . table_prefix . "page_text SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';",
+											'UPDATE ' . table_prefix . "categories SET page_id = '$page_id_new', namespace = '$namespace_new' WHERE page_id = '$page_id_db' AND namespace = '$namespace_db';"
+										);
+									foreach ( $sql as $q )
+									{
+										if ( !$db->sql_query($q) )
+											$db->_die('PageManager running slave update query after page ID/namespace change');
+									}
+									
+									// If we're going File -> other, remove files
+									if ( $namespace_db === 'File' )
+									{
+										PageUtils::delete_page_files($page_id);
+									}
+								}
+								
+								// Did we change the name of the page? If so, make PageProcessor log it
+								if ( $dataset_backup['name'] != $dataset['name'] )
+								{
+									$page = new PageProcessor($page_id_new, $namespace_new);
+									$page->rename_page($dataset['name']);
+								}
+								
+								// Finally, clear the metadata cache
+								$cache->purge('page_meta');
+							}
+							
+							// Did the user ask to delete the page?
+							// I know it's a bit pointless to delete the page only after validating and processing the whole form, but what the heck :)
+							if ( isset($_POST['delete']) )
+							{
+								PageUtils::deletepage($page_id_new, $namespace_new, $lang->get('acppm_delete_reason'));
+							}
+							
+							echo '<div class="info-box">' . $lang->get('acppm_msg_save_success', array( 'viewpage_url' => makeUrlNS($dataset['namespace'], $dataset['urlname']) )) . '</div>';
+							break 2;
+						}
+						
+						break;
+				}
+				$tpl_code = <<<TPLCODE
+				<div class="tblholder">
+					<table border="0" cellspacing="1" cellpadding="4">
+						<tr>
+							<th colspan="2">
+								{lang:acppm_heading_editing} "{PAGE_NAME}"
+							</th>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_page_name}
+							</td>
+							<td class="row1">
+								<input type="text" name="page_name" value="{PAGE_NAME}" size="40" />
+							</td>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_page_urlname}<br />
+								<small>{lang:acppm_lbl_page_urlname_hint}</small>
+							</td>
+							<td class="row1">
+								<input type="text" name="page_urlname" value="{PAGE_URLNAME}" size="40" />
+							</td>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_namespace}
+							</td>
+							<td class="row1">
+								<select name="page_namespace">
+								{NAMESPACE_LIST}</select>
+								<!-- BEGIN is_file -->
+								<br />
+								{lang:acppm_msg_file_ns_warning}
+								<!-- END is_file -->
+							</td>
+						</tr>
+						
+						<tr>
+							<th colspan="2" class="subhead">
+								{lang:acppm_heading_advanced}
+							</th>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_enable_comments_title}
+							</td>
+							<td class="row1">
+								<label>
+									<input type="checkbox" name="comments_on" <!-- BEGIN comments_enabled -->checked="checked" <!-- END comments_enabled -->/>
+									{lang:acppm_lbl_enable_comments}
+								</label>
+								<br />
+								<small>{lang:acppm_lbl_enable_comments_hint}</small>
+							</td>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_special_title}
+							</td>
+							<td class="row1">
+								<label>
+									<input type="checkbox" name="special" <!-- BEGIN special -->checked="checked" <!-- END special -->/>
+									{lang:acppm_lbl_special}
+								</label>
+								<br />
+								<small>{lang:acppm_lbl_special_hint}</small>
+							</td>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_visible_title}
+							</td>
+							<td class="row1">
+								<label>
+									<input type="checkbox" name="visible" <!-- BEGIN visible -->checked="checked" <!-- END visible -->/>
+									{lang:acppm_lbl_visible}
+								</label>
+								<br />
+								<small>{lang:acppm_lbl_visible_hint}</small>
+							</td>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_protected_title}
+							</td>
+							<td class="row1">
+								<label>
+									<input type="radio" name="protected" value="0" <!-- BEGIN protected_off -->checked="checked" <!-- END protected_off -->/>
+									{lang:acppm_lbl_protected_off}
+								</label>
+								<br />
+								<label>
+									<input type="radio" name="protected" value="1" <!-- BEGIN protected_on -->checked="checked" <!-- END protected_on -->/>
+									{lang:acppm_lbl_protected_on}
+								</label>
+								<br />
+								<label>
+									<input type="radio" name="protected" value="2" <!-- BEGIN protected_semi -->checked="checked" <!-- END protected_semi -->/>
+									{lang:acppm_lbl_protected_semi}
+								</label>
+								<br />
+								<small>{lang:acppm_lbl_protected_hint}</small>
+							</td>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_wikimode_title}
+							</td>
+							<td class="row1">
+								<label>
+									<input type="radio" name="wikimode" value="0" <!-- BEGIN wikimode_off -->checked="checked" <!-- END wikimode_off -->/>
+									{lang:acppm_lbl_wikimode_off}
+								</label>
+								<br />
+								<label>
+									<input type="radio" name="wikimode" value="1" <!-- BEGIN wikimode_on -->checked="checked" <!-- END wikimode_on -->/>
+									{lang:acppm_lbl_wikimode_on}
+								</label>
+								<br />
+								<label>
+									<input type="radio" name="wikimode" value="2" <!-- BEGIN wikimode_global -->checked="checked" <!-- END wikimode_global -->/>
+									{lang:acppm_lbl_wikimode_global}
+								</label>
+								<br />
+								<small>{lang:acppm_lbl_wikimode_hint}</small>
+							</td>
+						</tr>
+						
+						<tr>
+							<td class="row2">
+								{lang:acppm_lbl_delete_title}
+							</td>
+							<td class="row1">
+								<label>
+									<input type="checkbox" name="delete" />
+									{lang:acppm_lbl_delete}
+								</label>
+								<br />
+								<small>{lang:acppm_lbl_delete_hint}</small>
+							</td>
+						</tr>
+						
+						<tr>
+							<th colspan="2" class="subhead">
+								<button name="action" value="save">
+									<b>{lang:etc_save_changes}</b>
+								</button>
+								<button name="action" value="nil">
+									<b>{lang:etc_cancel}</b>
+								</button>
+							</th>
+						</tr>
+						
+					</table>
+				</div>
+				
+				<input type="hidden" name="page_id" value="{PATHS_KEY}" />
 TPLCODE;
-        $parser = $template->makeParserText($tpl_code);
-        
-        $ns_list = '';
-        foreach ( $paths->nslist as $ns => $prefix ) 
-        {
-          // FIXME: Plugins need to specify whether they want Enano's regular PageProcessor
-          // to handle these pages, and whether such pages from namespaces created by plugins
-          // can be stored in the database or not.
-          if ( $ns == 'Special' || $ns == 'Admin' || $ns == 'Anonymous' )
-            continue;
-          $ns = htmlspecialchars($ns);
-          $prefix = htmlspecialchars($prefix);
-          if ( empty($prefix) )
-            $prefix = $lang->get('acppm_ns_article');
-          $sel = ( $dataset['namespace'] == $ns ) ? ' selected="selected"' : '';
-          $ns_list .= "  <option value=\"$ns\"$sel>$prefix</option>\n                ";
-        }
-        
-        $parser->assign_vars(array(
-            'PAGE_NAME' => htmlspecialchars($dataset['name']),
-            'PAGE_URLNAME' => htmlspecialchars($dataset['urlname']),
-            'NAMESPACE_LIST' => $ns_list,
-            'PATHS_KEY' => $pathskey
-          ));
-        
-        $parser->assign_bool(array(
-            'comments_enabled' => ( $dataset['comments_on'] == 1 ),
-            'special' => ( $dataset['special'] == 1 ),
-            'visible' => ( $dataset['visible'] == 1 ),
-            'protected_off'   => ( $dataset['protected'] == 0 ),
-            'protected_on'    => ( $dataset['protected'] == 1 ),
-            'protected_semi'  => ( $dataset['protected'] == 2 ),
-            'wikimode_off'    => ( $dataset['wiki_mode'] == 0 ),
-            'wikimode_on'     => ( $dataset['wiki_mode'] == 1 ),
-            'wikimode_global' => ( $dataset['wiki_mode'] == 2 ),
-            'is_file'         => ( $dataset['namespace'] == 'File' )
-          ));
-        
-        if ( isset($errors) )
-        {
-          echo '<div class="error-box">';
-          echo $lang->get('acppm_err_header');
-          echo '<ul>';
-          echo '<li>' . implode('</li><li>', $errors) . '</li>';
-          echo '</ul>';
-          echo '</div>';
-        }
-        
-        $form_action = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager", true);
-        
-        echo "<form action=\"$form_action\" method=\"post\">";
-        echo $parser->run();
-        echo "</form>";
-        
-        $show_select = false;
-        break;
-    }
-  }
-  
-  if ( $show_select )
-  {
-    echo '<p>' . $lang->get('acppm_hint') . '</p>';
-    
-    // Show the search form
-    
-    $form_action = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager", true);
-    echo "<form action=\"$form_action\" method=\"post\">";
-    echo $lang->get('acppm_lbl_field_search') . ' ';
-    echo $template->pagename_field('page_id') . ' ';
-    echo '<input type="hidden" name="action" value="select" />';
-    echo '<input type="submit" name="pid_search" value="' . $lang->get('search_btn_search') . '" />';
-    echo "</form>";
-    
-    // Grab all pages from the database and show a list of pages on the site
-    
-    echo '<h3>' . $lang->get('acppm_heading_select_page_from_list') . '</h3>';
-    echo '<p>' . $lang->get('acppm_hint_select_page_from_list') . '</p>';
-    
-    $q = $db->sql_query('SELECT COUNT(name) AS num_pages FROM ' . table_prefix . 'pages;');
-    if ( !$q )
-      $db->_die('PageManager doing initial page count');
-    list($num_pages) = $db->fetchrow_num();
-    $db->free_result();
-    
-    $pg_start = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0;
-    
-    $q = $db->sql_query('SELECT urlname, name, namespace, ' . $num_pages . ' AS num_pages, ' . $pg_start . ' AS offset FROM ' . table_prefix . 'pages ORDER BY name ASC;');
-    if ( !$q )
-      $db->_die('PageManager doing main select query for page list');
-    
-    // Paginate results
-    $html = paginate(
-        $q,
-        '{urlname}',
-        $num_pages,
-        makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager&offset=%s", false),
-        $pg_start,
-        99,
-        array('urlname' => 'admin_pagemanager_format_listing'),
-        '<div class="tblholder" style="height: 300px; clip: rect(0px, auto, auto, 0px); overflow: auto;">
-        <table border="0" cellspacing="1" cellpadding="4">',
-        '  </table>
-         </div>'
-      );
-    echo $html;
-  }
-  
+				$parser = $template->makeParserText($tpl_code);
+				
+				$ns_list = '';
+				foreach ( $paths->nslist as $ns => $prefix ) 
+				{
+					// FIXME: Plugins need to specify whether they want Enano's regular PageProcessor
+					// to handle these pages, and whether such pages from namespaces created by plugins
+					// can be stored in the database or not.
+					if ( $ns == 'Special' || $ns == 'Admin' || $ns == 'Anonymous' )
+						continue;
+					$ns = htmlspecialchars($ns);
+					$prefix = htmlspecialchars($prefix);
+					if ( empty($prefix) )
+						$prefix = $lang->get('acppm_ns_article');
+					$sel = ( $dataset['namespace'] == $ns ) ? ' selected="selected"' : '';
+					$ns_list .= "  <option value=\"$ns\"$sel>$prefix</option>\n                ";
+				}
+				
+				$parser->assign_vars(array(
+						'PAGE_NAME' => htmlspecialchars($dataset['name']),
+						'PAGE_URLNAME' => htmlspecialchars($dataset['urlname']),
+						'NAMESPACE_LIST' => $ns_list,
+						'PATHS_KEY' => $pathskey
+					));
+				
+				$parser->assign_bool(array(
+						'comments_enabled' => ( $dataset['comments_on'] == 1 ),
+						'special' => ( $dataset['special'] == 1 ),
+						'visible' => ( $dataset['visible'] == 1 ),
+						'protected_off'   => ( $dataset['protected'] == 0 ),
+						'protected_on'    => ( $dataset['protected'] == 1 ),
+						'protected_semi'  => ( $dataset['protected'] == 2 ),
+						'wikimode_off'    => ( $dataset['wiki_mode'] == 0 ),
+						'wikimode_on'     => ( $dataset['wiki_mode'] == 1 ),
+						'wikimode_global' => ( $dataset['wiki_mode'] == 2 ),
+						'is_file'         => ( $dataset['namespace'] == 'File' )
+					));
+				
+				if ( isset($errors) )
+				{
+					echo '<div class="error-box">';
+					echo $lang->get('acppm_err_header');
+					echo '<ul>';
+					echo '<li>' . implode('</li><li>', $errors) . '</li>';
+					echo '</ul>';
+					echo '</div>';
+				}
+				
+				$form_action = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager", true);
+				
+				echo "<form action=\"$form_action\" method=\"post\">";
+				echo $parser->run();
+				echo "</form>";
+				
+				$show_select = false;
+				break;
+		}
+	}
+	
+	if ( $show_select )
+	{
+		echo '<p>' . $lang->get('acppm_hint') . '</p>';
+		
+		// Show the search form
+		
+		$form_action = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager", true);
+		echo "<form action=\"$form_action\" method=\"post\">";
+		echo $lang->get('acppm_lbl_field_search') . ' ';
+		echo $template->pagename_field('page_id') . ' ';
+		echo '<input type="hidden" name="action" value="select" />';
+		echo '<input type="submit" name="pid_search" value="' . $lang->get('search_btn_search') . '" />';
+		echo "</form>";
+		
+		// Grab all pages from the database and show a list of pages on the site
+		
+		echo '<h3>' . $lang->get('acppm_heading_select_page_from_list') . '</h3>';
+		echo '<p>' . $lang->get('acppm_hint_select_page_from_list') . '</p>';
+		
+		$q = $db->sql_query('SELECT COUNT(name) AS num_pages FROM ' . table_prefix . 'pages;');
+		if ( !$q )
+			$db->_die('PageManager doing initial page count');
+		list($num_pages) = $db->fetchrow_num();
+		$db->free_result();
+		
+		$pg_start = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0;
+		
+		$q = $db->sql_query('SELECT urlname, name, namespace, ' . $num_pages . ' AS num_pages, ' . $pg_start . ' AS offset FROM ' . table_prefix . 'pages ORDER BY name ASC;');
+		if ( !$q )
+			$db->_die('PageManager doing main select query for page list');
+		
+		// Paginate results
+		$html = paginate(
+				$q,
+				'{urlname}',
+				$num_pages,
+				makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager&offset=%s", false),
+				$pg_start,
+				99,
+				array('urlname' => 'admin_pagemanager_format_listing'),
+				'<div class="tblholder" style="height: 300px; clip: rect(0px, auto, auto, 0px); overflow: auto;">
+				<table border="0" cellspacing="1" cellpadding="4">',
+				'  </table>
+ 				</div>'
+			);
+		echo $html;
+	}
+	
 }
 
 function admin_pagemanager_format_listing($_, $row)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  
-  static $cell_count = 0;
-  static $td_class = 'row1';
-  static $run_count = 0;
-  static $num_pages_floor = false;
-  if ( !$num_pages_floor )
-  {
-    $num_pages_floor = $row['num_pages'];
-    while ( $num_pages_floor % 99 > 0 )
-      $num_pages_floor--;
-  }
-  $return = '';
-  $run_count++;
-  
-  $last_page = ( $row['offset'] == $num_pages_floor );
-  $last_run = ( ( $last_page && $run_count == $row['num_pages'] % 99 ) || $run_count == 99 );
-  if ( $cell_count == 0 )
-  {
-    $return .= "<tr>\n";
-  }
-  $title = get_page_title_ns($row['urlname'], $row['namespace']);
-  $pathskey = $paths->nslist[$row['namespace']] . $row['urlname'];
-  if ( isset($row['mode']) && $row['mode'] == 'edit' )
-  {
-    $url = makeUrlNS($row['namespace'], $row['urlname'], false, true) . '#do:edit';
-  }
-  else
-  {
-    $url = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager&action=select&page_id=$pathskey", true);
-  }
-  $url = '<a href="' . $url . '">' . htmlspecialchars($title) . '</a>';
-  $return .= '  <td class="' . $td_class . '" style="width: 33%;">' . $url . '</td>' . "\n";
-  $cell_count++;
-  if ( $cell_count == 3 && !$last_run )
-  {
-    $cell_count = 0;
-    $td_class = ( $td_class == 'row2' ) ? 'row1' : 'row2';
-    $return .= "</tr>\n";
-  }
-  else if ( $last_run )
-  {
-    while ( $cell_count < 3 )
-    {
-      $return .= "  <td class=\"{$td_class}\"></td>\n";
-      $cell_count++;
-    }
-    $return .= "</tr>\n";
-  }
-  return $return;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	
+	static $cell_count = 0;
+	static $td_class = 'row1';
+	static $run_count = 0;
+	static $num_pages_floor = false;
+	if ( !$num_pages_floor )
+	{
+		$num_pages_floor = $row['num_pages'];
+		while ( $num_pages_floor % 99 > 0 )
+			$num_pages_floor--;
+	}
+	$return = '';
+	$run_count++;
+	
+	$last_page = ( $row['offset'] == $num_pages_floor );
+	$last_run = ( ( $last_page && $run_count == $row['num_pages'] % 99 ) || $run_count == 99 );
+	if ( $cell_count == 0 )
+	{
+		$return .= "<tr>\n";
+	}
+	$title = get_page_title_ns($row['urlname'], $row['namespace']);
+	$pathskey = $paths->nslist[$row['namespace']] . $row['urlname'];
+	if ( isset($row['mode']) && $row['mode'] == 'edit' )
+	{
+		$url = makeUrlNS($row['namespace'], $row['urlname'], false, true) . '#do:edit';
+	}
+	else
+	{
+		$url = makeUrlNS('Special', 'Administration', "module={$paths->nslist['Admin']}PageManager&action=select&page_id=$pathskey", true);
+	}
+	$url = '<a href="' . $url . '">' . htmlspecialchars($title) . '</a>';
+	$return .= '  <td class="' . $td_class . '" style="width: 33%;">' . $url . '</td>' . "\n";
+	$cell_count++;
+	if ( $cell_count == 3 && !$last_run )
+	{
+		$cell_count = 0;
+		$td_class = ( $td_class == 'row2' ) ? 'row1' : 'row2';
+		$return .= "</tr>\n";
+	}
+	else if ( $last_run )
+	{
+		while ( $cell_count < 3 )
+		{
+			$return .= "  <td class=\"{$td_class}\"></td>\n";
+			$cell_count++;
+		}
+		$return .= "</tr>\n";
+	}
+	return $return;
 }
 
 ?>
--- a/plugins/admin/PluginManager.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/PluginManager.php	Sun Mar 28 23:10:46 2010 -0400
@@ -43,13 +43,13 @@
  <code>
  /**!info**
  {
-   "Plugin Name" : "Foo plugin",
-   "Plugin URI" : "http://fooplugin.enanocms.org/",
-   "Description" : "Some short descriptive text",
-   "Author" : "John Doe",
-   "Version" : "0.1",
-   "Author URI" : "http://yourdomain.com/",
-   "Version list" : [ "0.1-alpha1", "0.1-alpha2", "0.1-beta1", "0.1" ]
+ 	"Plugin Name" : "Foo plugin",
+ 	"Plugin URI" : "http://fooplugin.enanocms.org/",
+ 	"Description" : "Some short descriptive text",
+ 	"Author" : "John Doe",
+ 	"Version" : "0.1",
+ 	"Author URI" : "http://yourdomain.com/",
+ 	"Version list" : [ "0.1-alpha1", "0.1-alpha2", "0.1-beta1", "0.1" ]
  }
  **!* /
  </code>
@@ -57,21 +57,21 @@
  <code>
  /**!language**
  {
-   // each entry at this level should be an ISO-639-1 language code.
-   eng: {
-     // from here on in is the standard langauge file format
-     categories: [ 'meta', 'foo', 'bar' ],
-     strings: {
-       meta: {
-         foo: "Foo strings",
-         bar: "Bar strings"
-       },
-       foo: {
-         string_name: "string value",
-         string_name_2: "string value 2"
-       }
-     }
-   }
+ 	// each entry at this level should be an ISO-639-1 language code.
+ 	eng: {
+ 		// from here on in is the standard langauge file format
+ 		categories: [ 'meta', 'foo', 'bar' ],
+ 		strings: {
+ 			meta: {
+ 				foo: "Foo strings",
+ 				bar: "Bar strings"
+ 			},
+ 			foo: {
+ 				string_name: "string value",
+ 				string_name_2: "string value 2"
+ 			}
+ 		}
+ 	}
  }
  **!* / (once more, remove the space in there)
  </code>
@@ -80,7 +80,7 @@
  /**!install**
  
  CREATE TABLE {{TABLE_PREFIX}}foo_table(
-   ...
+ 	...
  )
  
  **!* /
@@ -106,402 +106,402 @@
 
 function page_Admin_PluginManager()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang, $cache;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  $plugin_list = $plugins->get_plugin_list(null, false);
-  
-  // Are we processing an AJAX request from the smartform?
-  if ( $paths->getParam(0) == 'action.json' )
-  {
-    // Set to application/json to discourage advertisement scripts
-    header('Content-Type: text/javascript');
-    
-    // Init return data
-    $return = array('mode' => 'error', 'error' => 'undefined');
-    
-    // Start parsing process
-    try
-    {
-      // Is the request properly sent on POST?
-      if ( isset($_POST['r']) )
-      {
-        // Try to decode the request
-        $request = enano_json_decode($_POST['r']);
-        // Is the action to perform specified?
-        if ( isset($request['mode']) )
-        {
-          switch ( $request['mode'] )
-          {
-            case 'install':
-              // did they specify a plugin to operate on?
-              if ( !isset($request['plugin']) )
-              {
-                $return = array(
-                  'mode' => 'error',
-                  'error' => 'No plugin specified.',
-                );
-                break;
-              }
-              if ( !isset($request['install_confirmed']) )
-              {
-                if ( $plugins->is_file_auth_plugin($request['plugin']) )
-                {
-                  $return = array(
-                    'confirm_title' => 'acppl_msg_confirm_authext_title',
-                    'confirm_body' => 'acppl_msg_confirm_authext_body',
-                    'need_confirm' => true,
-                    'success' => false
-                  );
-                  break;
-                }
-              }
-              
-              $return = $plugins->install_plugin($request['plugin'], $plugin_list);
-              break;
-            case 'upgrade':
-              // did they specify a plugin to operate on?
-              if ( !isset($request['plugin']) )
-              {
-                $return = array(
-                  'mode' => 'error',
-                  'error' => 'No plugin specified.',
-                );
-                break;
-              }
-              
-              $return = $plugins->upgrade_plugin($request['plugin'], $plugin_list);
-              break;
-            case 'reimport':
-              // did they specify a plugin to operate on?
-              if ( !isset($request['plugin']) )
-              {
-                $return = array(
-                  'mode' => 'error',
-                  'error' => 'No plugin specified.',
-                );
-                break;
-              }
-              
-              $return = $plugins->reimport_plugin_strings($request['plugin'], $plugin_list);
-              break;
-            case 'uninstall':
-              // did they specify a plugin to operate on?
-              if ( !isset($request['plugin']) )
-              {
-                $return = array(
-                  'mode' => 'error',
-                  'error' => 'No plugin specified.',
-                );
-                break;
-              }
-              
-              $return = $plugins->uninstall_plugin($request['plugin'], $plugin_list);
-              break;
-            case 'disable':
-            case 'enable':
-              // We're not in demo mode. Right?
-              if ( defined('ENANO_DEMO_MODE') )
-              {
-                $return = array(
-                    'mode' => 'error',
-                    'error' => $lang->get('acppl_err_demo_mode')
-                  );
-                break;
-              }
-              $flags_col = ( $request['mode'] == 'disable' ) ?
-                            "plugin_flags | "  . PLUGIN_DISABLED :
-                            "plugin_flags & ~" . PLUGIN_DISABLED;
-              // did they specify a plugin to operate on?
-              if ( !isset($request['plugin']) )
-              {
-                $return = array(
-                  'mode' => 'error',
-                  'error' => 'No plugin specified.',
-                );
-                break;
-              }
-              // is the plugin in the directory and already installed?
-              if ( !isset($plugin_list[$request['plugin']]) || (
-                  isset($plugin_list[$request['plugin']]) && !$plugin_list[$request['plugin']]['installed']
-                ))
-              {
-                $return = array(
-                  'mode' => 'error',
-                  'error' => 'Invalid plugin specified.',
-                );
-                break;
-              }
-              // get plugin id
-              $dataset =& $plugin_list[$request['plugin']];
-              if ( empty($dataset['plugin id']) )
-              {
-                $return = array(
-                  'mode' => 'error',
-                  'error' => 'Couldn\'t retrieve plugin ID.',
-                );
-                break;
-              }
-              
-              // log action
-              $time        = time();
-              $ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
-              $username_db = $db->escape($session->username);
-              $file_db     = $db->escape($request['plugin']);
-              // request['mode'] is TRUSTED - the case statement will only process if it is one of {enable,disable}.
-              $q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, page_text) VALUES\n"
-                                . "  ('security', 'plugin_{$request['mode']}', $time, '$ip_db', '$username_db', '$file_db');");
-              if ( !$q )
-                $db->_die();
-              
-              // perform update
-              $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_flags = $flags_col WHERE plugin_id = {$dataset['plugin id']};");
-              if ( !$q )
-                $db->die_json();
-              
-              $cache->purge('plugins');
-              
-              $return = array(
-                'success' => true
-              );
-              break;
-            case 'import':
-              // import all of the plugin_* config entries
-              $q = $db->sql_query('SELECT config_name, config_value FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';");
-              if ( !$q )
-                $db->die_json();
-              
-              while ( $row = $db->fetchrow($q) )
-              {
-                $plugin_filename = preg_replace('/^plugin_/', '', $row['config_name']);
-                if ( isset($plugin_list[$plugin_filename]) && !@$plugin_list[$plugin_filename]['installed'] )
-                {
-                  $return = $plugins->install_plugin($plugin_filename, $plugin_list);
-                  if ( !$return['success'] )
-                    break 2;
-                  if ( $row['config_value'] == '0' )
-                  {
-                    $fn_db = $db->escape($plugin_filename);
-                    $e = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_flags = plugin_flags | " . PLUGIN_DISABLED . " WHERE plugin_filename = '$fn_db';");
-                    if ( !$e )
-                      $db->die_json();
-                  }
-                }
-              }
-              $db->free_result($q);
-              
-              $q = $db->sql_query('DELETE FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';");
-              if ( !$q )
-                $db->die_json();
-              
-              $return = array('success' => true);
-              break;
-            default:
-              // The requested action isn't something this script knows how to do
-              $return = array(
-                'mode' => 'error',
-                'error' => 'Unknown mode "' . $request['mode'] . '" sent in request'
-              );
-              break;
-          }
-        }
-        else
-        {
-          // Didn't specify action
-          $return = array(
-            'mode' => 'error',
-            'error' => 'Missing key "mode" in request'
-          );
-        }
-      }
-      else
-      {
-        // Didn't send a request
-        $return = array(
-          'mode' => 'error',
-          'error' => 'No request specified'
-        );
-      }
-    }
-    catch ( Exception $e )
-    {
-      // Sent a request but it's not valid JSON
-      $return = array(
-          'mode' => 'error',
-          'error' => 'Invalid request - JSON parsing failed'
-        );
-    }
-    
-    echo enano_json_encode($return);
-    
-    return true;
-  }
-  
-  // Sort so that system plugins come last
-  ksort($plugin_list);
-  $plugin_list_sorted = array();
-  foreach ( $plugin_list as $filename => $data )
-  {
-    if ( !$data['system plugin'] )
-    {
-      $plugin_list_sorted[$filename] = $data;
-    }
-  }
-  ksort($plugin_list_sorted);
-  foreach ( $plugin_list as $filename => $data )
-  {
-    if ( $data['system plugin'] )
-    {
-      $plugin_list_sorted[$filename] = $data;
-    }
-  }
-  
-  $plugin_list =& $plugin_list_sorted;
-  
-  //
-  // Not a JSON request, output normal HTML interface
-  //
-  
-  // start printing things out
-  echo '<h3>' . $lang->get('acppl_heading_main') . '</h3>';
-  echo '<p>' . $lang->get('acppl_intro') . '</p>';
-  ?>
-  <div class="tblholder">
-    <table border="0" cellspacing="1" cellpadding="5">
-      <?php
-      $rowid = '2';
-      foreach ( $plugin_list as $filename => $data )
-      {
-        // print out all plugins
-        $rowid = ( $rowid == '1' ) ? '2' : '1';
-        $plugin_name = ( preg_match('/^[a-z0-9_]+$/', $data['plugin name']) ) ? $lang->get($data['plugin name']) : $data['plugin name'];
-        $plugin_basics = $lang->get('acppl_lbl_plugin_name', array(
-            'plugin' => $plugin_name,
-            'author' => $data['author']
-          ));
-        $color = '';
-        $buttons = '';
-        if ( $data['system plugin'] )
-        {
-          $status = $lang->get('acppl_lbl_status_system');
-        }
-        else if ( $data['installed'] && !( $data['status'] & PLUGIN_DISABLED ) && !( $data['status'] & PLUGIN_OUTOFDATE ) )
-        {
-          // this plugin is all good
-          $color = '_green';
-          $status = $lang->get('acppl_lbl_status_installed');
-          $buttons = 'reimport|uninstall|disable';
-        }
-        else if ( $data['installed'] && $data['status'] & PLUGIN_OUTOFDATE )
-        {
-          $color = '_red';
-          $status = $lang->get('acppl_lbl_status_need_upgrade');
-          $buttons = 'uninstall|upgrade';
-        }
-        else if ( $data['installed'] && $data['status'] & PLUGIN_DISABLED )
-        {
-          $color = '_red';
-          $status = $lang->get('acppl_lbl_status_disabled');
-          $buttons = 'uninstall|enable';
-        }
-        else
-        {
-          $color = '_red';
-          $status = $lang->get('acppl_lbl_status_uninstalled');
-          $buttons = 'install';
-        }
-        $uuid = md5($data['plugin name'] . $data['version'] . $filename);
-        $desc = ( preg_match('/^[a-z0-9_]+$/', $data['description']) ) ? $lang->get($data['description']) : $data['description'];
-        $desc = sanitize_html($desc);
-        
-        $additional = '';
-        
-        // filename
-        $additional .= '<b>' . $lang->get('acppl_lbl_filename') . '</b> ' . "{$filename}<br />";
-        
-        // plugin's site
-        $data['plugin uri'] = htmlspecialchars($data['plugin uri']);
-        $additional .= '<b>' . $lang->get('acppl_lbl_plugin_site') . '</b> ' . "<a href=\"{$data['plugin uri']}\">{$data['plugin uri']}</a><br />";
-        
-        // author's site
-        $data['author uri'] = htmlspecialchars($data['author uri']);
-        $additional .= '<b>' . $lang->get('acppl_lbl_author_site') . '</b> ' . "<a href=\"{$data['author uri']}\">{$data['author uri']}</a><br />";
-        
-        // version
-        $additional .= '<b>' . $lang->get('acppl_lbl_version') . '</b> ' . "{$data['version']}<br />";
-        
-        // installed version
-        if ( $data['status'] & PLUGIN_OUTOFDATE )
-        {
-          $additional .= '<b>' . $lang->get('acppl_lbl_installed_version') . '</b> ' . "{$data['version installed']}<br />";
-        }
-        
-        // build list of buttons
-        $buttons_html = '';
-        if ( !empty($buttons) )
-        {
-          $filename_js = addslashes($filename);
-          $buttons = explode('|', $buttons);
-          $colors = array(
-              'install' => 'green',
-              'disable' => 'blue',
-              'enable' => 'blue',
-              'upgrade' => 'green',
-              'uninstall' => 'red',
-              'reimport' => 'green'
-            );
-          foreach ( $buttons as $button )
-          {
-            $btnface = $lang->get("acppl_btn_$button");
-            $buttons_html .= "<a href=\"#\" onclick=\"ajaxPluginAction('$button', '$filename_js', this); return false;\" class=\"abutton_{$colors[$button]} abutton\">$btnface</a>\n";
-          }
-        }
-        
-        echo "<tr>
-                <td class=\"row{$rowid}$color\">
-                  <div style=\"float: right;\">
-                    <b>$status</b>
-                  </div>
-                  <div style=\"cursor: pointer;\" onclick=\"if ( !this.fx ) { load_component('jquery'); load_component('jquery-ui'); load_component('messagebox'); load_component('ajax'); this.fx = true; } $('#plugininfo_$uuid').toggle('blind', {}, 500);\">
-                    $plugin_basics
-                  </div>
-                  <span class=\"menuclear\"></span>
-                  <div id=\"plugininfo_$uuid\" style=\"display: none;\">
-                    $desc
-                    <div style=\"padding: 5px;\">
-                      $additional
-                      <div style=\"float: right; position: relative; top: -10px;\">
-                        $buttons_html
-                      </div>
-                      <span class=\"menuclear\"></span>
-                    </div>
-                  </div>
-                </td>
-              </tr>";
-      }
-      ?>
-    </table>
-  </div>
-  <?php
-  // are there still old style plugin entries?
-  $q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';");
-  if ( !$q )
-    $db->_die();
-  
-  $count = $db->numrows();
-  $db->free_result($q);
-  
-  if ( $count > 0 )
-  {
-    echo '<h3>' . $lang->get('acppl_msg_old_entries_title') . '</h3>';
-    echo '<p>' . $lang->get('acppl_msg_old_entries_body') . '</p>';
-    echo '<p><a class="abutton abutton_green" href="#" onclick="ajaxPluginAction(\'import\', \'\', false); return false;">' . $lang->get('acppl_btn_import_old') . '</a></p>';
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang, $cache;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	$plugin_list = $plugins->get_plugin_list(null, false);
+	
+	// Are we processing an AJAX request from the smartform?
+	if ( $paths->getParam(0) == 'action.json' )
+	{
+		// Set to application/json to discourage advertisement scripts
+		header('Content-Type: text/javascript');
+		
+		// Init return data
+		$return = array('mode' => 'error', 'error' => 'undefined');
+		
+		// Start parsing process
+		try
+		{
+			// Is the request properly sent on POST?
+			if ( isset($_POST['r']) )
+			{
+				// Try to decode the request
+				$request = enano_json_decode($_POST['r']);
+				// Is the action to perform specified?
+				if ( isset($request['mode']) )
+				{
+					switch ( $request['mode'] )
+					{
+						case 'install':
+							// did they specify a plugin to operate on?
+							if ( !isset($request['plugin']) )
+							{
+								$return = array(
+									'mode' => 'error',
+									'error' => 'No plugin specified.',
+								);
+								break;
+							}
+							if ( !isset($request['install_confirmed']) )
+							{
+								if ( $plugins->is_file_auth_plugin($request['plugin']) )
+								{
+									$return = array(
+										'confirm_title' => 'acppl_msg_confirm_authext_title',
+										'confirm_body' => 'acppl_msg_confirm_authext_body',
+										'need_confirm' => true,
+										'success' => false
+									);
+									break;
+								}
+							}
+							
+							$return = $plugins->install_plugin($request['plugin'], $plugin_list);
+							break;
+						case 'upgrade':
+							// did they specify a plugin to operate on?
+							if ( !isset($request['plugin']) )
+							{
+								$return = array(
+									'mode' => 'error',
+									'error' => 'No plugin specified.',
+								);
+								break;
+							}
+							
+							$return = $plugins->upgrade_plugin($request['plugin'], $plugin_list);
+							break;
+						case 'reimport':
+							// did they specify a plugin to operate on?
+							if ( !isset($request['plugin']) )
+							{
+								$return = array(
+									'mode' => 'error',
+									'error' => 'No plugin specified.',
+								);
+								break;
+							}
+							
+							$return = $plugins->reimport_plugin_strings($request['plugin'], $plugin_list);
+							break;
+						case 'uninstall':
+							// did they specify a plugin to operate on?
+							if ( !isset($request['plugin']) )
+							{
+								$return = array(
+									'mode' => 'error',
+									'error' => 'No plugin specified.',
+								);
+								break;
+							}
+							
+							$return = $plugins->uninstall_plugin($request['plugin'], $plugin_list);
+							break;
+						case 'disable':
+						case 'enable':
+							// We're not in demo mode. Right?
+							if ( defined('ENANO_DEMO_MODE') )
+							{
+								$return = array(
+										'mode' => 'error',
+										'error' => $lang->get('acppl_err_demo_mode')
+									);
+								break;
+							}
+							$flags_col = ( $request['mode'] == 'disable' ) ?
+														"plugin_flags | "  . PLUGIN_DISABLED :
+														"plugin_flags & ~" . PLUGIN_DISABLED;
+							// did they specify a plugin to operate on?
+							if ( !isset($request['plugin']) )
+							{
+								$return = array(
+									'mode' => 'error',
+									'error' => 'No plugin specified.',
+								);
+								break;
+							}
+							// is the plugin in the directory and already installed?
+							if ( !isset($plugin_list[$request['plugin']]) || (
+									isset($plugin_list[$request['plugin']]) && !$plugin_list[$request['plugin']]['installed']
+								))
+							{
+								$return = array(
+									'mode' => 'error',
+									'error' => 'Invalid plugin specified.',
+								);
+								break;
+							}
+							// get plugin id
+							$dataset =& $plugin_list[$request['plugin']];
+							if ( empty($dataset['plugin id']) )
+							{
+								$return = array(
+									'mode' => 'error',
+									'error' => 'Couldn\'t retrieve plugin ID.',
+								);
+								break;
+							}
+							
+							// log action
+							$time        = time();
+							$ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
+							$username_db = $db->escape($session->username);
+							$file_db     = $db->escape($request['plugin']);
+							// request['mode'] is TRUSTED - the case statement will only process if it is one of {enable,disable}.
+							$q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, page_text) VALUES\n"
+																. "  ('security', 'plugin_{$request['mode']}', $time, '$ip_db', '$username_db', '$file_db');");
+							if ( !$q )
+								$db->_die();
+							
+							// perform update
+							$q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_flags = $flags_col WHERE plugin_id = {$dataset['plugin id']};");
+							if ( !$q )
+								$db->die_json();
+							
+							$cache->purge('plugins');
+							
+							$return = array(
+								'success' => true
+							);
+							break;
+						case 'import':
+							// import all of the plugin_* config entries
+							$q = $db->sql_query('SELECT config_name, config_value FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';");
+							if ( !$q )
+								$db->die_json();
+							
+							while ( $row = $db->fetchrow($q) )
+							{
+								$plugin_filename = preg_replace('/^plugin_/', '', $row['config_name']);
+								if ( isset($plugin_list[$plugin_filename]) && !@$plugin_list[$plugin_filename]['installed'] )
+								{
+									$return = $plugins->install_plugin($plugin_filename, $plugin_list);
+									if ( !$return['success'] )
+										break 2;
+									if ( $row['config_value'] == '0' )
+									{
+										$fn_db = $db->escape($plugin_filename);
+										$e = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_flags = plugin_flags | " . PLUGIN_DISABLED . " WHERE plugin_filename = '$fn_db';");
+										if ( !$e )
+											$db->die_json();
+									}
+								}
+							}
+							$db->free_result($q);
+							
+							$q = $db->sql_query('DELETE FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';");
+							if ( !$q )
+								$db->die_json();
+							
+							$return = array('success' => true);
+							break;
+						default:
+							// The requested action isn't something this script knows how to do
+							$return = array(
+								'mode' => 'error',
+								'error' => 'Unknown mode "' . $request['mode'] . '" sent in request'
+							);
+							break;
+					}
+				}
+				else
+				{
+					// Didn't specify action
+					$return = array(
+						'mode' => 'error',
+						'error' => 'Missing key "mode" in request'
+					);
+				}
+			}
+			else
+			{
+				// Didn't send a request
+				$return = array(
+					'mode' => 'error',
+					'error' => 'No request specified'
+				);
+			}
+		}
+		catch ( Exception $e )
+		{
+			// Sent a request but it's not valid JSON
+			$return = array(
+					'mode' => 'error',
+					'error' => 'Invalid request - JSON parsing failed'
+				);
+		}
+		
+		echo enano_json_encode($return);
+		
+		return true;
+	}
+	
+	// Sort so that system plugins come last
+	ksort($plugin_list);
+	$plugin_list_sorted = array();
+	foreach ( $plugin_list as $filename => $data )
+	{
+		if ( !$data['system plugin'] )
+		{
+			$plugin_list_sorted[$filename] = $data;
+		}
+	}
+	ksort($plugin_list_sorted);
+	foreach ( $plugin_list as $filename => $data )
+	{
+		if ( $data['system plugin'] )
+		{
+			$plugin_list_sorted[$filename] = $data;
+		}
+	}
+	
+	$plugin_list =& $plugin_list_sorted;
+	
+	//
+	// Not a JSON request, output normal HTML interface
+	//
+	
+	// start printing things out
+	echo '<h3>' . $lang->get('acppl_heading_main') . '</h3>';
+	echo '<p>' . $lang->get('acppl_intro') . '</p>';
+	?>
+	<div class="tblholder">
+		<table border="0" cellspacing="1" cellpadding="5">
+			<?php
+			$rowid = '2';
+			foreach ( $plugin_list as $filename => $data )
+			{
+				// print out all plugins
+				$rowid = ( $rowid == '1' ) ? '2' : '1';
+				$plugin_name = ( preg_match('/^[a-z0-9_]+$/', $data['plugin name']) ) ? $lang->get($data['plugin name']) : $data['plugin name'];
+				$plugin_basics = $lang->get('acppl_lbl_plugin_name', array(
+						'plugin' => $plugin_name,
+						'author' => $data['author']
+					));
+				$color = '';
+				$buttons = '';
+				if ( $data['system plugin'] )
+				{
+					$status = $lang->get('acppl_lbl_status_system');
+				}
+				else if ( $data['installed'] && !( $data['status'] & PLUGIN_DISABLED ) && !( $data['status'] & PLUGIN_OUTOFDATE ) )
+				{
+					// this plugin is all good
+					$color = '_green';
+					$status = $lang->get('acppl_lbl_status_installed');
+					$buttons = 'reimport|uninstall|disable';
+				}
+				else if ( $data['installed'] && $data['status'] & PLUGIN_OUTOFDATE )
+				{
+					$color = '_red';
+					$status = $lang->get('acppl_lbl_status_need_upgrade');
+					$buttons = 'uninstall|upgrade';
+				}
+				else if ( $data['installed'] && $data['status'] & PLUGIN_DISABLED )
+				{
+					$color = '_red';
+					$status = $lang->get('acppl_lbl_status_disabled');
+					$buttons = 'uninstall|enable';
+				}
+				else
+				{
+					$color = '_red';
+					$status = $lang->get('acppl_lbl_status_uninstalled');
+					$buttons = 'install';
+				}
+				$uuid = md5($data['plugin name'] . $data['version'] . $filename);
+				$desc = ( preg_match('/^[a-z0-9_]+$/', $data['description']) ) ? $lang->get($data['description']) : $data['description'];
+				$desc = sanitize_html($desc);
+				
+				$additional = '';
+				
+				// filename
+				$additional .= '<b>' . $lang->get('acppl_lbl_filename') . '</b> ' . "{$filename}<br />";
+				
+				// plugin's site
+				$data['plugin uri'] = htmlspecialchars($data['plugin uri']);
+				$additional .= '<b>' . $lang->get('acppl_lbl_plugin_site') . '</b> ' . "<a href=\"{$data['plugin uri']}\">{$data['plugin uri']}</a><br />";
+				
+				// author's site
+				$data['author uri'] = htmlspecialchars($data['author uri']);
+				$additional .= '<b>' . $lang->get('acppl_lbl_author_site') . '</b> ' . "<a href=\"{$data['author uri']}\">{$data['author uri']}</a><br />";
+				
+				// version
+				$additional .= '<b>' . $lang->get('acppl_lbl_version') . '</b> ' . "{$data['version']}<br />";
+				
+				// installed version
+				if ( $data['status'] & PLUGIN_OUTOFDATE )
+				{
+					$additional .= '<b>' . $lang->get('acppl_lbl_installed_version') . '</b> ' . "{$data['version installed']}<br />";
+				}
+				
+				// build list of buttons
+				$buttons_html = '';
+				if ( !empty($buttons) )
+				{
+					$filename_js = addslashes($filename);
+					$buttons = explode('|', $buttons);
+					$colors = array(
+							'install' => 'green',
+							'disable' => 'blue',
+							'enable' => 'blue',
+							'upgrade' => 'green',
+							'uninstall' => 'red',
+							'reimport' => 'green'
+						);
+					foreach ( $buttons as $button )
+					{
+						$btnface = $lang->get("acppl_btn_$button");
+						$buttons_html .= "<a href=\"#\" onclick=\"ajaxPluginAction('$button', '$filename_js', this); return false;\" class=\"abutton_{$colors[$button]} abutton\">$btnface</a>\n";
+					}
+				}
+				
+				echo "<tr>
+								<td class=\"row{$rowid}$color\">
+									<div style=\"float: right;\">
+										<b>$status</b>
+									</div>
+									<div style=\"cursor: pointer;\" onclick=\"if ( !this.fx ) { load_component('jquery'); load_component('jquery-ui'); load_component('messagebox'); load_component('ajax'); this.fx = true; } $('#plugininfo_$uuid').toggle('blind', {}, 500);\">
+										$plugin_basics
+									</div>
+									<span class=\"menuclear\"></span>
+									<div id=\"plugininfo_$uuid\" style=\"display: none;\">
+										$desc
+										<div style=\"padding: 5px;\">
+											$additional
+											<div style=\"float: right; position: relative; top: -10px;\">
+												$buttons_html
+											</div>
+											<span class=\"menuclear\"></span>
+										</div>
+									</div>
+								</td>
+							</tr>";
+			}
+			?>
+		</table>
+	</div>
+	<?php
+	// are there still old style plugin entries?
+	$q = $db->sql_query('SELECT 1 FROM ' . table_prefix . "config WHERE config_name LIKE 'plugin_%';");
+	if ( !$q )
+		$db->_die();
+	
+	$count = $db->numrows();
+	$db->free_result($q);
+	
+	if ( $count > 0 )
+	{
+		echo '<h3>' . $lang->get('acppl_msg_old_entries_title') . '</h3>';
+		echo '<p>' . $lang->get('acppl_msg_old_entries_body') . '</p>';
+		echo '<p><a class="abutton abutton_green" href="#" onclick="ajaxPluginAction(\'import\', \'\', false); return false;">' . $lang->get('acppl_btn_import_old') . '</a></p>';
+	}
 }
--- a/plugins/admin/SecurityLog.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/SecurityLog.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,182 +13,182 @@
  
 function page_Admin_SecurityLog()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  // if ( defined('ENANO_DEMO_MODE') && substr($_SERVER['REMOTE_ADDR'], 0, 8) != '192.168.' )
-  // {
-  //   die('Security log is disabled in demo mode.');
-  // }
-  
-  echo '<h3>' . $lang->get('acpsl_heading_main') . '</h3>';
-  
-  // Not calling the real fetcher because we have to paginate the results
-  $offset = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0;
-  $q = $db->sql_query('SELECT COUNT(time_id) as num FROM '.table_prefix.'logs WHERE log_type=\'security\' GROUP BY log_id, time_id, log_type, action ORDER BY time_id DESC, action ASC;');
-  if ( !$q )
-    $db->_die();
-  $row = $db->fetchrow();
-  $db->free_result();
-  $count = intval($row['num']);
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	// if ( defined('ENANO_DEMO_MODE') && substr($_SERVER['REMOTE_ADDR'], 0, 8) != '192.168.' )
+	// {
+	//   die('Security log is disabled in demo mode.');
+	// }
+	
+	echo '<h3>' . $lang->get('acpsl_heading_main') . '</h3>';
+	
+	// Not calling the real fetcher because we have to paginate the results
+	$offset = ( isset($_GET['offset']) ) ? intval($_GET['offset']) : 0;
+	$q = $db->sql_query('SELECT COUNT(time_id) as num FROM '.table_prefix.'logs WHERE log_type=\'security\' GROUP BY log_id, time_id, log_type, action ORDER BY time_id DESC, action ASC;');
+	if ( !$q )
+		$db->_die();
+	$row = $db->fetchrow();
+	$db->free_result();
+	$count = intval($row['num']);
 
-  $l = 'SELECT action,date_string,author,author_uid,u.username,edit_summary,time_id,page_text FROM '.table_prefix."logs AS l\n"
-     . "  LEFT JOIN " . table_prefix . "users AS u\n"
-     . "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
-     . "  WHERE log_type='security'\n"
-     . "  ORDER BY time_id DESC, action ASC;";
-         
-  $q = $db->sql_query($l);
-  if ( !$q )
-    $db->_die();
-   
-  $html = paginate(
-      $q,
-      '{time_id}',
-      $count,
-      makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'SecurityLog&offset=%s'),
-      $offset,
-      50,
-      array('time_id' => 'seclog_format_inner'),
-      '<div class="tblholder" style="/* max-height: 500px; clip: rect(0px,auto,auto,0px); overflow: auto; */"><table border="0" cellspacing="1" cellpadding="4" width="100%">
-       <tr>
-         <th style="width: 60%;">' . $lang->get('acpsl_col_type') . '</th>
-         <th>' . $lang->get('acpsl_col_date') . '</th>
-         <th>' . $lang->get('acpsl_col_username') . '</th>
-         <th>' . $lang->get('acpsl_col_ip') . '</th>
-       </tr>',
-      '</table></div>'
-    );
-  
-  echo $html;
-  
+	$l = 'SELECT action,date_string,author,author_uid,u.username,edit_summary,time_id,page_text FROM '.table_prefix."logs AS l\n"
+ 		. "  LEFT JOIN " . table_prefix . "users AS u\n"
+ 		. "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
+ 		. "  WHERE log_type='security'\n"
+ 		. "  ORDER BY time_id DESC, action ASC;";
+ 				
+	$q = $db->sql_query($l);
+	if ( !$q )
+		$db->_die();
+ 	
+	$html = paginate(
+			$q,
+			'{time_id}',
+			$count,
+			makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'SecurityLog&offset=%s'),
+			$offset,
+			50,
+			array('time_id' => 'seclog_format_inner'),
+			'<div class="tblholder" style="/* max-height: 500px; clip: rect(0px,auto,auto,0px); overflow: auto; */"><table border="0" cellspacing="1" cellpadding="4" width="100%">
+ 			<tr>
+ 				<th style="width: 60%;">' . $lang->get('acpsl_col_type') . '</th>
+ 				<th>' . $lang->get('acpsl_col_date') . '</th>
+ 				<th>' . $lang->get('acpsl_col_username') . '</th>
+ 				<th>' . $lang->get('acpsl_col_ip') . '</th>
+ 			</tr>',
+			'</table></div>'
+		);
+	
+	echo $html;
+	
 }
 
 function get_security_log($num = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  if ( $session->auth_level < USER_LEVEL_ADMIN )
-  {
-    $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'seclog_unauth\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', \'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
-    if ( !$q )
-      $db->_die();
-    die('Security log: unauthorized attempt to fetch. Call has been logged and reported to the administrators.');
-  }
-  
-  $return = '<div class="tblholder" style="/* max-height: 500px; clip: rect(0px,auto,auto,0px); overflow: auto; */"><table border="0" cellspacing="1" cellpadding="4" width="100%">';
-  $cls = 'row2';                                                                                               
-  $return .= '<tr><th style="width: 60%;">' . $lang->get('acpsl_col_type') . '</th><th>' . $lang->get('acpsl_col_date') . '</th><th>' . $lang->get('acpsl_col_username') . '</th><th>' . $lang->get('acpsl_col_ip') . '</th></tr>';
-  $hash = sha1(microtime());
-  if ( defined('ENANO_DEMO_MODE') )
-  {
-    require('config.php');
-    $hash = md5($dbpasswd);
-    unset($dbname, $dbhost, $dbuser, $dbpasswd);
-    unset($dbname, $dbhost, $dbuser, $dbpasswd); // PHP5 Zend bug
-  }
-  // if ( defined('ENANO_DEMO_MODE') && !isset($_GET[ $hash ]) && substr($_SERVER['REMOTE_ADDR'], 0, 8) != '192.168.' )
-  // {
-  //   $return .= '<tr><td class="row1" colspan="4">Logs are recorded but not displayed for privacy purposes in the demo.</td></tr>';
-  // }
-  // else
-  // {
-    $limit_clause = is_int($num) ? " LIMIT $num" : '';
-    $l = 'SELECT action,date_string,author,author_uid,u.username,edit_summary,time_id,page_text FROM '.table_prefix."logs AS l\n"
-         . "  LEFT JOIN " . table_prefix . "users AS u\n"
-         . "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
-         . "  WHERE log_type='security'\n"
-         . "  ORDER BY time_id DESC, action ASC{$limit_clause};";
-    
-    $q = $db->sql_query($l);
-    while($r = $db->fetchrow($q))
-    {
-      $return .= seclog_format_inner($r);
-    }
-    $db->free_result();
-  // }
-  $return .= '</table></div>';
-  
-  return $return;
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	if ( $session->auth_level < USER_LEVEL_ADMIN )
+	{
+		$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid) VALUES(\'security\',\'seclog_unauth\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', \'' . $db->escape($session->username) . '\', ' . $session->user_id . ');');
+		if ( !$q )
+			$db->_die();
+		die('Security log: unauthorized attempt to fetch. Call has been logged and reported to the administrators.');
+	}
+	
+	$return = '<div class="tblholder" style="/* max-height: 500px; clip: rect(0px,auto,auto,0px); overflow: auto; */"><table border="0" cellspacing="1" cellpadding="4" width="100%">';
+	$cls = 'row2';                                                                                               
+	$return .= '<tr><th style="width: 60%;">' . $lang->get('acpsl_col_type') . '</th><th>' . $lang->get('acpsl_col_date') . '</th><th>' . $lang->get('acpsl_col_username') . '</th><th>' . $lang->get('acpsl_col_ip') . '</th></tr>';
+	$hash = sha1(microtime());
+	if ( defined('ENANO_DEMO_MODE') )
+	{
+		require('config.php');
+		$hash = md5($dbpasswd);
+		unset($dbname, $dbhost, $dbuser, $dbpasswd);
+		unset($dbname, $dbhost, $dbuser, $dbpasswd); // PHP5 Zend bug
+	}
+	// if ( defined('ENANO_DEMO_MODE') && !isset($_GET[ $hash ]) && substr($_SERVER['REMOTE_ADDR'], 0, 8) != '192.168.' )
+	// {
+	//   $return .= '<tr><td class="row1" colspan="4">Logs are recorded but not displayed for privacy purposes in the demo.</td></tr>';
+	// }
+	// else
+	// {
+		$limit_clause = is_int($num) ? " LIMIT $num" : '';
+		$l = 'SELECT action,date_string,author,author_uid,u.username,edit_summary,time_id,page_text FROM '.table_prefix."logs AS l\n"
+ 				. "  LEFT JOIN " . table_prefix . "users AS u\n"
+ 				. "    ON ( u.user_id = l.author_uid OR u.user_id IS NULL )\n"
+ 				. "  WHERE log_type='security'\n"
+ 				. "  ORDER BY time_id DESC, action ASC{$limit_clause};";
+		
+		$q = $db->sql_query($l);
+		while($r = $db->fetchrow($q))
+		{
+			$return .= seclog_format_inner($r);
+		}
+		$db->free_result();
+	// }
+	$return .= '</table></div>';
+	
+	return $return;
 }
 
 function seclog_format_inner($r, $f = false)
 {
-  if ( is_array($f) )
-  {
-    unset($r);
-    $r =& $f;
-  }
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  $return = '';
-  static $cls = 'row2';
-  if ( substr($_SERVER['REMOTE_ADDR'], 0, 8) != '192.168.' && defined('ENANO_DEMO_MODE') )
-  {
-    $r['edit_summary'] = preg_replace('/([0-9])/', 'x', $r['edit_summary']);
-  }
-  if ( $r['action'] == 'illegal_page' )
-  {
-    list($illegal_id, $illegal_ns) = unserialize($r['page_text']);
-    $url = makeUrlNS($illegal_ns, $illegal_id, false, true);
-    $title = get_page_title_ns($illegal_id, $illegal_ns);
-    $class = ( isPage($paths->nslist[$illegal_ns] . $illegal_id) ) ? '' : ' class="wikilink-nonexistent"';
-    $illegal_link = '<a href="' . $url . '"' . $class . ' onclick="window.open(this.href); return false;">' . $title . '</a>';
-  }
-  else if ( $r['action'] == 'plugin_enable' || $r['action'] == 'plugin_disable' )
-  {
-    $r['page_text'] = htmlspecialchars($r['page_text']);
-  }
-  $cls = ( $cls == 'row2' ) ? 'row1' : 'row2';
-  $return .= '<tr><td class="'.$cls.'">';
-  switch($r['action'])
-  {
-    case "admin_auth_good" : $return .= $lang->get('acpsl_entry_admin_auth_good'  , array('level' => $session->userlevel_to_string( intval($r['page_text']) ))); break;
-    case "admin_auth_bad"  : $return .= $lang->get('acpsl_entry_admin_auth_bad'   , array('level' => $session->userlevel_to_string( intval($r['page_text']) ))); break;
-    case "activ_good"      : $return .= $lang->get('acpsl_entry_activ_good')      ; break;
-    case "auth_good"       : $return .= $lang->get('acpsl_entry_auth_good')       ; break;
-    case "activ_bad"       : $return .= $lang->get('acpsl_entry_activ_bad')       ; break;
-    case "auth_bad"        : $return .= $lang->get('acpsl_entry_auth_bad')        ; break;
-    case "sql_inject"      : $return .= $lang->get('acpsl_entry_sql_inject'       , array('query' => htmlspecialchars($r['page_text']))); break;
-    case "db_backup"       : $return .= $lang->get('acpsl_entry_db_backup'        , array('tables' => $r['page_text']))       ; break;
-    case "install_enano"   : $return .= $lang->get('acpsl_entry_install_enano'    , array('version' => $r['page_text'])); break; // version is in $r['page_text']
-    case "upgrade_enano"   : $return .= $lang->get('acpsl_entry_upgrade_enano'    , array('version' => $r['page_text'])); break; // version is in $r['page_text']
-    case "illegal_page"    : $return .= $lang->get('acpsl_entry_illegal_page'     , array('illegal_link' => $illegal_link))    ; break;
-    case "upload_enable"   : $return .= $lang->get('acpsl_entry_upload_enable')   ; break;
-    case "upload_disable"  : $return .= $lang->get('acpsl_entry_upload_disable')  ; break;
-    case "magick_enable"   : $return .= $lang->get('acpsl_entry_magick_enable')   ; break;
-    case "magick_disable"  : $return .= $lang->get('acpsl_entry_magick_disable')  ; break;
-    case "filehist_enable" : $return .= $lang->get('acpsl_entry_filehist_enable') ; break;
-    case "filehist_disable": $return .= $lang->get('acpsl_entry_filehist_disable'); break;
-    case "magick_path"     : $return .= $lang->get('acpsl_entry_magick_path')     ; break;
-    case "plugin_disable"  : $return .= $lang->get('acpsl_entry_plugin_disable'   , array('plugin' => $r['page_text'])); break;
-    case "plugin_enable"   : $return .= $lang->get('acpsl_entry_plugin_enable'    , array('plugin' => $r['page_text'])); break;
-    case "plugin_install"  : $return .= $lang->get('acpsl_entry_plugin_install'   , array('plugin' => $r['page_text'])); break;
-    case "plugin_uninstall": $return .= $lang->get('acpsl_entry_plugin_uninstall' , array('plugin' => $r['page_text'])); break;
-    case "plugin_upgrade"  : $return .= $lang->get('acpsl_entry_plugin_upgrade'   , array('plugin' => $r['page_text'])); break;
-    case "seclog_unauth"   : $return .= $lang->get('acpsl_entry_seclog_unauth')   ; break;
-    case "u_from_admin"    : $return .= $lang->get('acpsl_entry_u_from_admin'     , array('username' => $r['page_text'])); break;
-    case "u_from_mod"      : $return .= $lang->get('acpsl_entry_u_from_mod'       , array('username' => $r['page_text'])); break;
-    case "u_to_admin"      : $return .= $lang->get('acpsl_entry_u_to_admin'       , array('username' => $r['page_text'])); break;
-    case "u_to_mod"        : $return .= $lang->get('acpsl_entry_u_to_mod'         , array('username' => $r['page_text'])); break;
-    case "view_comment_ip" : $return .= $lang->get('acpsl_entry_view_comment_ip'  , array('username' => htmlspecialchars($r['page_text']))); break;
-  }
-  $author_bit = '<span style="';
-  $rank_info = $session->get_user_rank($r['author_uid']);
-  $author_bit .= $rank_info['rank_style'];
-  $author_bit .= '">';
-  $author_bit .= $r['author_uid'] > 1 && !empty($r['username']) ? htmlspecialchars($r['username']) : htmlspecialchars($r['author']);
-  $author_bit .= '</span>';
-  $return .= '</td><td class="'.$cls.'">'.enano_date(ED_DATE | ED_TIME, $r['time_id']).'</td><td class="'.$cls.'">'.$author_bit.'</td><td class="'.$cls.'" style="cursor: pointer;" onclick="ajaxReverseDNS(this);" title="' . $lang->get('acpsl_tip_reverse_dns') . '">'.$r['edit_summary'].'</td></tr>';
-  return $return;
+	if ( is_array($f) )
+	{
+		unset($r);
+		$r =& $f;
+	}
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	$return = '';
+	static $cls = 'row2';
+	if ( substr($_SERVER['REMOTE_ADDR'], 0, 8) != '192.168.' && defined('ENANO_DEMO_MODE') )
+	{
+		$r['edit_summary'] = preg_replace('/([0-9])/', 'x', $r['edit_summary']);
+	}
+	if ( $r['action'] == 'illegal_page' )
+	{
+		list($illegal_id, $illegal_ns) = unserialize($r['page_text']);
+		$url = makeUrlNS($illegal_ns, $illegal_id, false, true);
+		$title = get_page_title_ns($illegal_id, $illegal_ns);
+		$class = ( isPage($paths->nslist[$illegal_ns] . $illegal_id) ) ? '' : ' class="wikilink-nonexistent"';
+		$illegal_link = '<a href="' . $url . '"' . $class . ' onclick="window.open(this.href); return false;">' . $title . '</a>';
+	}
+	else if ( $r['action'] == 'plugin_enable' || $r['action'] == 'plugin_disable' )
+	{
+		$r['page_text'] = htmlspecialchars($r['page_text']);
+	}
+	$cls = ( $cls == 'row2' ) ? 'row1' : 'row2';
+	$return .= '<tr><td class="'.$cls.'">';
+	switch($r['action'])
+	{
+		case "admin_auth_good" : $return .= $lang->get('acpsl_entry_admin_auth_good'  , array('level' => $session->userlevel_to_string( intval($r['page_text']) ))); break;
+		case "admin_auth_bad"  : $return .= $lang->get('acpsl_entry_admin_auth_bad'   , array('level' => $session->userlevel_to_string( intval($r['page_text']) ))); break;
+		case "activ_good"      : $return .= $lang->get('acpsl_entry_activ_good')      ; break;
+		case "auth_good"       : $return .= $lang->get('acpsl_entry_auth_good')       ; break;
+		case "activ_bad"       : $return .= $lang->get('acpsl_entry_activ_bad')       ; break;
+		case "auth_bad"        : $return .= $lang->get('acpsl_entry_auth_bad')        ; break;
+		case "sql_inject"      : $return .= $lang->get('acpsl_entry_sql_inject'       , array('query' => htmlspecialchars($r['page_text']))); break;
+		case "db_backup"       : $return .= $lang->get('acpsl_entry_db_backup'        , array('tables' => $r['page_text']))       ; break;
+		case "install_enano"   : $return .= $lang->get('acpsl_entry_install_enano'    , array('version' => $r['page_text'])); break; // version is in $r['page_text']
+		case "upgrade_enano"   : $return .= $lang->get('acpsl_entry_upgrade_enano'    , array('version' => $r['page_text'])); break; // version is in $r['page_text']
+		case "illegal_page"    : $return .= $lang->get('acpsl_entry_illegal_page'     , array('illegal_link' => $illegal_link))    ; break;
+		case "upload_enable"   : $return .= $lang->get('acpsl_entry_upload_enable')   ; break;
+		case "upload_disable"  : $return .= $lang->get('acpsl_entry_upload_disable')  ; break;
+		case "magick_enable"   : $return .= $lang->get('acpsl_entry_magick_enable')   ; break;
+		case "magick_disable"  : $return .= $lang->get('acpsl_entry_magick_disable')  ; break;
+		case "filehist_enable" : $return .= $lang->get('acpsl_entry_filehist_enable') ; break;
+		case "filehist_disable": $return .= $lang->get('acpsl_entry_filehist_disable'); break;
+		case "magick_path"     : $return .= $lang->get('acpsl_entry_magick_path')     ; break;
+		case "plugin_disable"  : $return .= $lang->get('acpsl_entry_plugin_disable'   , array('plugin' => $r['page_text'])); break;
+		case "plugin_enable"   : $return .= $lang->get('acpsl_entry_plugin_enable'    , array('plugin' => $r['page_text'])); break;
+		case "plugin_install"  : $return .= $lang->get('acpsl_entry_plugin_install'   , array('plugin' => $r['page_text'])); break;
+		case "plugin_uninstall": $return .= $lang->get('acpsl_entry_plugin_uninstall' , array('plugin' => $r['page_text'])); break;
+		case "plugin_upgrade"  : $return .= $lang->get('acpsl_entry_plugin_upgrade'   , array('plugin' => $r['page_text'])); break;
+		case "seclog_unauth"   : $return .= $lang->get('acpsl_entry_seclog_unauth')   ; break;
+		case "u_from_admin"    : $return .= $lang->get('acpsl_entry_u_from_admin'     , array('username' => $r['page_text'])); break;
+		case "u_from_mod"      : $return .= $lang->get('acpsl_entry_u_from_mod'       , array('username' => $r['page_text'])); break;
+		case "u_to_admin"      : $return .= $lang->get('acpsl_entry_u_to_admin'       , array('username' => $r['page_text'])); break;
+		case "u_to_mod"        : $return .= $lang->get('acpsl_entry_u_to_mod'         , array('username' => $r['page_text'])); break;
+		case "view_comment_ip" : $return .= $lang->get('acpsl_entry_view_comment_ip'  , array('username' => htmlspecialchars($r['page_text']))); break;
+	}
+	$author_bit = '<span style="';
+	$rank_info = $session->get_user_rank($r['author_uid']);
+	$author_bit .= $rank_info['rank_style'];
+	$author_bit .= '">';
+	$author_bit .= $r['author_uid'] > 1 && !empty($r['username']) ? htmlspecialchars($r['username']) : htmlspecialchars($r['author']);
+	$author_bit .= '</span>';
+	$return .= '</td><td class="'.$cls.'">'.enano_date(ED_DATE | ED_TIME, $r['time_id']).'</td><td class="'.$cls.'">'.$author_bit.'</td><td class="'.$cls.'" style="cursor: pointer;" onclick="ajaxReverseDNS(this);" title="' . $lang->get('acpsl_tip_reverse_dns') . '">'.$r['edit_summary'].'</td></tr>';
+	return $return;
 }
 
 ?>
--- a/plugins/admin/ThemeManager.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/ThemeManager.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,416 +13,416 @@
 
 function page_Admin_ThemeManager($force_no_json = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $cache;
-  
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  $system_themes =& $template->system_themes;
-  
-  // Obtain the list of themes (both available and already installed) and the styles available for each
-  $dh = @opendir(ENANO_ROOT . '/themes');
-  if ( !$dh )
-    die('Couldn\'t open themes directory');
-  $themes = array();
-  while ( $dr = @readdir($dh) )
-  {
-    if ( $dr == '.' || $dr == '..' )
-      continue;
-    if ( !is_dir(ENANO_ROOT . "/themes/$dr") )
-      continue;
-    if ( !file_exists(ENANO_ROOT . "/themes/$dr/theme.cfg") || !is_dir(ENANO_ROOT . "/themes/$dr/css") )
-      continue;
-    $cdh = @opendir(ENANO_ROOT . "/themes/$dr/css");
-    if ( !$cdh )
-      continue;
-    
-    require(ENANO_ROOT . "/themes/$dr/theme.cfg");
-    global $theme;
-    
-    $themes[$dr] = array(
-        'css' => array(),
-        'theme_name' => $theme['theme_name']
-      );
-    while ( $cdr = @readdir($cdh) )
-    {
-      if ( $cdr == '.' || $cdr == '..' )
-        continue;
-      if ( preg_match('/\.css$/i', $cdr) )
-        $themes[$dr]['css'][] = substr($cdr, 0, -4);
-    }
-  }
-  
-  // Decide which themes are not installed
-  $installable = array_flip(array_keys($themes));
-  // FIXME: sanitize directory names or check with preg_match()
-  $where_clause = 'theme_id = \'' . implode('\' OR theme_id = \'', array_flip($installable)) . '\'';
-  $q = $db->sql_query('SELECT theme_id, theme_name, enabled FROM ' . table_prefix . "themes WHERE $where_clause;");
-  if ( !$q )
-    $db->_die();
-  
-  while ( $row = $db->fetchrow() )
-  {
-    $tid =& $row['theme_id'];
-    unset($installable[$tid]);
-    $themes[$tid]['theme_name'] = $row['theme_name'];
-    $themes[$tid]['enabled'] = ( $row['enabled'] == 1 );
-  }
-  
-  foreach ( $system_themes as $st )
-  {
-    unset($installable[$st]);
-  }
-  
-  $installable = array_flip($installable);
-  
-  // AJAX code
-  if ( $paths->getParam(0) === 'action.json' && !$force_no_json )
-  {
-    return ajaxServlet_Admin_ThemeManager($themes);
-  }
-  
-  // List installed themes
-  ?>
-  <div style="float: right;">
-    <a href="#" id="systheme_toggler" onclick="ajaxToggleSystemThemes(); return false;"><?php echo $lang->get('acptm_btn_system_themes_show'); ?></a>
-  </div>
-  <?php
-  echo '<h3>' . $lang->get('acptm_heading_edit_themes') . '</h3>';
-  echo '<div id="theme_list_edit">';
-  foreach ( $themes as $theme_id => $theme_data )
-  {
-    if ( in_array($theme_id, $installable) )
-      continue;
-    if ( file_exists(ENANO_ROOT . "/themes/$theme_id/preview.png") )
-    {
-      $preview_path = scriptPath . "/themes/$theme_id/preview.png";
-    }
-    else
-    {
-      $preview_path = scriptPath . "/images/themepreview.png";
-    }
-    $d = ( @$theme_data['enabled'] ) ? '' : ' themebutton_theme_disabled';
-    $st = ( in_array($theme_id, $system_themes) ) ? ' themebutton_theme_system' : '';
-    echo '<div class="themebutton' . $st . '' . $d . '" id="themebtn_edit_' . $theme_id . '" style="background-image: url(' . $preview_path . ');">';
-    if ( in_array($theme_id, $system_themes) )
-    {
-      echo   '<a class="tb-inner" href="#" onclick="return false;">
-                ' . $lang->get('acptm_btn_theme_system') . '
-                <span class="themename">' . htmlspecialchars($theme_data['theme_name']) . '</span>
-              </a>';
-    }
-    else
-    {
-      echo   '<a class="tb-inner" href="#" onclick="ajaxEditTheme(\'' . $theme_id . '\'); return false;">
-                ' . $lang->get('acptm_btn_theme_edit') . '
-                <span class="themename">' . htmlspecialchars($theme_data['theme_name']) . '</span>
-              </a>';
-    }
-    echo '</div>';
-  }
-  echo '</div>';
-  echo '<span class="menuclear"></span>';
-  
-  if ( count($installable) > 0 )
-  {
-    echo '<h3>' . $lang->get('acptm_heading_install_themes') . '</h3>';
-  
-    echo '<div id="theme_list_install">';
-    foreach ( $installable as $i => $theme_id )
-    {
-      if ( file_exists(ENANO_ROOT . "/themes/$theme_id/preview.png") )
-      {
-        $preview_path = scriptPath . "/themes/$theme_id/preview.png";
-      }
-      else
-      {
-        $preview_path = scriptPath . "/images/themepreview.png";
-      }
-      echo '<div class="themebutton" id="themebtn_install_' . $theme_id . '" enano:themename="' . htmlspecialchars($themes[$theme_id]['theme_name']) . '" style="background-image: url(' . $preview_path . ');">';
-      echo   '<a class="tb-inner" href="#" onclick="ajaxInstallTheme(\'' . $theme_id . '\'); return false;">
-                ' . $lang->get('acptm_btn_theme_install') . '
-                <span class="themename">' . htmlspecialchars($themes[$theme_id]['theme_name']) . '</span>
-              </a>';
-      echo '</div>';
-    }
-    echo '</div>';
-    echo '<span class="menuclear"></span>';
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $cache;
+	
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	$system_themes =& $template->system_themes;
+	
+	// Obtain the list of themes (both available and already installed) and the styles available for each
+	$dh = @opendir(ENANO_ROOT . '/themes');
+	if ( !$dh )
+		die('Couldn\'t open themes directory');
+	$themes = array();
+	while ( $dr = @readdir($dh) )
+	{
+		if ( $dr == '.' || $dr == '..' )
+			continue;
+		if ( !is_dir(ENANO_ROOT . "/themes/$dr") )
+			continue;
+		if ( !file_exists(ENANO_ROOT . "/themes/$dr/theme.cfg") || !is_dir(ENANO_ROOT . "/themes/$dr/css") )
+			continue;
+		$cdh = @opendir(ENANO_ROOT . "/themes/$dr/css");
+		if ( !$cdh )
+			continue;
+		
+		require(ENANO_ROOT . "/themes/$dr/theme.cfg");
+		global $theme;
+		
+		$themes[$dr] = array(
+				'css' => array(),
+				'theme_name' => $theme['theme_name']
+			);
+		while ( $cdr = @readdir($cdh) )
+		{
+			if ( $cdr == '.' || $cdr == '..' )
+				continue;
+			if ( preg_match('/\.css$/i', $cdr) )
+				$themes[$dr]['css'][] = substr($cdr, 0, -4);
+		}
+	}
+	
+	// Decide which themes are not installed
+	$installable = array_flip(array_keys($themes));
+	// FIXME: sanitize directory names or check with preg_match()
+	$where_clause = 'theme_id = \'' . implode('\' OR theme_id = \'', array_flip($installable)) . '\'';
+	$q = $db->sql_query('SELECT theme_id, theme_name, enabled FROM ' . table_prefix . "themes WHERE $where_clause;");
+	if ( !$q )
+		$db->_die();
+	
+	while ( $row = $db->fetchrow() )
+	{
+		$tid =& $row['theme_id'];
+		unset($installable[$tid]);
+		$themes[$tid]['theme_name'] = $row['theme_name'];
+		$themes[$tid]['enabled'] = ( $row['enabled'] == 1 );
+	}
+	
+	foreach ( $system_themes as $st )
+	{
+		unset($installable[$st]);
+	}
+	
+	$installable = array_flip($installable);
+	
+	// AJAX code
+	if ( $paths->getParam(0) === 'action.json' && !$force_no_json )
+	{
+		return ajaxServlet_Admin_ThemeManager($themes);
+	}
+	
+	// List installed themes
+	?>
+	<div style="float: right;">
+		<a href="#" id="systheme_toggler" onclick="ajaxToggleSystemThemes(); return false;"><?php echo $lang->get('acptm_btn_system_themes_show'); ?></a>
+	</div>
+	<?php
+	echo '<h3>' . $lang->get('acptm_heading_edit_themes') . '</h3>';
+	echo '<div id="theme_list_edit">';
+	foreach ( $themes as $theme_id => $theme_data )
+	{
+		if ( in_array($theme_id, $installable) )
+			continue;
+		if ( file_exists(ENANO_ROOT . "/themes/$theme_id/preview.png") )
+		{
+			$preview_path = scriptPath . "/themes/$theme_id/preview.png";
+		}
+		else
+		{
+			$preview_path = scriptPath . "/images/themepreview.png";
+		}
+		$d = ( @$theme_data['enabled'] ) ? '' : ' themebutton_theme_disabled';
+		$st = ( in_array($theme_id, $system_themes) ) ? ' themebutton_theme_system' : '';
+		echo '<div class="themebutton' . $st . '' . $d . '" id="themebtn_edit_' . $theme_id . '" style="background-image: url(' . $preview_path . ');">';
+		if ( in_array($theme_id, $system_themes) )
+		{
+			echo   '<a class="tb-inner" href="#" onclick="return false;">
+								' . $lang->get('acptm_btn_theme_system') . '
+								<span class="themename">' . htmlspecialchars($theme_data['theme_name']) . '</span>
+							</a>';
+		}
+		else
+		{
+			echo   '<a class="tb-inner" href="#" onclick="ajaxEditTheme(\'' . $theme_id . '\'); return false;">
+								' . $lang->get('acptm_btn_theme_edit') . '
+								<span class="themename">' . htmlspecialchars($theme_data['theme_name']) . '</span>
+							</a>';
+		}
+		echo '</div>';
+	}
+	echo '</div>';
+	echo '<span class="menuclear"></span>';
+	
+	if ( count($installable) > 0 )
+	{
+		echo '<h3>' . $lang->get('acptm_heading_install_themes') . '</h3>';
+	
+		echo '<div id="theme_list_install">';
+		foreach ( $installable as $i => $theme_id )
+		{
+			if ( file_exists(ENANO_ROOT . "/themes/$theme_id/preview.png") )
+			{
+				$preview_path = scriptPath . "/themes/$theme_id/preview.png";
+			}
+			else
+			{
+				$preview_path = scriptPath . "/images/themepreview.png";
+			}
+			echo '<div class="themebutton" id="themebtn_install_' . $theme_id . '" enano:themename="' . htmlspecialchars($themes[$theme_id]['theme_name']) . '" style="background-image: url(' . $preview_path . ');">';
+			echo   '<a class="tb-inner" href="#" onclick="ajaxInstallTheme(\'' . $theme_id . '\'); return false;">
+								' . $lang->get('acptm_btn_theme_install') . '
+								<span class="themename">' . htmlspecialchars($themes[$theme_id]['theme_name']) . '</span>
+							</a>';
+			echo '</div>';
+		}
+		echo '</div>';
+		echo '<span class="menuclear"></span>';
+	}
 }
 
 function ajaxServlet_Admin_ThemeManager(&$themes)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  global $cache;
-  
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  if ( !isset($_POST['r']) )
-    return false;
-  
-  try
-  {
-    $request = enano_json_decode($_POST['r']);
-  }
-  catch ( Exception $e )
-  {
-    die('Exception in JSON parser, probably invalid input.');
-  }
-  
-  if ( !isset($request['mode']) )
-  {
-    die('No mode specified in JSON request.');
-  }
-  
-  switch ( $request['mode'] )
-  {
-    case 'fetch_theme':
-      $theme_id = $db->escape($request['theme_id']);
-      if ( empty($theme_id) )
-        die('Invalid theme_id');
-      
-      $q = $db->sql_query("SELECT theme_id, theme_name, default_style, enabled, group_policy, group_list FROM " . table_prefix . "themes WHERE theme_id = '$theme_id';");
-      if ( !$q )
-        $db->die_json();
-      
-      if ( $db->numrows() < 1 )
-        die('BUG: no theme with that theme_id installed.');
-      
-      $row = $db->fetchrow();
-      $row['enabled'] = ( $row['enabled'] == 1 );
-      $row['css'] = @$themes[$theme_id]['css'];
-      $row['default_style'] = preg_replace('/\.css$/', '', $row['default_style']);
-      $row['is_default'] = ( getConfig('theme_default') === $theme_id );
-      $row['group_list'] = ( empty($row['group_list']) ) ? array() : enano_json_decode($row['group_list']);
-      
-      // Build a list of group names
-      $row['group_names'] = array();
-      $q = $db->sql_query('SELECT group_id, group_name FROM ' . table_prefix . 'groups;');
-      if ( !$q )
-        $db->die_json();
-      while ( $gr = $db->fetchrow() )
-      {
-        $row['group_names'][ intval($gr['group_id']) ] = $gr['group_name'];
-      }
-      $db->free_result();
-      
-      // Build a list of usernames
-      $row['usernames'] = array();
-      foreach ( $row['group_list'] as $el )
-      {
-        if ( !preg_match('/^u:([0-9]+)$/', $el, $match) )
-          continue;
-        $uid =& $match[1];
-        $q = $db->sql_query('SELECT username FROM ' . table_prefix . "users WHERE user_id = $uid;");
-        if ( !$q )
-          $db->die_json();
-        if ( $db->numrows() < 1 )
-        {
-          $db->free_result();
-          continue;
-        }
-        list($username) = $db->fetchrow_num();
-        $row['usernames'][$uid] = $username;
-        $db->free_result();
-      }
-      
-      echo enano_json_encode($row);
-      break;
-    case 'uid_lookup':
-      $username = @$request['username'];
-      if ( empty($username) )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => $lang->get('acptm_err_invalid_username')
-          )));
-      }
-      $username = $db->escape(strtolower($username));
-      $q = $db->sql_query('SELECT user_id, username FROM ' . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';");
-      if ( !$q )
-        $db->die_json();
-      
-      if ( $db->numrows() < 1 )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => $lang->get('acptm_err_username_not_found')
-          )));
-      }
-      
-      list($uid, $username_real) = $db->fetchrow_num();
-      $db->free_result();
-      
-      echo enano_json_encode(array(
-          'uid' => $uid,
-          'username' => $username_real
-        ));
-      break;
-    case 'save_theme':
-      if ( !isset($request['theme_data']) )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => 'No theme data in request'
-          )));
-      }
-      $theme_data =& $request['theme_data'];
-      // Perform integrity check on theme data
-      $chk_theme_exists = isset($themes[@$theme_data['theme_id']]);
-      $theme_data['theme_name'] = trim(@$theme_data['theme_name']);
-      $chk_name_good = !empty($theme_data['theme_name']);
-      $chk_policy_good = in_array(@$theme_data['group_policy'], array('allow_all', 'whitelist', 'blacklist'));
-      $chk_grouplist_good = true;
-      foreach ( $theme_data['group_list'] as $acl_entry )
-      {
-        if ( !preg_match('/^(u|g):[0-9]+$/', $acl_entry) )
-        {
-          $chk_grouplist_good = false;
-          break;
-        }
-      }
-      $chk_style_good = @in_array(@$theme_data['default_style'], @$themes[@$theme_data['theme_id']]['css']);
-      if ( !$chk_theme_exists || !$chk_name_good || !$chk_policy_good || !$chk_grouplist_good || !$chk_style_good )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => $lang->get('acptm_err_save_validation_failed')
-          )));
-      }
-      
-      $enable = ( $theme_data['enabled'] ) ? '1' : '0';
-      $theme_default = getConfig('theme_default');
-      $warn_default = ( $theme_default === $theme_data['theme_id'] || $theme_data['make_default'] ) ?
-                        ' ' . $lang->get('acptm_warn_access_with_default') . ' ' :
-                        ' ';
-      if ( $enable == 0 && ( $theme_default === $theme_data['theme_id'] || $theme_data['make_default'] ) )
-      {
-        $enable = '1';
-        $warn_default .= '<b>' . $lang->get('acptm_warn_cant_disable_default') . '</b>';
-      }
-      
-      // We're good. Update the theme...
-      $q = $db->sql_query('UPDATE ' . table_prefix . 'themes SET
-                               theme_name = \'' . $db->escape($theme_data['theme_name']) . '\',
-                               default_style = \'' . $db->escape($theme_data['default_style']) . '\',
-                               group_list = \'' . $db->escape(enano_json_encode($theme_data['group_list'])) . '\',
-                               group_policy = \'' . $db->escape($theme_data['group_policy']) . '\',
-                               enabled = ' . $enable . '
-                             WHERE theme_id = \'' . $db->escape($theme_data['theme_id']) . '\';');
-      if ( !$q )
-        $db->die_json();
-      
-      if ( $theme_data['make_default'] )
-      {
-        setConfig('theme_default', $theme_data['theme_id']);
-      }
-      
-      $cache->purge('themes');
-      
-      echo '<div class="info-box"><b>' . $lang->get('acptm_msg_save_success') . '</b>' . $warn_default . '</div>';
-      
-      page_Admin_ThemeManager(true);
-      break;
-    case 'install':
-      $theme_id =& $request['theme_id'];
-      if ( !isset($themes[$theme_id]) )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => 'Theme was deleted from themes/ directory or couldn\'t read theme metadata from filesystem'
-          )));
-      }
-      if ( !isset($themes[$theme_id]['css'][0]) )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => 'Theme doesn\'t have any files in css/, thus it can\'t be installed. (translators: l10n?)'
-          )));
-      }
-      // build dataset
-      $theme_name = $db->escape($themes[$theme_id]['theme_name']);
-      $default_style = $db->escape($themes[$theme_id]['css'][0]);
-      $theme_id = $db->escape($theme_id);
-      
-      // insert it
-      $q = $db->sql_query('INSERT INTO ' . table_prefix . "themes(theme_id, theme_name, default_style, enabled, group_list, group_policy)\n"
-                        . "  VALUES( '$theme_id', '$theme_name', '$default_style', 1, '[]', 'allow_all' );");
-      if ( !$q )
-        $db->die_json();
-      
-      $cache->purge('themes');
-      
-      // The response isn't processed unless it's in JSON.
-      echo 'Roger that, over and out.';
-      
-      break;
-    case 'uninstall':
-      $theme_id =& $request['theme_id'];
-      $theme_default = getConfig('theme_default');
-      
-      // Validation
-      if ( !isset($themes[$theme_id]) )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => 'Theme was deleted from themes/ directory or couldn\'t read theme metadata from filesystem'
-          )));
-      }
-      
-      if ( $theme_id == $theme_default )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => $lang->get('acptm_err_uninstalling_default')
-          )));
-      }
-      
-      if ( $theme_id == 'oxygen' )
-      {
-        die(enano_json_encode(array(
-            'mode' => 'error',
-            'error' => $lang->get('acptm_err_uninstalling_oxygen')
-          )));
-      }
-      
-      $theme_id = $db->escape($theme_id);
-      
-      $q = $db->sql_query('DELETE FROM ' . table_prefix . "themes WHERE theme_id = '$theme_id';");
-      if ( !$q )
-        $db->die_json();
-      
-      $cache->purge('themes');
-      
-      // Change all the users that were on that theme to the default
-      $default_style = $template->named_theme_list[$theme_default]['default_style'];
-      $default_style = preg_replace('/\.css$/', '', $default_style);
-      
-      $theme_default = $db->escape($theme_default);
-      $default_style = $db->escape($default_style);
-      
-      $q = $db->sql_query('UPDATE ' . table_prefix . "users SET theme = '$theme_default', style = '$default_style' WHERE theme = '$theme_id';");
-      if ( !$q )
-        $db->die_json();
-      
-      echo '<div class="info-box">' . $lang->get('acptm_msg_uninstall_success') . '</div>';
-      
-      page_Admin_ThemeManager(true);
-      break;
-  }
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	global $cache;
+	
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	if ( !isset($_POST['r']) )
+		return false;
+	
+	try
+	{
+		$request = enano_json_decode($_POST['r']);
+	}
+	catch ( Exception $e )
+	{
+		die('Exception in JSON parser, probably invalid input.');
+	}
+	
+	if ( !isset($request['mode']) )
+	{
+		die('No mode specified in JSON request.');
+	}
+	
+	switch ( $request['mode'] )
+	{
+		case 'fetch_theme':
+			$theme_id = $db->escape($request['theme_id']);
+			if ( empty($theme_id) )
+				die('Invalid theme_id');
+			
+			$q = $db->sql_query("SELECT theme_id, theme_name, default_style, enabled, group_policy, group_list FROM " . table_prefix . "themes WHERE theme_id = '$theme_id';");
+			if ( !$q )
+				$db->die_json();
+			
+			if ( $db->numrows() < 1 )
+				die('BUG: no theme with that theme_id installed.');
+			
+			$row = $db->fetchrow();
+			$row['enabled'] = ( $row['enabled'] == 1 );
+			$row['css'] = @$themes[$theme_id]['css'];
+			$row['default_style'] = preg_replace('/\.css$/', '', $row['default_style']);
+			$row['is_default'] = ( getConfig('theme_default') === $theme_id );
+			$row['group_list'] = ( empty($row['group_list']) ) ? array() : enano_json_decode($row['group_list']);
+			
+			// Build a list of group names
+			$row['group_names'] = array();
+			$q = $db->sql_query('SELECT group_id, group_name FROM ' . table_prefix . 'groups;');
+			if ( !$q )
+				$db->die_json();
+			while ( $gr = $db->fetchrow() )
+			{
+				$row['group_names'][ intval($gr['group_id']) ] = $gr['group_name'];
+			}
+			$db->free_result();
+			
+			// Build a list of usernames
+			$row['usernames'] = array();
+			foreach ( $row['group_list'] as $el )
+			{
+				if ( !preg_match('/^u:([0-9]+)$/', $el, $match) )
+					continue;
+				$uid =& $match[1];
+				$q = $db->sql_query('SELECT username FROM ' . table_prefix . "users WHERE user_id = $uid;");
+				if ( !$q )
+					$db->die_json();
+				if ( $db->numrows() < 1 )
+				{
+					$db->free_result();
+					continue;
+				}
+				list($username) = $db->fetchrow_num();
+				$row['usernames'][$uid] = $username;
+				$db->free_result();
+			}
+			
+			echo enano_json_encode($row);
+			break;
+		case 'uid_lookup':
+			$username = @$request['username'];
+			if ( empty($username) )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => $lang->get('acptm_err_invalid_username')
+					)));
+			}
+			$username = $db->escape(strtolower($username));
+			$q = $db->sql_query('SELECT user_id, username FROM ' . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';");
+			if ( !$q )
+				$db->die_json();
+			
+			if ( $db->numrows() < 1 )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => $lang->get('acptm_err_username_not_found')
+					)));
+			}
+			
+			list($uid, $username_real) = $db->fetchrow_num();
+			$db->free_result();
+			
+			echo enano_json_encode(array(
+					'uid' => $uid,
+					'username' => $username_real
+				));
+			break;
+		case 'save_theme':
+			if ( !isset($request['theme_data']) )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => 'No theme data in request'
+					)));
+			}
+			$theme_data =& $request['theme_data'];
+			// Perform integrity check on theme data
+			$chk_theme_exists = isset($themes[@$theme_data['theme_id']]);
+			$theme_data['theme_name'] = trim(@$theme_data['theme_name']);
+			$chk_name_good = !empty($theme_data['theme_name']);
+			$chk_policy_good = in_array(@$theme_data['group_policy'], array('allow_all', 'whitelist', 'blacklist'));
+			$chk_grouplist_good = true;
+			foreach ( $theme_data['group_list'] as $acl_entry )
+			{
+				if ( !preg_match('/^(u|g):[0-9]+$/', $acl_entry) )
+				{
+					$chk_grouplist_good = false;
+					break;
+				}
+			}
+			$chk_style_good = @in_array(@$theme_data['default_style'], @$themes[@$theme_data['theme_id']]['css']);
+			if ( !$chk_theme_exists || !$chk_name_good || !$chk_policy_good || !$chk_grouplist_good || !$chk_style_good )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => $lang->get('acptm_err_save_validation_failed')
+					)));
+			}
+			
+			$enable = ( $theme_data['enabled'] ) ? '1' : '0';
+			$theme_default = getConfig('theme_default');
+			$warn_default = ( $theme_default === $theme_data['theme_id'] || $theme_data['make_default'] ) ?
+												' ' . $lang->get('acptm_warn_access_with_default') . ' ' :
+												' ';
+			if ( $enable == 0 && ( $theme_default === $theme_data['theme_id'] || $theme_data['make_default'] ) )
+			{
+				$enable = '1';
+				$warn_default .= '<b>' . $lang->get('acptm_warn_cant_disable_default') . '</b>';
+			}
+			
+			// We're good. Update the theme...
+			$q = $db->sql_query('UPDATE ' . table_prefix . 'themes SET
+ 															theme_name = \'' . $db->escape($theme_data['theme_name']) . '\',
+ 															default_style = \'' . $db->escape($theme_data['default_style']) . '\',
+ 															group_list = \'' . $db->escape(enano_json_encode($theme_data['group_list'])) . '\',
+ 															group_policy = \'' . $db->escape($theme_data['group_policy']) . '\',
+ 															enabled = ' . $enable . '
+ 														WHERE theme_id = \'' . $db->escape($theme_data['theme_id']) . '\';');
+			if ( !$q )
+				$db->die_json();
+			
+			if ( $theme_data['make_default'] )
+			{
+				setConfig('theme_default', $theme_data['theme_id']);
+			}
+			
+			$cache->purge('themes');
+			
+			echo '<div class="info-box"><b>' . $lang->get('acptm_msg_save_success') . '</b>' . $warn_default . '</div>';
+			
+			page_Admin_ThemeManager(true);
+			break;
+		case 'install':
+			$theme_id =& $request['theme_id'];
+			if ( !isset($themes[$theme_id]) )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => 'Theme was deleted from themes/ directory or couldn\'t read theme metadata from filesystem'
+					)));
+			}
+			if ( !isset($themes[$theme_id]['css'][0]) )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => 'Theme doesn\'t have any files in css/, thus it can\'t be installed. (translators: l10n?)'
+					)));
+			}
+			// build dataset
+			$theme_name = $db->escape($themes[$theme_id]['theme_name']);
+			$default_style = $db->escape($themes[$theme_id]['css'][0]);
+			$theme_id = $db->escape($theme_id);
+			
+			// insert it
+			$q = $db->sql_query('INSERT INTO ' . table_prefix . "themes(theme_id, theme_name, default_style, enabled, group_list, group_policy)\n"
+												. "  VALUES( '$theme_id', '$theme_name', '$default_style', 1, '[]', 'allow_all' );");
+			if ( !$q )
+				$db->die_json();
+			
+			$cache->purge('themes');
+			
+			// The response isn't processed unless it's in JSON.
+			echo 'Roger that, over and out.';
+			
+			break;
+		case 'uninstall':
+			$theme_id =& $request['theme_id'];
+			$theme_default = getConfig('theme_default');
+			
+			// Validation
+			if ( !isset($themes[$theme_id]) )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => 'Theme was deleted from themes/ directory or couldn\'t read theme metadata from filesystem'
+					)));
+			}
+			
+			if ( $theme_id == $theme_default )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => $lang->get('acptm_err_uninstalling_default')
+					)));
+			}
+			
+			if ( $theme_id == 'oxygen' )
+			{
+				die(enano_json_encode(array(
+						'mode' => 'error',
+						'error' => $lang->get('acptm_err_uninstalling_oxygen')
+					)));
+			}
+			
+			$theme_id = $db->escape($theme_id);
+			
+			$q = $db->sql_query('DELETE FROM ' . table_prefix . "themes WHERE theme_id = '$theme_id';");
+			if ( !$q )
+				$db->die_json();
+			
+			$cache->purge('themes');
+			
+			// Change all the users that were on that theme to the default
+			$default_style = $template->named_theme_list[$theme_default]['default_style'];
+			$default_style = preg_replace('/\.css$/', '', $default_style);
+			
+			$theme_default = $db->escape($theme_default);
+			$default_style = $db->escape($default_style);
+			
+			$q = $db->sql_query('UPDATE ' . table_prefix . "users SET theme = '$theme_default', style = '$default_style' WHERE theme = '$theme_id';");
+			if ( !$q )
+				$db->die_json();
+			
+			echo '<div class="info-box">' . $lang->get('acptm_msg_uninstall_success') . '</div>';
+			
+			page_Admin_ThemeManager(true);
+			break;
+	}
 }
 
--- a/plugins/admin/UserManager.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/UserManager.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,589 +13,589 @@
 
 function page_Admin_UserManager()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  require_once(ENANO_ROOT . '/includes/math.php');
-  require_once(ENANO_ROOT . '/includes/diffiehellman.php');
-  
-  $GLOBALS['dh_supported'] = $dh_supported;
-  
-  //die('<pre>' . htmlspecialchars(print_r($_POST, true)) . '</pre>');
-  
-  if ( isset($_POST['action']['save']) )
-  {
-    #
-    # BEGIN VALIDATION
-    #
-    
-    $errors = array();
-    
-    if ( defined('ENANO_DEMO_MODE') )
-    {
-      $errors[] = $lang->get('acpum_err_nosave_demo');
-    }
-    
-    $user_id = intval($_POST['user_id']);
-    if ( empty($user_id) || $user_id == 1 )
-      $errors[] = 'Invalid user ID.';
-    
-    if ( isset($_POST['delete_account']) && count($errors) < 1 )
-    {
-      $q = $db->sql_query('DELETE FROM '.table_prefix."users_extra WHERE user_id=$user_id;");
-      if ( !$q )
-        $db->_die();
-      $q = $db->sql_query('DELETE FROM '.table_prefix."users WHERE user_id=$user_id;");
-      if ( !$q )
-        $db->_die();
-      $q = $db->sql_query('DELETE FROM '.table_prefix."session_keys WHERE user_id=$user_id;");
-      if ( !$q )
-        $db->_die();
-      echo '<div class="info-box">' . $lang->get('acpum_msg_delete_success') . '</div>';
-      
-      // deleting own account?
-      if ( $user_id === $session->user_id )
-      {
-        // cute little hack to boot them out of the admin panel
-        echo '<script type="text/javascript">
-          addOnloadHook(function()
-          {
-            setTimeout(function()
-            {
-              eraseCookie("sid");
-              ENANO_SID = false;
-              auth_level = USER_LEVEL_MEMBER;
-              window.location = makeUrlNS("Special", "Login");
-            }, 3000);
-          });
-        </script>';
-      }
-    }
-    else
-    {
-      if ( $session->user_id == $user_id )
-      {
-        $username = $session->username;
-        $password = false;
-        $email = $session->email;
-        $real_name = $session->real_name;
-      }
-      else
-      {
-        $username = $_POST['username'];
-        if ( !preg_match('#^'.$session->valid_username.'$#', $username) )
-          $errors[] = $lang->get('acpum_err_illegal_username');
-        
-        $password = false;
-        if ( $_POST['changing_pw'] == 'yes' )
-        {
-          $password = $session->get_aes_post('new_password');
-        }
-        
-        $email = $_POST['email'];
-        if ( !preg_match('/^(?:[\w\d]+\.?)+@((?:(?:[\w\d]\-?)+\.)+\w{2,4}|localhost)$/', $email) )
-          $errors[] = $lang->get('acpum_err_illegal_email');
-        
-        $real_name = $_POST['real_name'];
-      }
-      
-      $signature = RenderMan::preprocess_text($_POST['signature'], true, false);
-      
-      $user_level = intval($_POST['user_level']);
-      if ( $user_level < USER_LEVEL_MEMBER || $user_level > USER_LEVEL_ADMIN )
-        $errors[] = 'Invalid user level';
-      
-      $user_rank = $_POST['user_rank'];
-      if ( $user_rank !== 'NULL' )
-      {
-        $user_rank = intval($user_rank);
-        if ( !$user_rank )
-          $errors[] = 'Invalid user rank';
-      }
-      
-      $imaddr_aim = htmlspecialchars($_POST['imaddr_aim']);
-      $imaddr_msn = htmlspecialchars($_POST['imaddr_msn']);
-      $imaddr_yahoo = htmlspecialchars($_POST['imaddr_yahoo']);
-      $imaddr_xmpp = htmlspecialchars($_POST['imaddr_xmpp']);
-      $homepage = htmlspecialchars($_POST['homepage']);
-      $location = htmlspecialchars($_POST['location']);
-      $occupation = htmlspecialchars($_POST['occupation']);
-      $hobbies = htmlspecialchars($_POST['hobbies']);
-      $email_public = ( isset($_POST['email_public']) ) ? '1' : '0';
-      $user_title = htmlspecialchars($_POST['user_title']);
-      
-      if ( !preg_match('/@([a-z0-9-]+)(\.([a-z0-9-\.]+))?/', $imaddr_msn) && !empty($imaddr_msn) )
-      {
-        $imaddr_msn = "$imaddr_msn@hotmail.com";
-      }
-      
-      if ( !preg_match('#^https?://#', $homepage) )
-      {
-        $homepage = "http://$homepage";
-      }
-      
-      if ( !preg_match('/^http:\/\/([a-z0-9-.]+)([A-z0-9@#\$%\&:;<>,\.\?=\+\(\)\[\]_\/\\\\]*?)$/i', $homepage) )
-      {
-        $homepage = '';
-      }
-      
-      // true for quiet operation
-      list(, , $avatar_post_fail) = avatar_post($user_id, true);
-      
-      if ( count($errors) < 1 && !$avatar_post_fail )
-      {
-        $q = $db->sql_query('SELECT u.user_level, u.user_has_avatar, u.avatar_type, u.username FROM '.table_prefix.'users AS u WHERE u.user_id = ' . $user_id . ';');
-        if ( !$q )
-          $db->_die();
-        
-        if ( $db->numrows() < 1 )
-        {
-          echo 'Couldn\'t select user data: no rows returned';
-        }
-        
-        $row = $db->fetchrow();
-        $existing_level =& $row['user_level'];
-        $avi_type =& $row['avatar_type'];
-        $has_avi = ( $row['user_has_avatar'] == 1 );
-        $old_username = $row['username'];
-        $db->free_result();
-        
-        $to_update_users = array();
-        if ( $user_id != $session->user_id )
-        {
-          $to_update_users['username'] = $username;
-          if ( $password )
-          {
-            $session->set_password($user_id, $password);
-          }
-          $to_update_users['email'] = $email;
-          $to_update_users['real_name'] = $real_name;
-        }
-        $to_update_users['signature'] = $signature;
-        $to_update_users['user_level'] = $user_level;
-        $to_update_users['user_rank'] = $user_rank;
-        $to_update_users['user_title'] = $user_title;
-        
-        if ( $user_rank > 0 )
-        {
-          $to_update_users['user_rank_userset'] = '0';
-        }
-        
-        if ( isset($_POST['account_active']) )
-        {
-          $to_update_users['account_active'] = "1";
-        }
-        else
-        {
-          $to_update_users['account_active'] = "0";
-          $to_update_users['activation_key'] = sha1($session->dss_rand());
-        }
-        
-        if ( count($errors) < 1 )
-        {
-          $to_update_users_extra = array();
-          $to_update_users_extra['user_aim'] = $imaddr_aim;
-          $to_update_users_extra['user_msn'] = $imaddr_msn;
-          $to_update_users_extra['user_yahoo'] = $imaddr_yahoo;
-          $to_update_users_extra['user_xmpp'] = $imaddr_xmpp;
-          $to_update_users_extra['user_homepage'] = $homepage;
-          $to_update_users_extra['user_location'] = $location;
-          $to_update_users_extra['user_job'] = $occupation;
-          $to_update_users_extra['user_hobbies'] = $hobbies;
-          $to_update_users_extra['email_public'] = ( $email_public ) ? '1' : '0';
-          
-          $update_sql = '';
-          
-          foreach ( $to_update_users as $key => $unused_crap )
-          {
-            $value =& $to_update_users[$key];
-            if ( $value !== 'NULL' )
-              $value = "'" . $db->escape($value) . "'";
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	require_once(ENANO_ROOT . '/includes/math.php');
+	require_once(ENANO_ROOT . '/includes/diffiehellman.php');
+	
+	$GLOBALS['dh_supported'] = $dh_supported;
+	
+	//die('<pre>' . htmlspecialchars(print_r($_POST, true)) . '</pre>');
+	
+	if ( isset($_POST['action']['save']) )
+	{
+		#
+		# BEGIN VALIDATION
+		#
+		
+		$errors = array();
+		
+		if ( defined('ENANO_DEMO_MODE') )
+		{
+			$errors[] = $lang->get('acpum_err_nosave_demo');
+		}
+		
+		$user_id = intval($_POST['user_id']);
+		if ( empty($user_id) || $user_id == 1 )
+			$errors[] = 'Invalid user ID.';
+		
+		if ( isset($_POST['delete_account']) && count($errors) < 1 )
+		{
+			$q = $db->sql_query('DELETE FROM '.table_prefix."users_extra WHERE user_id=$user_id;");
+			if ( !$q )
+				$db->_die();
+			$q = $db->sql_query('DELETE FROM '.table_prefix."users WHERE user_id=$user_id;");
+			if ( !$q )
+				$db->_die();
+			$q = $db->sql_query('DELETE FROM '.table_prefix."session_keys WHERE user_id=$user_id;");
+			if ( !$q )
+				$db->_die();
+			echo '<div class="info-box">' . $lang->get('acpum_msg_delete_success') . '</div>';
+			
+			// deleting own account?
+			if ( $user_id === $session->user_id )
+			{
+				// cute little hack to boot them out of the admin panel
+				echo '<script type="text/javascript">
+					addOnloadHook(function()
+					{
+						setTimeout(function()
+						{
+							eraseCookie("sid");
+							ENANO_SID = false;
+							auth_level = USER_LEVEL_MEMBER;
+							window.location = makeUrlNS("Special", "Login");
+						}, 3000);
+					});
+				</script>';
+			}
+		}
+		else
+		{
+			if ( $session->user_id == $user_id )
+			{
+				$username = $session->username;
+				$password = false;
+				$email = $session->email;
+				$real_name = $session->real_name;
+			}
+			else
+			{
+				$username = $_POST['username'];
+				if ( !preg_match('#^'.$session->valid_username.'$#', $username) )
+					$errors[] = $lang->get('acpum_err_illegal_username');
+				
+				$password = false;
+				if ( $_POST['changing_pw'] == 'yes' )
+				{
+					$password = $session->get_aes_post('new_password');
+				}
+				
+				$email = $_POST['email'];
+				if ( !preg_match('/^(?:[\w\d]+\.?)+@((?:(?:[\w\d]\-?)+\.)+\w{2,4}|localhost)$/', $email) )
+					$errors[] = $lang->get('acpum_err_illegal_email');
+				
+				$real_name = $_POST['real_name'];
+			}
+			
+			$signature = RenderMan::preprocess_text($_POST['signature'], true, false);
+			
+			$user_level = intval($_POST['user_level']);
+			if ( $user_level < USER_LEVEL_MEMBER || $user_level > USER_LEVEL_ADMIN )
+				$errors[] = 'Invalid user level';
+			
+			$user_rank = $_POST['user_rank'];
+			if ( $user_rank !== 'NULL' )
+			{
+				$user_rank = intval($user_rank);
+				if ( !$user_rank )
+					$errors[] = 'Invalid user rank';
+			}
+			
+			$imaddr_aim = htmlspecialchars($_POST['imaddr_aim']);
+			$imaddr_msn = htmlspecialchars($_POST['imaddr_msn']);
+			$imaddr_yahoo = htmlspecialchars($_POST['imaddr_yahoo']);
+			$imaddr_xmpp = htmlspecialchars($_POST['imaddr_xmpp']);
+			$homepage = htmlspecialchars($_POST['homepage']);
+			$location = htmlspecialchars($_POST['location']);
+			$occupation = htmlspecialchars($_POST['occupation']);
+			$hobbies = htmlspecialchars($_POST['hobbies']);
+			$email_public = ( isset($_POST['email_public']) ) ? '1' : '0';
+			$user_title = htmlspecialchars($_POST['user_title']);
+			
+			if ( !preg_match('/@([a-z0-9-]+)(\.([a-z0-9-\.]+))?/', $imaddr_msn) && !empty($imaddr_msn) )
+			{
+				$imaddr_msn = "$imaddr_msn@hotmail.com";
+			}
+			
+			if ( !preg_match('#^https?://#', $homepage) )
+			{
+				$homepage = "http://$homepage";
+			}
+			
+			if ( !preg_match('/^http:\/\/([a-z0-9-.]+)([A-z0-9@#\$%\&:;<>,\.\?=\+\(\)\[\]_\/\\\\]*?)$/i', $homepage) )
+			{
+				$homepage = '';
+			}
+			
+			// true for quiet operation
+			list(, , $avatar_post_fail) = avatar_post($user_id, true);
+			
+			if ( count($errors) < 1 && !$avatar_post_fail )
+			{
+				$q = $db->sql_query('SELECT u.user_level, u.user_has_avatar, u.avatar_type, u.username FROM '.table_prefix.'users AS u WHERE u.user_id = ' . $user_id . ';');
+				if ( !$q )
+					$db->_die();
+				
+				if ( $db->numrows() < 1 )
+				{
+					echo 'Couldn\'t select user data: no rows returned';
+				}
+				
+				$row = $db->fetchrow();
+				$existing_level =& $row['user_level'];
+				$avi_type =& $row['avatar_type'];
+				$has_avi = ( $row['user_has_avatar'] == 1 );
+				$old_username = $row['username'];
+				$db->free_result();
+				
+				$to_update_users = array();
+				if ( $user_id != $session->user_id )
+				{
+					$to_update_users['username'] = $username;
+					if ( $password )
+					{
+						$session->set_password($user_id, $password);
+					}
+					$to_update_users['email'] = $email;
+					$to_update_users['real_name'] = $real_name;
+				}
+				$to_update_users['signature'] = $signature;
+				$to_update_users['user_level'] = $user_level;
+				$to_update_users['user_rank'] = $user_rank;
+				$to_update_users['user_title'] = $user_title;
+				
+				if ( $user_rank > 0 )
+				{
+					$to_update_users['user_rank_userset'] = '0';
+				}
+				
+				if ( isset($_POST['account_active']) )
+				{
+					$to_update_users['account_active'] = "1";
+				}
+				else
+				{
+					$to_update_users['account_active'] = "0";
+					$to_update_users['activation_key'] = sha1($session->dss_rand());
+				}
+				
+				if ( count($errors) < 1 )
+				{
+					$to_update_users_extra = array();
+					$to_update_users_extra['user_aim'] = $imaddr_aim;
+					$to_update_users_extra['user_msn'] = $imaddr_msn;
+					$to_update_users_extra['user_yahoo'] = $imaddr_yahoo;
+					$to_update_users_extra['user_xmpp'] = $imaddr_xmpp;
+					$to_update_users_extra['user_homepage'] = $homepage;
+					$to_update_users_extra['user_location'] = $location;
+					$to_update_users_extra['user_job'] = $occupation;
+					$to_update_users_extra['user_hobbies'] = $hobbies;
+					$to_update_users_extra['email_public'] = ( $email_public ) ? '1' : '0';
+					
+					$update_sql = '';
+					
+					foreach ( $to_update_users as $key => $unused_crap )
+					{
+						$value =& $to_update_users[$key];
+						if ( $value !== 'NULL' )
+							$value = "'" . $db->escape($value) . "'";
  
-            $update_sql .= ( empty($update_sql) ? '' : ',' ) . "$key=$value";
-          }
-          
-          $update_sql = 'UPDATE ' . table_prefix . "users SET $update_sql WHERE user_id=$user_id;";
-          
-          $update_sql_extra = '';
-          
-          foreach ( $to_update_users_extra as $key => $unused_crap )
-          {
-            $value =& $to_update_users_extra[$key];
-            $value = $db->escape($value);
-            $update_sql_extra .= ( empty($update_sql_extra) ? '' : ',' ) . "$key='$value'";
-          }
-          
-          $update_sql_extra = 'UPDATE '.table_prefix."users_extra SET $update_sql_extra WHERE user_id=$user_id;";
-          
-          if ( !$db->sql_query($update_sql) )
-            $db->_die();
-          
-          if ( !$db->sql_query($update_sql_extra) )
-            $db->_die();
-          
-          // If the username was changed, we need to update their user page as well
-          if ( $old_username != $username )
-          {
-            $page = new PageProcessor($old_username, 'User');
-            if ( $page->exists() )
-            {
-              // they have a user page, rename it
-              $old_urlname = $db->escape(sanitize_page_id($old_username));
-              $new_urlname = $db->escape(sanitize_page_id($username));
-              $sql = array(
-                      'UPDATE ' . table_prefix . "pages      SET urlname = '$new_urlname' WHERE urlname = '$old_urlname' AND namespace = 'User';",
-                      // Change the page's title ONLY if it exactly matches the old username
-                      'UPDATE ' . table_prefix . "pages      SET name = '" . $db->escape($username) . "' WHERE urlname = '$new_urlname' AND name = '" . $db->escape($old_username) . "' AND namespace = 'User';",
-                      'UPDATE ' . table_prefix . "logs       SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';",
-                      'UPDATE ' . table_prefix . "tags       SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';",
-                      'UPDATE ' . table_prefix . "comments   SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';",
-                      'UPDATE ' . table_prefix . "page_text  SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';",
-                      'UPDATE ' . table_prefix . "categories SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';"
-                    );
-              foreach ( $sql as $q )
-              {
-                if ( !$db->sql_query($q) )
-                  $db->_die('UserManager renaming user page post-username change');
-              }
-            }
-          }
-          
-          if ( $existing_level != $user_level )
-          {
-            // We need to update group memberships
-            if ( $existing_level == USER_LEVEL_ADMIN ) 
-            {
-              $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'u_from_admin\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', ' . $session->user_id . ', \'' . $db->escape($session->username) . '\', \'' . $db->escape($username) . '\');');
-              if ( !$q )
-                $db->_die();
-              $session->remove_user_from_group($user_id, GROUP_ID_ADMIN);
-            }
-            else if ( $existing_level == USER_LEVEL_MOD ) 
-            {
-              $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'u_from_mod\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', ' . $session->user_id . ', \'' . $db->escape($session->username) . '\', \'' . $db->escape($username) . '\');');
-              if ( !$q )
-                $db->_die();
-              $session->remove_user_from_group($user_id, GROUP_ID_MOD);
-            }
-            
-            if ( $user_level == USER_LEVEL_ADMIN )
-            {
-              $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'u_to_admin\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', ' . $session->user_id . ', \'' . $db->escape($session->username) . '\', \'' . $db->escape($username) . '\');');
-              if ( !$q )
-                $db->_die();
-              $session->add_user_to_group($user_id, GROUP_ID_ADMIN, false);
-            }
-            else if ( $user_level == USER_LEVEL_MOD )
-            {
-              $q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'u_to_mod\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', ' . $session->user_id . ', \'' . $db->escape($session->username) . '\', \'' . $db->escape($username) . '\');');
-              if ( !$q )
-                $db->_die();
-              $session->add_user_to_group($user_id, GROUP_ID_MOD, false);
-            }
-          }
-          
-          // user level updated, regenerate the ranks cache
-          generate_cache_userranks();
-          
-          echo '<div class="info-box">' . $lang->get('acpum_msg_save_success') . '</div>';
-        }
-      }
-    }
-    
-    if ( count($errors) > 0 || @$avatar_post_fail )
-    {
-      if ( count($errors) > 0 )
-      {
-        echo '<div class="error-box">
-                <b>' . $lang->get('acpum_err_validation_fail') . '</b>
-                <ul>
-                  <li>' . implode("</li>\n        <li>", $errors) . '</li>
-                </ul>
-              </div>';
-      }
-      $form = new Admin_UserManager_SmartForm();
-      $form->user_id = $user_id;
-      $form->username = $username;
-      $form->email = $email;
-      $form->real_name = $real_name;
-      $form->signature = $signature;
-      $form->user_level = $user_level;
-      $form->user_rank = $user_rank;
-      $form->user_title = $user_title;
-      $form->im = array(
-          'aim' => $imaddr_aim,
-          'yahoo' => $imaddr_yahoo,
-          'msn' => $imaddr_msn,
-          'xmpp' => $imaddr_xmpp
-        );
-      $form->contact = array(
-          'homepage' => $homepage,
-          'location' => $location,
-          'job' => $occupation,
-          'hobbies' => $hobbies
-        );
-      $form->email_public = ( isset($_POST['email_public']) );
-      $form->account_active = ( isset($_POST['account_active']) );
-      // This is SAFE. The smartform calls is_valid_ip() on this value, thus preventing XSS
-      // attempts from making it into the form HTML. Badly coded templates may still be
-      // affected, but if have_reg_ip is checked for, then you're fine.
-      $form->reg_ip_addr = $_POST['user_registration_ip'];
-      echo $form->render();
-      return false;
-    }
-    
-    #
-    # END VALIDATION
-    #
-  }
-  else if ( isset($_POST['action']['go']) || ( isset($_GET['src']) && $_GET['src'] == 'get' ) || ($pathsuser = $paths->getParam(0)) )
-  {
-    if ( isset($_GET['user']) )
-    {
-      $username =& $_GET['user'];
-    }
-    else if ( isset($_GET['username']) )
-    {
-      $username =& $_GET['username'];
-    }
-    else if ( isset($_POST['username']) )
-    {
-      $username =& $_POST['username'];
-    }
-    else if ( $pathsuser )
-    {
-      $username = str_replace('_', ' ', dirtify_page_id($pathsuser));
-    }
-    else
-    {
-      echo 'No username provided';
-      return false;
-    }
-    $q = $db->sql_query('SELECT u.user_id AS authoritative_uid, u.username, u.email, u.real_name, u.signature, u.account_active, u.user_level, u.user_rank, u.user_title, u.user_has_avatar, u.avatar_type, u.user_registration_ip, x.* FROM '.table_prefix.'users AS u
-                           LEFT JOIN '.table_prefix.'users_extra AS x
-                             ON ( u.user_id = x.user_id OR x.user_id IS NULL )
-                           WHERE ( ' . ENANO_SQLFUNC_LOWERCASE . '(u.username) = \'' . $db->escape(strtolower($username)) . '\' OR u.username = \'' . $db->escape($username) . '\' ) AND u.user_id != 1;');
-    if ( !$q )
-      $db->_die();
-    
-    if ( $db->numrows() < 1 )
-    {
-      echo '<div class="error-box">' . $lang->get('acpum_err_bad_username') . '</div>';
-    }
-    else
-    {
-      $row = $db->fetchrow();
-      $row['user_id'] = $row['authoritative_uid'];
-      $form = new Admin_UserManager_SmartForm();
-      $form->user_id   = $row['user_id'];
-      $form->username  = $row['username'];
-      $form->email     = $row['email'];
-      $form->real_name = $row['real_name'];
-      $form->signature = $row['signature'];
-      $form->user_level= $row['user_level'];
-      $form->user_rank = $row['user_rank'];
-      $form->user_title= $row['user_title'];
-      $form->account_active = ( $row['account_active'] == 1 );
-      $form->email_public   = ( $row['email_public'] == 1 );
-      $form->has_avatar     = ( $row['user_has_avatar'] == 1 );
-      $form->avi_type       = $row['avatar_type'];
-      $form->im = array(
-          'aim' => $row['user_aim'],
-          'yahoo' => $row['user_yahoo'],
-          'msn' => $row['user_msn'],
-          'xmpp' => $row['user_xmpp']
-        );
-      $form->contact = array(
-          'homepage' => $row['user_homepage'],
-          'location' => $row['user_location'],
-          'job'      => $row['user_job'],
-          'hobbies'  => $row['user_hobbies'],
-        );
-      $form->email_public = ( $row['email_public'] == 1 );
-      $form->reg_ip_addr = ( $row['user_registration_ip'] ) ? $row['user_registration_ip'] : '';
-      $html = $form->render();
-      if ( !$html )
-      {
-        echo 'Internal error: form processor returned false';
-      }
-      else
-      {
-        echo $html;
-      }
-      return true;
-    }
-  }
-  else if ( isset($_POST['action']['clear_sessions']) )
-  {
-    if ( defined('ENANO_DEMO_MODE') )
-    {
-      echo '<div class="error-box">' . $lang->get('acpum_err_sessionclear_demo') . '</div>';
-    }
-    else
-    {
-      // Get the current session information so the user doesn't get logged out
-      $aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
-      $sk = md5($session->sid_super);
-      $qb = $db->sql_query('SELECT session_key,salt,auth_level,source_ip,time FROM '.table_prefix.'session_keys WHERE session_key=\''.$sk.'\' AND user_id='.$session->user_id.' AND auth_level='.USER_LEVEL_ADMIN);
-      if ( !$qb )
-      {
-        die('Error selecting session key info block B: '.$db->get_error());
-      }
-      if ( $db->numrows($qb) < 1 )
-      {
-        die('Error: cannot read admin session info block B, aborting table clear process');
-      }
-      $qa = $db->sql_query('SELECT session_key,salt,auth_level,source_ip,time FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($session->sid).'\' AND user_id='.$session->user_id.' AND auth_level='.USER_LEVEL_MEMBER);
-      if ( !$qa )
-      {
-        die('Error selecting session key info block A: '.$db->get_error());
-      }
-      if ( $db->numrows($qa) < 1 )
-      {
-        die('Error: cannot read user session info block A, aborting table clear process');
-      }
-      $ra = $db->fetchrow($qa);
-      $rb = $db->fetchrow($qb);
-      $db->free_result($qa);
-      $db->free_result($qb);
-      
-      $db->sql_query('DELETE FROM '.table_prefix.'session_keys;');
-      $db->sql_query('INSERT INTO '.table_prefix.'session_keys( session_key,salt,user_id,auth_level,source_ip,time ) VALUES( \''.$ra['session_key'].'\', \'' . $db->escape($ra['salt']) . '\', \''.$session->user_id.'\', \''.$ra['auth_level'].'\', \''.$ra['source_ip'].'\', '.$ra['time'].' ),( \''.$rb['session_key'].'\', \'' . $db->escape($rb['salt']) . '\', \''.$session->user_id.'\', \''.$rb['auth_level'].'\', \''.$rb['source_ip'].'\', '.$rb['time'].' )');
-      
-      echo '<div class="info-box">' . $lang->get('acpum_msg_sessionclear_success') . '</div>';
-    }
-  }
-  echo '<form action="' . makeUrlNS('Special', 'Administration', 'module=' . $paths->cpage['module'], true) . '" method="post" enctype="multipart/form-data" onsubmit="if ( !submitAuthorized ) return false;">';
-  echo '<h3>' . $lang->get('acpum_heading_main') . '</h3>';
-  echo '<p>' . $lang->get('acpum_hint_intro') . '</p>';
-  echo '<table border="0">
-          <tr>
-            <td><b>' . $lang->get('acpum_field_search_user') . '</b><br />
-                <small>' . $lang->get('acpum_field_search_user_hint') . '</small>
-                </td>
-            <td style="width: 10px;"></td>
-            <td>' . $template->username_field('username') . '</td>
-            <td>
-              <input type="submit" name="action[go]" value="' . $lang->get('acpum_btn_search_user_go') . ' &raquo;" />
-            </td>
-          </tr>
-        </table>';
-  echo '<h3>' . $lang->get('acpum_heading_clear_sessions') . '</h3>';
-  echo '<p>' . $lang->get('acpum_hint_clear_sessions') . '</p>';
-  echo '<p><input type="submit" name="action[clear_sessions]" value="' . $lang->get('acpum_btn_clear_sessions') . '" /></p>';
-  echo '</form>';
-  
-  if(isset($_GET['action']) && isset($_GET['user']))
-  {
-    switch($_GET['action'])
-    {
-      case "activate":
-        $e = $db->sql_query('SELECT activation_key FROM '.table_prefix.'users WHERE username=\'' . $db->escape($_GET['user']) . '\'');
-        if ( $e )
-        {
-          // attempt to activate the account
-          $row = $db->fetchrow();
-          $db->free_result();
-          if ( $session->activate_account($_GET['user'], $row['activation_key']) )
-          {
-            echo '<div class="info-box">' . $lang->get('acpum_msg_activate_success', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
-            $db->sql_query('DELETE FROM '.table_prefix.'logs WHERE time_id=' . $db->escape($_GET['logid']));
-          }
-          else
-          {
-            echo '<div class="warning-box">' . $lang->get('acpum_err_activate_fail', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
-          }
-        }
-        else
-        {
-          echo '<div class="error-box">Error activating account: '.$db->get_error().'</div>';
-        }
-        break;
-      case "sendemail":
-        if ( $session->send_activation_mail($_GET['user'] ) )
-        {
-          echo '<div class="info-box">' . $lang->get('acpum_msg_activate_email_success', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
-          $db->sql_query('DELETE FROM '.table_prefix.'logs WHERE time_id=' . $db->escape($_GET['logid']));
-        }
-        else
-        {
-          echo '<div class="error-box">' . $lang->get('acpum_err_activate_email_fail', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
-        }
-        break;
-      case "deny":
-        $e = $db->sql_query('DELETE FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\' AND time_id=\'' . $db->escape($_GET['logid']) . '\';');
-        if ( !$e )
-        {
-          echo '<div class="error-box">Error during row deletion: '.$db->get_error().'</div>';
-        }
-        else
-        {
-          echo '<div class="info-box">' . $lang->get('acpum_msg_activate_deny_success', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
-        }
-        break;
-    }
-  }
-  $q = $db->sql_query('SELECT l.log_type, l.action, l.time_id, l.date_string, l.author, l.edit_summary, u.user_coppa FROM '.table_prefix.'logs AS l
-                         LEFT JOIN '.table_prefix.'users AS u
-                           ON ( u.username = l.edit_summary OR u.username IS NULL )
-                         WHERE log_type=\'admin\' AND action=\'activ_req\' ORDER BY time_id DESC;');
-  if($q)
-  {
-    if($db->numrows() > 0)
-    {
-      $n = $db->numrows();
-      $str = ( $n == 1 ) ?
-        $lang->get('acpum_heading_activation_one') :
-        $lang->get('acpum_heading_activation_plural', array('count' => strval($n)));
-        
-      echo '<h3>' . $str . '</h3>';
-        
-      echo '<div class="tblholder">
-              <table border="0" cellspacing="1" cellpadding="4" width="100%">
-                <tr>
-                  <th>' . $lang->get('acpum_col_activate_timestamp') . '</th>
-                  <th>' . $lang->get('acpum_col_activate_requestedby') . '</th>
-                  <th>' . $lang->get('acpum_col_activate_requestedfor') . '</th>
-                  <th>' . $lang->get('acpum_col_activate_coppauser') . '</th>
-                  <th colspan="3">' . $lang->get('acpum_col_activate_actions') . '</th>
-                </tr>';
-      $cls = 'row2';
-      while($row = $db->fetchrow())
-      {
-        if($cls == 'row2') $cls = 'row1';
-        else $cls = 'row2';
-        $coppa = ( $row['user_coppa'] == '1' ) ? '<b>' . $lang->get('acpum_coppauser_yes') . '</b>' : $lang->get('acpum_coppauser_no');
-        echo '<tr>
-                <td class="'.$cls.'">'.enano_date(ED_DATE | ED_TIME, $row['time_id']).'</td>
-                <td class="'.$cls.'">'.$row['author'].'</td>
-                <td class="'.$cls.'">'.$row['edit_summary'].'</td>
-                <td style="text-align: center;" class="' . $cls . '">' . $coppa . '</td>
-                <td class="'.$cls.'" style="text-align: center;">
-                  <a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&action=activate&user='.rawurlencode($row['edit_summary']).'&logid='.$row['time_id'], true).'">' . $lang->get('acpum_btn_activate_now') . '</a>
-                </td>
-                <td class="'.$cls.'" style="text-align: center;">
-                  <a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&action=sendemail&user='.rawurlencode($row['edit_summary']).'&logid='.$row['time_id'], true).'">' . $lang->get('acpum_btn_send_email') . '</a>
-                </td>
-                <td class="'.$cls.'" style="text-align: center;">
-                  <a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&action=deny&user='.rawurlencode($row['edit_summary']).'&logid='.$row['time_id'], true).'">' . $lang->get('acpum_btn_activate_deny') . '</a>
-                </td>
-              </tr>';
-      }
-      echo '</table>';
-      echo '</div>';
-    }
-    $db->free_result();
-  }
-  
-  acp_usermanager_lockouts();
+						$update_sql .= ( empty($update_sql) ? '' : ',' ) . "$key=$value";
+					}
+					
+					$update_sql = 'UPDATE ' . table_prefix . "users SET $update_sql WHERE user_id=$user_id;";
+					
+					$update_sql_extra = '';
+					
+					foreach ( $to_update_users_extra as $key => $unused_crap )
+					{
+						$value =& $to_update_users_extra[$key];
+						$value = $db->escape($value);
+						$update_sql_extra .= ( empty($update_sql_extra) ? '' : ',' ) . "$key='$value'";
+					}
+					
+					$update_sql_extra = 'UPDATE '.table_prefix."users_extra SET $update_sql_extra WHERE user_id=$user_id;";
+					
+					if ( !$db->sql_query($update_sql) )
+						$db->_die();
+					
+					if ( !$db->sql_query($update_sql_extra) )
+						$db->_die();
+					
+					// If the username was changed, we need to update their user page as well
+					if ( $old_username != $username )
+					{
+						$page = new PageProcessor($old_username, 'User');
+						if ( $page->exists() )
+						{
+							// they have a user page, rename it
+							$old_urlname = $db->escape(sanitize_page_id($old_username));
+							$new_urlname = $db->escape(sanitize_page_id($username));
+							$sql = array(
+											'UPDATE ' . table_prefix . "pages      SET urlname = '$new_urlname' WHERE urlname = '$old_urlname' AND namespace = 'User';",
+											// Change the page's title ONLY if it exactly matches the old username
+											'UPDATE ' . table_prefix . "pages      SET name = '" . $db->escape($username) . "' WHERE urlname = '$new_urlname' AND name = '" . $db->escape($old_username) . "' AND namespace = 'User';",
+											'UPDATE ' . table_prefix . "logs       SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';",
+											'UPDATE ' . table_prefix . "tags       SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';",
+											'UPDATE ' . table_prefix . "comments   SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';",
+											'UPDATE ' . table_prefix . "page_text  SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';",
+											'UPDATE ' . table_prefix . "categories SET page_id = '$new_urlname' WHERE page_id = '$old_urlname' AND namespace = 'User';"
+										);
+							foreach ( $sql as $q )
+							{
+								if ( !$db->sql_query($q) )
+									$db->_die('UserManager renaming user page post-username change');
+							}
+						}
+					}
+					
+					if ( $existing_level != $user_level )
+					{
+						// We need to update group memberships
+						if ( $existing_level == USER_LEVEL_ADMIN ) 
+						{
+							$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'u_from_admin\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', ' . $session->user_id . ', \'' . $db->escape($session->username) . '\', \'' . $db->escape($username) . '\');');
+							if ( !$q )
+								$db->_die();
+							$session->remove_user_from_group($user_id, GROUP_ID_ADMIN);
+						}
+						else if ( $existing_level == USER_LEVEL_MOD ) 
+						{
+							$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'u_from_mod\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', ' . $session->user_id . ', \'' . $db->escape($session->username) . '\', \'' . $db->escape($username) . '\');');
+							if ( !$q )
+								$db->_die();
+							$session->remove_user_from_group($user_id, GROUP_ID_MOD);
+						}
+						
+						if ( $user_level == USER_LEVEL_ADMIN )
+						{
+							$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'u_to_admin\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', ' . $session->user_id . ', \'' . $db->escape($session->username) . '\', \'' . $db->escape($username) . '\');');
+							if ( !$q )
+								$db->_die();
+							$session->add_user_to_group($user_id, GROUP_ID_ADMIN, false);
+						}
+						else if ( $user_level == USER_LEVEL_MOD )
+						{
+							$q = $db->sql_query('INSERT INTO '.table_prefix.'logs(log_type,action,time_id,edit_summary,author,author_uid,page_text) VALUES(\'security\',\'u_to_mod\',' . time() . ', \'' . $db->escape($_SERVER['REMOTE_ADDR']) . '\', ' . $session->user_id . ', \'' . $db->escape($session->username) . '\', \'' . $db->escape($username) . '\');');
+							if ( !$q )
+								$db->_die();
+							$session->add_user_to_group($user_id, GROUP_ID_MOD, false);
+						}
+					}
+					
+					// user level updated, regenerate the ranks cache
+					generate_cache_userranks();
+					
+					echo '<div class="info-box">' . $lang->get('acpum_msg_save_success') . '</div>';
+				}
+			}
+		}
+		
+		if ( count($errors) > 0 || @$avatar_post_fail )
+		{
+			if ( count($errors) > 0 )
+			{
+				echo '<div class="error-box">
+								<b>' . $lang->get('acpum_err_validation_fail') . '</b>
+								<ul>
+									<li>' . implode("</li>\n        <li>", $errors) . '</li>
+								</ul>
+							</div>';
+			}
+			$form = new Admin_UserManager_SmartForm();
+			$form->user_id = $user_id;
+			$form->username = $username;
+			$form->email = $email;
+			$form->real_name = $real_name;
+			$form->signature = $signature;
+			$form->user_level = $user_level;
+			$form->user_rank = $user_rank;
+			$form->user_title = $user_title;
+			$form->im = array(
+					'aim' => $imaddr_aim,
+					'yahoo' => $imaddr_yahoo,
+					'msn' => $imaddr_msn,
+					'xmpp' => $imaddr_xmpp
+				);
+			$form->contact = array(
+					'homepage' => $homepage,
+					'location' => $location,
+					'job' => $occupation,
+					'hobbies' => $hobbies
+				);
+			$form->email_public = ( isset($_POST['email_public']) );
+			$form->account_active = ( isset($_POST['account_active']) );
+			// This is SAFE. The smartform calls is_valid_ip() on this value, thus preventing XSS
+			// attempts from making it into the form HTML. Badly coded templates may still be
+			// affected, but if have_reg_ip is checked for, then you're fine.
+			$form->reg_ip_addr = $_POST['user_registration_ip'];
+			echo $form->render();
+			return false;
+		}
+		
+		#
+		# END VALIDATION
+		#
+	}
+	else if ( isset($_POST['action']['go']) || ( isset($_GET['src']) && $_GET['src'] == 'get' ) || ($pathsuser = $paths->getParam(0)) )
+	{
+		if ( isset($_GET['user']) )
+		{
+			$username =& $_GET['user'];
+		}
+		else if ( isset($_GET['username']) )
+		{
+			$username =& $_GET['username'];
+		}
+		else if ( isset($_POST['username']) )
+		{
+			$username =& $_POST['username'];
+		}
+		else if ( $pathsuser )
+		{
+			$username = str_replace('_', ' ', dirtify_page_id($pathsuser));
+		}
+		else
+		{
+			echo 'No username provided';
+			return false;
+		}
+		$q = $db->sql_query('SELECT u.user_id AS authoritative_uid, u.username, u.email, u.real_name, u.signature, u.account_active, u.user_level, u.user_rank, u.user_title, u.user_has_avatar, u.avatar_type, u.user_registration_ip, x.* FROM '.table_prefix.'users AS u
+ 													LEFT JOIN '.table_prefix.'users_extra AS x
+ 														ON ( u.user_id = x.user_id OR x.user_id IS NULL )
+ 													WHERE ( ' . ENANO_SQLFUNC_LOWERCASE . '(u.username) = \'' . $db->escape(strtolower($username)) . '\' OR u.username = \'' . $db->escape($username) . '\' ) AND u.user_id != 1;');
+		if ( !$q )
+			$db->_die();
+		
+		if ( $db->numrows() < 1 )
+		{
+			echo '<div class="error-box">' . $lang->get('acpum_err_bad_username') . '</div>';
+		}
+		else
+		{
+			$row = $db->fetchrow();
+			$row['user_id'] = $row['authoritative_uid'];
+			$form = new Admin_UserManager_SmartForm();
+			$form->user_id   = $row['user_id'];
+			$form->username  = $row['username'];
+			$form->email     = $row['email'];
+			$form->real_name = $row['real_name'];
+			$form->signature = $row['signature'];
+			$form->user_level= $row['user_level'];
+			$form->user_rank = $row['user_rank'];
+			$form->user_title= $row['user_title'];
+			$form->account_active = ( $row['account_active'] == 1 );
+			$form->email_public   = ( $row['email_public'] == 1 );
+			$form->has_avatar     = ( $row['user_has_avatar'] == 1 );
+			$form->avi_type       = $row['avatar_type'];
+			$form->im = array(
+					'aim' => $row['user_aim'],
+					'yahoo' => $row['user_yahoo'],
+					'msn' => $row['user_msn'],
+					'xmpp' => $row['user_xmpp']
+				);
+			$form->contact = array(
+					'homepage' => $row['user_homepage'],
+					'location' => $row['user_location'],
+					'job'      => $row['user_job'],
+					'hobbies'  => $row['user_hobbies'],
+				);
+			$form->email_public = ( $row['email_public'] == 1 );
+			$form->reg_ip_addr = ( $row['user_registration_ip'] ) ? $row['user_registration_ip'] : '';
+			$html = $form->render();
+			if ( !$html )
+			{
+				echo 'Internal error: form processor returned false';
+			}
+			else
+			{
+				echo $html;
+			}
+			return true;
+		}
+	}
+	else if ( isset($_POST['action']['clear_sessions']) )
+	{
+		if ( defined('ENANO_DEMO_MODE') )
+		{
+			echo '<div class="error-box">' . $lang->get('acpum_err_sessionclear_demo') . '</div>';
+		}
+		else
+		{
+			// Get the current session information so the user doesn't get logged out
+			$aes = AESCrypt::singleton(AES_BITS, AES_BLOCKSIZE);
+			$sk = md5($session->sid_super);
+			$qb = $db->sql_query('SELECT session_key,salt,auth_level,source_ip,time FROM '.table_prefix.'session_keys WHERE session_key=\''.$sk.'\' AND user_id='.$session->user_id.' AND auth_level='.USER_LEVEL_ADMIN);
+			if ( !$qb )
+			{
+				die('Error selecting session key info block B: '.$db->get_error());
+			}
+			if ( $db->numrows($qb) < 1 )
+			{
+				die('Error: cannot read admin session info block B, aborting table clear process');
+			}
+			$qa = $db->sql_query('SELECT session_key,salt,auth_level,source_ip,time FROM '.table_prefix.'session_keys WHERE session_key=\''.md5($session->sid).'\' AND user_id='.$session->user_id.' AND auth_level='.USER_LEVEL_MEMBER);
+			if ( !$qa )
+			{
+				die('Error selecting session key info block A: '.$db->get_error());
+			}
+			if ( $db->numrows($qa) < 1 )
+			{
+				die('Error: cannot read user session info block A, aborting table clear process');
+			}
+			$ra = $db->fetchrow($qa);
+			$rb = $db->fetchrow($qb);
+			$db->free_result($qa);
+			$db->free_result($qb);
+			
+			$db->sql_query('DELETE FROM '.table_prefix.'session_keys;');
+			$db->sql_query('INSERT INTO '.table_prefix.'session_keys( session_key,salt,user_id,auth_level,source_ip,time ) VALUES( \''.$ra['session_key'].'\', \'' . $db->escape($ra['salt']) . '\', \''.$session->user_id.'\', \''.$ra['auth_level'].'\', \''.$ra['source_ip'].'\', '.$ra['time'].' ),( \''.$rb['session_key'].'\', \'' . $db->escape($rb['salt']) . '\', \''.$session->user_id.'\', \''.$rb['auth_level'].'\', \''.$rb['source_ip'].'\', '.$rb['time'].' )');
+			
+			echo '<div class="info-box">' . $lang->get('acpum_msg_sessionclear_success') . '</div>';
+		}
+	}
+	echo '<form action="' . makeUrlNS('Special', 'Administration', 'module=' . $paths->cpage['module'], true) . '" method="post" enctype="multipart/form-data" onsubmit="if ( !submitAuthorized ) return false;">';
+	echo '<h3>' . $lang->get('acpum_heading_main') . '</h3>';
+	echo '<p>' . $lang->get('acpum_hint_intro') . '</p>';
+	echo '<table border="0">
+					<tr>
+						<td><b>' . $lang->get('acpum_field_search_user') . '</b><br />
+								<small>' . $lang->get('acpum_field_search_user_hint') . '</small>
+								</td>
+						<td style="width: 10px;"></td>
+						<td>' . $template->username_field('username') . '</td>
+						<td>
+							<input type="submit" name="action[go]" value="' . $lang->get('acpum_btn_search_user_go') . ' &raquo;" />
+						</td>
+					</tr>
+				</table>';
+	echo '<h3>' . $lang->get('acpum_heading_clear_sessions') . '</h3>';
+	echo '<p>' . $lang->get('acpum_hint_clear_sessions') . '</p>';
+	echo '<p><input type="submit" name="action[clear_sessions]" value="' . $lang->get('acpum_btn_clear_sessions') . '" /></p>';
+	echo '</form>';
+	
+	if(isset($_GET['action']) && isset($_GET['user']))
+	{
+		switch($_GET['action'])
+		{
+			case "activate":
+				$e = $db->sql_query('SELECT activation_key FROM '.table_prefix.'users WHERE username=\'' . $db->escape($_GET['user']) . '\'');
+				if ( $e )
+				{
+					// attempt to activate the account
+					$row = $db->fetchrow();
+					$db->free_result();
+					if ( $session->activate_account($_GET['user'], $row['activation_key']) )
+					{
+						echo '<div class="info-box">' . $lang->get('acpum_msg_activate_success', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
+						$db->sql_query('DELETE FROM '.table_prefix.'logs WHERE time_id=' . $db->escape($_GET['logid']));
+					}
+					else
+					{
+						echo '<div class="warning-box">' . $lang->get('acpum_err_activate_fail', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
+					}
+				}
+				else
+				{
+					echo '<div class="error-box">Error activating account: '.$db->get_error().'</div>';
+				}
+				break;
+			case "sendemail":
+				if ( $session->send_activation_mail($_GET['user'] ) )
+				{
+					echo '<div class="info-box">' . $lang->get('acpum_msg_activate_email_success', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
+					$db->sql_query('DELETE FROM '.table_prefix.'logs WHERE time_id=' . $db->escape($_GET['logid']));
+				}
+				else
+				{
+					echo '<div class="error-box">' . $lang->get('acpum_err_activate_email_fail', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
+				}
+				break;
+			case "deny":
+				$e = $db->sql_query('DELETE FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\' AND time_id=\'' . $db->escape($_GET['logid']) . '\';');
+				if ( !$e )
+				{
+					echo '<div class="error-box">Error during row deletion: '.$db->get_error().'</div>';
+				}
+				else
+				{
+					echo '<div class="info-box">' . $lang->get('acpum_msg_activate_deny_success', array('username' => htmlspecialchars($_GET['user']))) . '</div>';
+				}
+				break;
+		}
+	}
+	$q = $db->sql_query('SELECT l.log_type, l.action, l.time_id, l.date_string, l.author, l.edit_summary, u.user_coppa FROM '.table_prefix.'logs AS l
+ 												LEFT JOIN '.table_prefix.'users AS u
+ 													ON ( u.username = l.edit_summary OR u.username IS NULL )
+ 												WHERE log_type=\'admin\' AND action=\'activ_req\' ORDER BY time_id DESC;');
+	if($q)
+	{
+		if($db->numrows() > 0)
+		{
+			$n = $db->numrows();
+			$str = ( $n == 1 ) ?
+				$lang->get('acpum_heading_activation_one') :
+				$lang->get('acpum_heading_activation_plural', array('count' => strval($n)));
+				
+			echo '<h3>' . $str . '</h3>';
+				
+			echo '<div class="tblholder">
+							<table border="0" cellspacing="1" cellpadding="4" width="100%">
+								<tr>
+									<th>' . $lang->get('acpum_col_activate_timestamp') . '</th>
+									<th>' . $lang->get('acpum_col_activate_requestedby') . '</th>
+									<th>' . $lang->get('acpum_col_activate_requestedfor') . '</th>
+									<th>' . $lang->get('acpum_col_activate_coppauser') . '</th>
+									<th colspan="3">' . $lang->get('acpum_col_activate_actions') . '</th>
+								</tr>';
+			$cls = 'row2';
+			while($row = $db->fetchrow())
+			{
+				if($cls == 'row2') $cls = 'row1';
+				else $cls = 'row2';
+				$coppa = ( $row['user_coppa'] == '1' ) ? '<b>' . $lang->get('acpum_coppauser_yes') . '</b>' : $lang->get('acpum_coppauser_no');
+				echo '<tr>
+								<td class="'.$cls.'">'.enano_date(ED_DATE | ED_TIME, $row['time_id']).'</td>
+								<td class="'.$cls.'">'.$row['author'].'</td>
+								<td class="'.$cls.'">'.$row['edit_summary'].'</td>
+								<td style="text-align: center;" class="' . $cls . '">' . $coppa . '</td>
+								<td class="'.$cls.'" style="text-align: center;">
+									<a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&action=activate&user='.rawurlencode($row['edit_summary']).'&logid='.$row['time_id'], true).'">' . $lang->get('acpum_btn_activate_now') . '</a>
+								</td>
+								<td class="'.$cls.'" style="text-align: center;">
+									<a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&action=sendemail&user='.rawurlencode($row['edit_summary']).'&logid='.$row['time_id'], true).'">' . $lang->get('acpum_btn_send_email') . '</a>
+								</td>
+								<td class="'.$cls.'" style="text-align: center;">
+									<a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&action=deny&user='.rawurlencode($row['edit_summary']).'&logid='.$row['time_id'], true).'">' . $lang->get('acpum_btn_activate_deny') . '</a>
+								</td>
+							</tr>';
+			}
+			echo '</table>';
+			echo '</div>';
+		}
+		$db->free_result();
+	}
+	
+	acp_usermanager_lockouts();
 }
 
 /**
@@ -606,698 +606,698 @@
 
 class Admin_UserManager_SmartForm
 {
-  
-  /**
-   * Universally Unique Identifier (UUID) for this editor instance. Used to unique-itize Javascript functions and whatnot.
-   * @var string
-   */
-  
-  var $uuid = '';
-  
-  /**
-   * User ID that we're editing.
-   * @var int
-   */
-  
-  var $user_id = 0;
-  
-  /**
-   * Username
-   * @var string
-   */
-  
-  var $username = '';
-  
-  /**
-   * E-mail address
-   * @var string
-   */
-  
-  var $email = '';
-  
-  /**
-   * Real name
-   * @var string
-   */
-  
-  var $real_name = '';
-  
-  /**
-   * Signature
-   * @var string
-   */
-  
-  var $signature = '';
-  
-  /**
-   * IM contact information
-   * @var array
-   */
-   
-  var $im = array();
-  
-  /**
-   * Real-life contact info
-   * @var array
-   */
-  
-  var $contact = array();
-  
-  /**
-   * User level
-   * @var int
-   */
-  
-  var $user_level = USER_LEVEL_MEMBER;
-  
-  /**
-   * User-specific user rank
-   * @var int
-   */
-  
-  var $user_rank = NULL;
-  
-  /**
-   * User's custom title
-   * @var int
-   */
-  
-  var $user_title = '';
-  
-  /**
-   * Account activated
-   * @var bool
-   */
-  
-  var $account_active = true;
-  
-  /**
-   * Email public switch
-   * @var bool
-   */
-  
-  var $email_public = false;
-  
-  /**
-   * Whether the user has an avatar or not.
-   * @var bool
-   */
-  
-  var $has_avatar = false;
-  
-  /**
-   * The type of avatar the user has. One of "jpg", "png", or "gif".
-   * @var string
-   */
-  
-  var $avi_type = 'png';
-  
-  /**
-   * The IP address of the user during registration
-   * @var string
-   */
-  
-  var $reg_ip_addr = '';
-  
-  /**
-   * Constructor.
-   */
-  
-  function Admin_UserManager_SmartForm()
-  {
-    $this->uuid = md5( mt_rand() . microtime() );
-  }
-  
-  /**
-   * Renders and returns the finished form.
-   * @return string
-   */
-  
-  function render()
-  {
-    global $db, $session, $paths, $template, $plugins; // Common objects
-    global $lang;
-    global $dh_supported;
-    if ( file_exists( ENANO_ROOT . "/themes/$template->theme/admin_usermanager_form.tpl" ) )
-    {
-      $parser = $template->makeParser('admin_usermanager_form.tpl');
-    }
-    else
-    {
-      $tpl_code = <<<EOF
-      <!-- Start of user edit form -->
-      
-        <script type="text/javascript">
-          function userform_{UUID}_chpasswd()
-          {
-            var link = document.getElementById('userform_{UUID}_pwlink');
-            var form = document.getElementById('userform_{UUID}_pwform');
-            domOpacity(link, 100, 0, 500);
-            domObjChangeOpac(0, form);
-            setTimeout("var link = document.getElementById('userform_{UUID}_pwlink'); var form = document.getElementById('userform_{UUID}_pwform'); link.style.display = 'none'; form.style.display = 'block'; domOpacity(form, 0, 100, 500);", 550);
-            <!-- BEGINNOT same_user -->document.forms['useredit_{UUID}'].changing_pw.value = 'yes';<!-- END same_user -->
-          }
-          
-          function userform_{UUID}_chpasswd_cancel()
-          {
-            var link = document.getElementById('userform_{UUID}_pwlink');
-            var form = document.getElementById('userform_{UUID}_pwform');
-            domOpacity(form, 100, 0, 500);
-            domObjChangeOpac(0, link);
-            setTimeout("var link = document.getElementById('userform_{UUID}_pwlink'); var form = document.getElementById('userform_{UUID}_pwform'); form.style.display = 'none'; link.style.display = 'block'; domOpacity(link, 0, 100, 500);", 550);
-            <!-- BEGINNOT same_user -->document.forms['useredit_{UUID}'].changing_pw.value = 'no';<!-- END same_user -->
-          }
-          
-          function userform_{UUID}_validate()
-          {
-            var form = document.forms['useredit_{UUID}'];
-            <!-- BEGINNOT same_user -->
-            if ( form.changing_pw.value == 'yes' )
-            {
-              return runEncryption(true);
-            }
-            <!-- END same_user -->
-            return true;
-          }
-        </script>
-      
-        <form action="{FORM_ACTION}" method="post" name="useredit_{UUID}" enctype="multipart/form-data" onsubmit="return userform_{UUID}_validate();">
-        
-          <input name="user_id" value="{USER_ID}" type="hidden" />
-        
-          <div class="tblholder">
-            <table border="0" cellspacing="1" cellpadding="4">
-            
-              <!-- Heading -->
-            
-              <tr>
-                <th colspan="2">
-                  {lang:acpum_heading_editing_user} {USERNAME}
-                </th>
-              </tr>
-              
-              <!-- Basic options (stored in enano_users) -->
-              
-                <tr>
-                  <th colspan="2" class="subhead">
-                    {lang:acpum_heading_basic_options}
-                  </th>
-                </tr>
-                
-                <tr>
-                  <td class="row2" style="width: 25%;">
-                    {lang:acpum_field_username}<br />
-                    <small>{lang:acpum_field_username_hint}</small>
-                  </td>
-                  <td class="row1" style="width: 75%;">
-                    <input type="text" name="username" value="{USERNAME}" size="40" <!-- BEGIN same_user -->disabled="disabled" <!-- END same_user -->/>
-                    <!-- BEGIN same_user --><small>{lang:acpum_msg_same_user_username}</small><!-- END same_user -->
-                  </td>
-                </tr>
-                
-                <tr>
-                  <td class="row2">
-                    {lang:acpum_field_password}
-                    <!-- BEGIN password_meter -->
-                    <br />
-                    <small>{lang:acpum_field_password_hint}</small>
-                    <!-- END password_meter -->
-                  </td>
-                  <td class="row1">
-                    <div id="userform_{UUID}_pwlink">
-                      <b>{lang:acpum_msg_password_unchanged}</b> <a href="#" onclick="userform_{UUID}_chpasswd(); return false;">{lang:acpum_btn_reset_password}</a>
-                    </div>
-                    <div id="userform_{UUID}_pwform" style="display: none;">
-                      <!-- BEGIN same_user -->
-                        {lang:acpum_msg_same_user_password} <a href="#" onclick="userform_{UUID}_chpasswd_cancel(); return false;">{lang:etc_cancel}</a>
-                      <!-- BEGINELSE same_user -->
-                      <input type="hidden" name="changing_pw" value="no" />
-                      {AES_FORM}
-                      <table border="0" style="background-color: transparent;" cellspacing="0" cellpadding="0">
-                        <tr>
-                          <td colspan="2">
-                            <b>{lang:acpum_field_password_title}</b>
-                          </td>
-                        </tr>
-                        <tr>
-                          <td>{lang:acpum_field_newpassword}</td>
-                          <td>
-                          <!-- BEGIN password_meter -->
-                            <input type="password" name="new_password" value="" onkeyup="password_score_field(this);" /><span class="password-checker" style="font-weight: bold; color: #A0A0A0"> Waiting for l10n init</span>
-                          <!-- BEGINELSE password_meter -->
-                            <input type="password" name="new_password" value="" />
-                          <!-- END password_meter -->
-                          <!-- BEGIN password_meter -->
-                            <div id="pwmeter" style="margin: 4px 0; height: 8px;"></div>
-                          <!-- END password_meter -->
-                          </td>
-                        </tr>
-                        <tr>
-                          <td>{lang:acpum_field_newpassword_confirm}</td>
-                          <td><input type="password" name="new_password_confirm" value="" /></td>
-                        </tr>
-                        <tr>
-                          <td colspan="2">
-                            <a href="#" onclick="userform_{UUID}_chpasswd_cancel(); return false;">{lang:etc_cancel}</a>
-                          </td>
-                        </tr>
-                      </table>
-                      <!-- END same_user -->
-                    </div>
-                  </td>
-                </tr>
-                
-                <tr>
-                  <td class="row2" style="width: 25%;">
-                    {lang:acpum_field_email}
-                  </td>
-                  <td class="row1" style="width: 75%;">
-                    <input type="text" name="email" value="{EMAIL}" size="40" <!-- BEGIN same_user -->disabled="disabled" <!-- END same_user -->/>
-                    <!-- BEGIN same_user --><small>{lang:acpum_msg_same_user_email}</small><!-- END same_user -->
-                  </td>
-                </tr>
-                
-                <tr>
-                  <td class="row2" style="width: 25%;">
-                    {lang:acpum_field_realname}
-                  </td>
-                  <td class="row1" style="width: 75%;">
-                    <input type="text" name="real_name" value="{REAL_NAME}" size="40" <!-- BEGIN same_user -->disabled="disabled" <!-- END same_user -->/>
-                    <!-- BEGIN same_user --><small>{lang:acpum_msg_same_user_realname}</small><!-- END same_user -->
-                  </td>
-                </tr>
-                
-                <tr>
-                  <td class="row2" style="width: 25%;">
-                    {lang:acpum_field_signature}
-                  </td>
-                  <td class="row1" style="width: 75%;">
-                    {SIGNATURE_FIELD}
-                  </td>
-                </tr>
-                
-                <tr>
-                  <td class="row2" style="width: 25%;">
-                    {lang:acpum_field_usertitle}<br />
-                    <small>
-                      {lang:acpum_field_usertitle_hint}
-                    </small>
-                  </td>
-                  <td class="row1" style="width: 75%;">
-                    <input type="text" name="user_title" value="{USER_TITLE}" />
-                  </td>
-                </tr>
-                
-                
-                
-              <!-- / Basic options -->
-              
-              <!-- Extended options (anything in enano_users_extra) -->
-              
-                <tr>
-                  <th class="subhead" colspan="2">
-                    {lang:acpum_heading_imcontact}
-                  </th>
-                <tr>
-                  <td class="row2">{lang:acpum_field_aim}</td>
-                  <td class="row1"><input type="text" name="imaddr_aim" value="{IM_AIM}" size="30" /></td>
-                </tr>
-                <tr>
-                  <td class="row2">{lang:acpum_field_wlm}<br /><small>{lang:acpum_field_wlm_hint}</small></td>
-                  <td class="row1"><input type="text" name="imaddr_msn" value="{IM_WLM}" size="30" /></td>
-                </tr>
-                <tr>
-                  <td class="row2">{lang:acpum_field_yim}</td>
-                  <td class="row1"><input type="text" name="imaddr_yahoo" value="{IM_YAHOO}" size="30" /></td>
-                </tr>
-                <tr>
-                  <td class="row2">{lang:acpum_field_xmpp}</td>
-                  <td class="row1"><input type="text" name="imaddr_xmpp" value="{IM_XMPP}" size="30" /></td>
-                </tr>
-                <tr>
-                  <th class="subhead" colspan="2">
-                    {lang:acpum_heading_contact_extra}
-                  </th>
-                </tr>
-                <tr>
-                  <td class="row2">{lang:acpum_field_homepage}<br /><small>{lang:acpum_field_homepage_hint}</small></td>
-                  <td class="row1"><input type="text" name="homepage" value="{HOMEPAGE}" size="30" /></td>
-                </tr>
-                <tr>
-                  <td class="row2">{lang:acpum_field_location}</td>
-                  <td class="row1"><input type="text" name="location" value="{LOCATION}" size="30" /></td>
-                </tr>
-                <tr>
-                  <td class="row2">{lang:acpum_field_job}</td>
-                  <td class="row1"><input type="text" name="occupation" value="{JOB}" size="30" /></td>
-                </tr>
-                <tr>
-                  <td class="row2">{lang:acpum_field_hobbies}</td>
-                  <td class="row1"><input type="text" name="hobbies" value="{HOBBIES}" size="30" /></td>
-                </tr>
-                <tr>
-                  <td class="row2"><label for="chk_email_public_{UUID}">{lang:acpum_field_email_public}</label><br /><small>{lang:acpum_field_email_public_hint}</small></td>
-                  <td class="row1"><input type="checkbox" id="chk_email_public_{UUID}" name="email_public" <!-- BEGIN email_public -->checked="checked" <!-- END email_public -->size="30" /></td>
-                </tr>
-              
-              <!-- / Extended options -->
-              
-              <!-- Avatar settings -->
-              
-                <tr>
-                  <th class="subhead" colspan="2">
-                    {lang:acpum_avatar_heading}
-                  </th>
-                </tr>
-                
-                <tr>
-                  <td class="row2">
-                    {lang:usercp_avatar_label_current}
-                  </td>
-                  <td class="row1">
-                    <!-- BEGIN user_has_avatar -->
-                      <img alt="{AVATAR_ALT}" src="{AVATAR_SRC}" />
-                    <!-- BEGINELSE user_has_avatar -->
-                      {lang:acpum_avatar_image_none}
-                    <!-- END user_has_avatar -->
-                  </td>
-                </tr>
-                
-                <tr>
-                  <td class="row2">
-                    {lang:acpum_avatar_lbl_change}
-                  </td>
-                  <td class="row1" id="avatar_upload_btns_{UUID}">
-                    <script type="text/javascript">
-                      function admincp_users_avatar_set_{UUID}(elParent)
-                      {
-                        $('td#avatar_upload_btns_{UUID} > div:visible').hide('blind');
-                        switch(elParent.value)
-                        {
-                          case 'set_http':
-                            $('#avatar_upload_http_{UUID}').show('blind');
-                            break;
-                          case 'set_file':
-                            $('#avatar_upload_file_{UUID}').show('blind');
-                            break;
-                          case 'set_gravatar':
-                            $('#avatar_upload_gravatar_{UUID}').show('blind');
-                            break;
-                        }
-                      }
-                    </script>
-                    <label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="keep" checked="checked" /> {lang:acpum_avatar_lbl_keep}</label><br />
-                    <label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="remove" /> {lang:acpum_avatar_lbl_remove}</label><br />
-                    <label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="set_http" /> {lang:acpum_avatar_lbl_set_http}</label><br />
-                      <div id="avatar_upload_http_{UUID}" style="display: none; margin: 10px 0 0 2.2em;">
-                        {lang:usercp_avatar_lbl_url} <input type="text" name="avatar_http_url" size="40" value="http://" /><br />
-                        <small>{lang:usercp_avatar_lbl_url_desc} {lang:usercp_avatar_limits}</small>
-                      </div>
-                    <label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="set_file" /> {lang:acpum_avatar_lbl_set_file}</label><br />
-                      <div id="avatar_upload_file_{UUID}" style="display: none; margin: 10px 0 0 2.2em;">
-                        {lang:usercp_avatar_lbl_file} <input type="file" name="avatar_file" size="40" value="http://" /><br />
-                        <small>{lang:usercp_avatar_lbl_file_desc} {lang:usercp_avatar_limits}</small>
-                      </div>
-                    <label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="set_gravatar" /> {lang:acpum_avatar_lbl_set_gravatar} <img alt=" " src="{GRAVATAR_URL}" /></label><br />
-                      <div id="avatar_upload_gravatar_{UUID}"></div>
-                  </td>
-                </tr>
-                
-              <!-- / Avatar settings -->
-              
-              <!-- Administrator-only options -->
-              
-                <tr>
-                  <th class="subhead" colspan="2">
-                    {lang:acpum_heading_adminonly}
-                  </th>
-                </tr>
-                
-                <tr>
-                  <td class="row2">{lang:acpum_field_active_title}<br />
-                                   <small>{lang:acpum_field_active_hint}</small>
-                                   </td>
-                  <td class="row1"><label><input type="checkbox" name="account_active" <!-- BEGIN account_active -->checked="checked" <!-- END account_active -->/> {lang:acpum_field_active}</label></td>
-                </tr>
-                
-                <tr>
-                  <td class="row2">
-                    {lang:acpum_field_userlevel}<br />
-                    <small>{lang:acpum_field_userlevel_hint}</small>
-                  </td>
-                  <td class="row1">
-                    <select name="user_level">
-                      <option value="{USER_LEVEL_MEMBER}"<!-- BEGIN ul_member --> selected="selected"<!-- END ul_member -->>{lang:userfuncs_ml_level_member}</option>
-                      <option value="{USER_LEVEL_MOD}"<!-- BEGIN ul_mod --> selected="selected"<!-- END ul_mod -->>{lang:userfuncs_ml_level_mod}</option>
-                      <option value="{USER_LEVEL_ADMIN}"<!-- BEGIN ul_admin --> selected="selected"<!-- END ul_admin -->>{lang:userfuncs_ml_level_admin}</option>
-                    </select>
-                  </td>
-                </tr>
-                
-                <tr>
-                  <td class="row2">
-                    {lang:acpum_field_userrank}<br />
-                    <small>{lang:acpum_field_userrank_hint}</small>
-                  </td>
-                  <td class="row1">
-                    <select name="user_rank">
-                      {RANK_LIST}
-                    </select>
-                  </td>
-                </tr>
-                
-                <!-- BEGIN have_reg_ip -->
-                <tr>
-                  <td class="row2">
-                    {lang:acpum_field_reg_ip}
-                  </td>
-                  <td class="row1">
-                    {REG_IP_ADDR}
-                    <input type="hidden" name="user_registration_ip" value="{REG_IP_ADDR}" />
-                  </td>
-                </tr>
-                <!-- BEGINELSE have_reg_ip -->
-                <input type="hidden" name="user_registration_ip" value="" />
-                <!-- END have_reg_ip -->
-                
-                <tr>
-                  <td class="row2">
-                    {lang:acpum_field_deleteaccount_title}
-                  </td>
-                  <td class="row1">
-                  <label><input type="checkbox" name="delete_account" onclick="var d = (this.checked) ? 'block' : 'none'; document.getElementById('delete_blurb_{UUID}').style.display = d;" /> {lang:acpum_field_deleteaccount}</label>
-                    <div id="delete_blurb_{UUID}" style="display: none;">
-                      <!-- BEGIN same_user -->
-                      <!-- Obnoxious I know, but it's needed. -->
-                      <p><b>{lang:acpum_msg_delete_own_account}</b></p>
-                      <!-- END same_user -->
-                      <p><small>{lang:acpum_field_deleteaccount_hint}</small></p>
-                    </div>
-                  </td>
-                </tr>
-                </tr>
-              
-              <!-- Save button -->
-              <tr>
-                <th colspan="2">
-                  <input type="submit" name="action[save]" value="{lang:acpum_btn_save}" style="font-weight: bold;" />
-                  <input type="submit" name="action[noop]" value="{lang:etc_cancel}" style="font-weight: normal;" />
-                </th>
-              </tr>
-            
-            </table>
-          </div>
-        
-        </form>
-        
-        <!-- BEGINNOT same_user -->
-        <script type="text/javascript">
-        password_score_field(document.forms['useredit_{UUID}'].new_password);
-        </script>
-        <!-- END same_user -->
-        
-        {AES_JAVASCRIPT}
-      <!-- Conclusion of user edit form -->
+	
+	/**
+ 	* Universally Unique Identifier (UUID) for this editor instance. Used to unique-itize Javascript functions and whatnot.
+ 	* @var string
+ 	*/
+	
+	var $uuid = '';
+	
+	/**
+ 	* User ID that we're editing.
+ 	* @var int
+ 	*/
+	
+	var $user_id = 0;
+	
+	/**
+ 	* Username
+ 	* @var string
+ 	*/
+	
+	var $username = '';
+	
+	/**
+ 	* E-mail address
+ 	* @var string
+ 	*/
+	
+	var $email = '';
+	
+	/**
+ 	* Real name
+ 	* @var string
+ 	*/
+	
+	var $real_name = '';
+	
+	/**
+ 	* Signature
+ 	* @var string
+ 	*/
+	
+	var $signature = '';
+	
+	/**
+ 	* IM contact information
+ 	* @var array
+ 	*/
+ 	
+	var $im = array();
+	
+	/**
+ 	* Real-life contact info
+ 	* @var array
+ 	*/
+	
+	var $contact = array();
+	
+	/**
+ 	* User level
+ 	* @var int
+ 	*/
+	
+	var $user_level = USER_LEVEL_MEMBER;
+	
+	/**
+ 	* User-specific user rank
+ 	* @var int
+ 	*/
+	
+	var $user_rank = NULL;
+	
+	/**
+ 	* User's custom title
+ 	* @var int
+ 	*/
+	
+	var $user_title = '';
+	
+	/**
+ 	* Account activated
+ 	* @var bool
+ 	*/
+	
+	var $account_active = true;
+	
+	/**
+ 	* Email public switch
+ 	* @var bool
+ 	*/
+	
+	var $email_public = false;
+	
+	/**
+ 	* Whether the user has an avatar or not.
+ 	* @var bool
+ 	*/
+	
+	var $has_avatar = false;
+	
+	/**
+ 	* The type of avatar the user has. One of "jpg", "png", or "gif".
+ 	* @var string
+ 	*/
+	
+	var $avi_type = 'png';
+	
+	/**
+ 	* The IP address of the user during registration
+ 	* @var string
+ 	*/
+	
+	var $reg_ip_addr = '';
+	
+	/**
+ 	* Constructor.
+ 	*/
+	
+	function Admin_UserManager_SmartForm()
+	{
+		$this->uuid = md5( mt_rand() . microtime() );
+	}
+	
+	/**
+ 	* Renders and returns the finished form.
+ 	* @return string
+ 	*/
+	
+	function render()
+	{
+		global $db, $session, $paths, $template, $plugins; // Common objects
+		global $lang;
+		global $dh_supported;
+		if ( file_exists( ENANO_ROOT . "/themes/$template->theme/admin_usermanager_form.tpl" ) )
+		{
+			$parser = $template->makeParser('admin_usermanager_form.tpl');
+		}
+		else
+		{
+			$tpl_code = <<<EOF
+			<!-- Start of user edit form -->
+			
+				<script type="text/javascript">
+					function userform_{UUID}_chpasswd()
+					{
+						var link = document.getElementById('userform_{UUID}_pwlink');
+						var form = document.getElementById('userform_{UUID}_pwform');
+						domOpacity(link, 100, 0, 500);
+						domObjChangeOpac(0, form);
+						setTimeout("var link = document.getElementById('userform_{UUID}_pwlink'); var form = document.getElementById('userform_{UUID}_pwform'); link.style.display = 'none'; form.style.display = 'block'; domOpacity(form, 0, 100, 500);", 550);
+						<!-- BEGINNOT same_user -->document.forms['useredit_{UUID}'].changing_pw.value = 'yes';<!-- END same_user -->
+					}
+					
+					function userform_{UUID}_chpasswd_cancel()
+					{
+						var link = document.getElementById('userform_{UUID}_pwlink');
+						var form = document.getElementById('userform_{UUID}_pwform');
+						domOpacity(form, 100, 0, 500);
+						domObjChangeOpac(0, link);
+						setTimeout("var link = document.getElementById('userform_{UUID}_pwlink'); var form = document.getElementById('userform_{UUID}_pwform'); form.style.display = 'none'; link.style.display = 'block'; domOpacity(link, 0, 100, 500);", 550);
+						<!-- BEGINNOT same_user -->document.forms['useredit_{UUID}'].changing_pw.value = 'no';<!-- END same_user -->
+					}
+					
+					function userform_{UUID}_validate()
+					{
+						var form = document.forms['useredit_{UUID}'];
+						<!-- BEGINNOT same_user -->
+						if ( form.changing_pw.value == 'yes' )
+						{
+							return runEncryption(true);
+						}
+						<!-- END same_user -->
+						return true;
+					}
+				</script>
+			
+				<form action="{FORM_ACTION}" method="post" name="useredit_{UUID}" enctype="multipart/form-data" onsubmit="return userform_{UUID}_validate();">
+				
+					<input name="user_id" value="{USER_ID}" type="hidden" />
+				
+					<div class="tblholder">
+						<table border="0" cellspacing="1" cellpadding="4">
+						
+							<!-- Heading -->
+						
+							<tr>
+								<th colspan="2">
+									{lang:acpum_heading_editing_user} {USERNAME}
+								</th>
+							</tr>
+							
+							<!-- Basic options (stored in enano_users) -->
+							
+								<tr>
+									<th colspan="2" class="subhead">
+										{lang:acpum_heading_basic_options}
+									</th>
+								</tr>
+								
+								<tr>
+									<td class="row2" style="width: 25%;">
+										{lang:acpum_field_username}<br />
+										<small>{lang:acpum_field_username_hint}</small>
+									</td>
+									<td class="row1" style="width: 75%;">
+										<input type="text" name="username" value="{USERNAME}" size="40" <!-- BEGIN same_user -->disabled="disabled" <!-- END same_user -->/>
+										<!-- BEGIN same_user --><small>{lang:acpum_msg_same_user_username}</small><!-- END same_user -->
+									</td>
+								</tr>
+								
+								<tr>
+									<td class="row2">
+										{lang:acpum_field_password}
+										<!-- BEGIN password_meter -->
+										<br />
+										<small>{lang:acpum_field_password_hint}</small>
+										<!-- END password_meter -->
+									</td>
+									<td class="row1">
+										<div id="userform_{UUID}_pwlink">
+											<b>{lang:acpum_msg_password_unchanged}</b> <a href="#" onclick="userform_{UUID}_chpasswd(); return false;">{lang:acpum_btn_reset_password}</a>
+										</div>
+										<div id="userform_{UUID}_pwform" style="display: none;">
+											<!-- BEGIN same_user -->
+												{lang:acpum_msg_same_user_password} <a href="#" onclick="userform_{UUID}_chpasswd_cancel(); return false;">{lang:etc_cancel}</a>
+											<!-- BEGINELSE same_user -->
+											<input type="hidden" name="changing_pw" value="no" />
+											{AES_FORM}
+											<table border="0" style="background-color: transparent;" cellspacing="0" cellpadding="0">
+												<tr>
+													<td colspan="2">
+														<b>{lang:acpum_field_password_title}</b>
+													</td>
+												</tr>
+												<tr>
+													<td>{lang:acpum_field_newpassword}</td>
+													<td>
+													<!-- BEGIN password_meter -->
+														<input type="password" name="new_password" value="" onkeyup="password_score_field(this);" /><span class="password-checker" style="font-weight: bold; color: #A0A0A0"> Waiting for l10n init</span>
+													<!-- BEGINELSE password_meter -->
+														<input type="password" name="new_password" value="" />
+													<!-- END password_meter -->
+													<!-- BEGIN password_meter -->
+														<div id="pwmeter" style="margin: 4px 0; height: 8px;"></div>
+													<!-- END password_meter -->
+													</td>
+												</tr>
+												<tr>
+													<td>{lang:acpum_field_newpassword_confirm}</td>
+													<td><input type="password" name="new_password_confirm" value="" /></td>
+												</tr>
+												<tr>
+													<td colspan="2">
+														<a href="#" onclick="userform_{UUID}_chpasswd_cancel(); return false;">{lang:etc_cancel}</a>
+													</td>
+												</tr>
+											</table>
+											<!-- END same_user -->
+										</div>
+									</td>
+								</tr>
+								
+								<tr>
+									<td class="row2" style="width: 25%;">
+										{lang:acpum_field_email}
+									</td>
+									<td class="row1" style="width: 75%;">
+										<input type="text" name="email" value="{EMAIL}" size="40" <!-- BEGIN same_user -->disabled="disabled" <!-- END same_user -->/>
+										<!-- BEGIN same_user --><small>{lang:acpum_msg_same_user_email}</small><!-- END same_user -->
+									</td>
+								</tr>
+								
+								<tr>
+									<td class="row2" style="width: 25%;">
+										{lang:acpum_field_realname}
+									</td>
+									<td class="row1" style="width: 75%;">
+										<input type="text" name="real_name" value="{REAL_NAME}" size="40" <!-- BEGIN same_user -->disabled="disabled" <!-- END same_user -->/>
+										<!-- BEGIN same_user --><small>{lang:acpum_msg_same_user_realname}</small><!-- END same_user -->
+									</td>
+								</tr>
+								
+								<tr>
+									<td class="row2" style="width: 25%;">
+										{lang:acpum_field_signature}
+									</td>
+									<td class="row1" style="width: 75%;">
+										{SIGNATURE_FIELD}
+									</td>
+								</tr>
+								
+								<tr>
+									<td class="row2" style="width: 25%;">
+										{lang:acpum_field_usertitle}<br />
+										<small>
+											{lang:acpum_field_usertitle_hint}
+										</small>
+									</td>
+									<td class="row1" style="width: 75%;">
+										<input type="text" name="user_title" value="{USER_TITLE}" />
+									</td>
+								</tr>
+								
+								
+								
+							<!-- / Basic options -->
+							
+							<!-- Extended options (anything in enano_users_extra) -->
+							
+								<tr>
+									<th class="subhead" colspan="2">
+										{lang:acpum_heading_imcontact}
+									</th>
+								<tr>
+									<td class="row2">{lang:acpum_field_aim}</td>
+									<td class="row1"><input type="text" name="imaddr_aim" value="{IM_AIM}" size="30" /></td>
+								</tr>
+								<tr>
+									<td class="row2">{lang:acpum_field_wlm}<br /><small>{lang:acpum_field_wlm_hint}</small></td>
+									<td class="row1"><input type="text" name="imaddr_msn" value="{IM_WLM}" size="30" /></td>
+								</tr>
+								<tr>
+									<td class="row2">{lang:acpum_field_yim}</td>
+									<td class="row1"><input type="text" name="imaddr_yahoo" value="{IM_YAHOO}" size="30" /></td>
+								</tr>
+								<tr>
+									<td class="row2">{lang:acpum_field_xmpp}</td>
+									<td class="row1"><input type="text" name="imaddr_xmpp" value="{IM_XMPP}" size="30" /></td>
+								</tr>
+								<tr>
+									<th class="subhead" colspan="2">
+										{lang:acpum_heading_contact_extra}
+									</th>
+								</tr>
+								<tr>
+									<td class="row2">{lang:acpum_field_homepage}<br /><small>{lang:acpum_field_homepage_hint}</small></td>
+									<td class="row1"><input type="text" name="homepage" value="{HOMEPAGE}" size="30" /></td>
+								</tr>
+								<tr>
+									<td class="row2">{lang:acpum_field_location}</td>
+									<td class="row1"><input type="text" name="location" value="{LOCATION}" size="30" /></td>
+								</tr>
+								<tr>
+									<td class="row2">{lang:acpum_field_job}</td>
+									<td class="row1"><input type="text" name="occupation" value="{JOB}" size="30" /></td>
+								</tr>
+								<tr>
+									<td class="row2">{lang:acpum_field_hobbies}</td>
+									<td class="row1"><input type="text" name="hobbies" value="{HOBBIES}" size="30" /></td>
+								</tr>
+								<tr>
+									<td class="row2"><label for="chk_email_public_{UUID}">{lang:acpum_field_email_public}</label><br /><small>{lang:acpum_field_email_public_hint}</small></td>
+									<td class="row1"><input type="checkbox" id="chk_email_public_{UUID}" name="email_public" <!-- BEGIN email_public -->checked="checked" <!-- END email_public -->size="30" /></td>
+								</tr>
+							
+							<!-- / Extended options -->
+							
+							<!-- Avatar settings -->
+							
+								<tr>
+									<th class="subhead" colspan="2">
+										{lang:acpum_avatar_heading}
+									</th>
+								</tr>
+								
+								<tr>
+									<td class="row2">
+										{lang:usercp_avatar_label_current}
+									</td>
+									<td class="row1">
+										<!-- BEGIN user_has_avatar -->
+											<img alt="{AVATAR_ALT}" src="{AVATAR_SRC}" />
+										<!-- BEGINELSE user_has_avatar -->
+											{lang:acpum_avatar_image_none}
+										<!-- END user_has_avatar -->
+									</td>
+								</tr>
+								
+								<tr>
+									<td class="row2">
+										{lang:acpum_avatar_lbl_change}
+									</td>
+									<td class="row1" id="avatar_upload_btns_{UUID}">
+										<script type="text/javascript">
+											function admincp_users_avatar_set_{UUID}(elParent)
+											{
+												$('td#avatar_upload_btns_{UUID} > div:visible').hide('blind');
+												switch(elParent.value)
+												{
+													case 'set_http':
+														$('#avatar_upload_http_{UUID}').show('blind');
+														break;
+													case 'set_file':
+														$('#avatar_upload_file_{UUID}').show('blind');
+														break;
+													case 'set_gravatar':
+														$('#avatar_upload_gravatar_{UUID}').show('blind');
+														break;
+												}
+											}
+										</script>
+										<label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="keep" checked="checked" /> {lang:acpum_avatar_lbl_keep}</label><br />
+										<label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="remove" /> {lang:acpum_avatar_lbl_remove}</label><br />
+										<label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="set_http" /> {lang:acpum_avatar_lbl_set_http}</label><br />
+											<div id="avatar_upload_http_{UUID}" style="display: none; margin: 10px 0 0 2.2em;">
+												{lang:usercp_avatar_lbl_url} <input type="text" name="avatar_http_url" size="40" value="http://" /><br />
+												<small>{lang:usercp_avatar_lbl_url_desc} {lang:usercp_avatar_limits}</small>
+											</div>
+										<label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="set_file" /> {lang:acpum_avatar_lbl_set_file}</label><br />
+											<div id="avatar_upload_file_{UUID}" style="display: none; margin: 10px 0 0 2.2em;">
+												{lang:usercp_avatar_lbl_file} <input type="file" name="avatar_file" size="40" value="http://" /><br />
+												<small>{lang:usercp_avatar_lbl_file_desc} {lang:usercp_avatar_limits}</small>
+											</div>
+										<label><input onclick="admincp_users_avatar_set_{UUID}(this);" type="radio" name="avatar_action" value="set_gravatar" /> {lang:acpum_avatar_lbl_set_gravatar} <img alt=" " src="{GRAVATAR_URL}" /></label><br />
+											<div id="avatar_upload_gravatar_{UUID}"></div>
+									</td>
+								</tr>
+								
+							<!-- / Avatar settings -->
+							
+							<!-- Administrator-only options -->
+							
+								<tr>
+									<th class="subhead" colspan="2">
+										{lang:acpum_heading_adminonly}
+									</th>
+								</tr>
+								
+								<tr>
+									<td class="row2">{lang:acpum_field_active_title}<br />
+ 																	<small>{lang:acpum_field_active_hint}</small>
+ 																	</td>
+									<td class="row1"><label><input type="checkbox" name="account_active" <!-- BEGIN account_active -->checked="checked" <!-- END account_active -->/> {lang:acpum_field_active}</label></td>
+								</tr>
+								
+								<tr>
+									<td class="row2">
+										{lang:acpum_field_userlevel}<br />
+										<small>{lang:acpum_field_userlevel_hint}</small>
+									</td>
+									<td class="row1">
+										<select name="user_level">
+											<option value="{USER_LEVEL_MEMBER}"<!-- BEGIN ul_member --> selected="selected"<!-- END ul_member -->>{lang:userfuncs_ml_level_member}</option>
+											<option value="{USER_LEVEL_MOD}"<!-- BEGIN ul_mod --> selected="selected"<!-- END ul_mod -->>{lang:userfuncs_ml_level_mod}</option>
+											<option value="{USER_LEVEL_ADMIN}"<!-- BEGIN ul_admin --> selected="selected"<!-- END ul_admin -->>{lang:userfuncs_ml_level_admin}</option>
+										</select>
+									</td>
+								</tr>
+								
+								<tr>
+									<td class="row2">
+										{lang:acpum_field_userrank}<br />
+										<small>{lang:acpum_field_userrank_hint}</small>
+									</td>
+									<td class="row1">
+										<select name="user_rank">
+											{RANK_LIST}
+										</select>
+									</td>
+								</tr>
+								
+								<!-- BEGIN have_reg_ip -->
+								<tr>
+									<td class="row2">
+										{lang:acpum_field_reg_ip}
+									</td>
+									<td class="row1">
+										{REG_IP_ADDR}
+										<input type="hidden" name="user_registration_ip" value="{REG_IP_ADDR}" />
+									</td>
+								</tr>
+								<!-- BEGINELSE have_reg_ip -->
+								<input type="hidden" name="user_registration_ip" value="" />
+								<!-- END have_reg_ip -->
+								
+								<tr>
+									<td class="row2">
+										{lang:acpum_field_deleteaccount_title}
+									</td>
+									<td class="row1">
+									<label><input type="checkbox" name="delete_account" onclick="var d = (this.checked) ? 'block' : 'none'; document.getElementById('delete_blurb_{UUID}').style.display = d;" /> {lang:acpum_field_deleteaccount}</label>
+										<div id="delete_blurb_{UUID}" style="display: none;">
+											<!-- BEGIN same_user -->
+											<!-- Obnoxious I know, but it's needed. -->
+											<p><b>{lang:acpum_msg_delete_own_account}</b></p>
+											<!-- END same_user -->
+											<p><small>{lang:acpum_field_deleteaccount_hint}</small></p>
+										</div>
+									</td>
+								</tr>
+								</tr>
+							
+							<!-- Save button -->
+							<tr>
+								<th colspan="2">
+									<input type="submit" name="action[save]" value="{lang:acpum_btn_save}" style="font-weight: bold;" />
+									<input type="submit" name="action[noop]" value="{lang:etc_cancel}" style="font-weight: normal;" />
+								</th>
+							</tr>
+						
+						</table>
+					</div>
+				
+				</form>
+				
+				<!-- BEGINNOT same_user -->
+				<script type="text/javascript">
+				password_score_field(document.forms['useredit_{UUID}'].new_password);
+				</script>
+				<!-- END same_user -->
+				
+				{AES_JAVASCRIPT}
+			<!-- Conclusion of user edit form -->
 EOF;
-      $parser = $template->makeParserText($tpl_code);
-    }
-    
-    $this->username = htmlspecialchars($this->username);
-    $this->email = htmlspecialchars($this->email);
-    $this->user_id = intval($this->user_id);
-    $this->real_name = htmlspecialchars($this->real_name);
-    $this->signature = htmlspecialchars($this->signature);
-    $this->user_level = intval($this->user_level);
-    
-    $im_aim   = ( isset($this->im['aim']) )   ? $this->im['aim']   : false;
-    $im_yahoo = ( isset($this->im['yahoo']) ) ? $this->im['yahoo'] : false;
-    $im_msn   = ( isset($this->im['msn']) )   ? $this->im['msn']   : false;
-    $im_xmpp  = ( isset($this->im['xmpp']) )  ? $this->im['xmpp']  : false;
-    
-    $homepage = ( isset($this->contact['homepage']) ) ? $this->contact['homepage'] : false;
-    $location = ( isset($this->contact['location']) ) ? $this->contact['location'] : false;
-    $job = ( isset($this->contact['job']) ) ? $this->contact['job'] : false;
-    $hobbies = ( isset($this->contact['hobbies']) ) ? $this->contact['hobbies'] : false;
-    
-    if ( empty($this->username) )
-    {
-      // @error One or more required parameters not set
-      return 'Admin_UserManager_SmartForm::render: Invalid parameter ($form->username)';
-    }
-    
-    if ( empty($this->user_id) )
-    {
-      // @error One or more required parameters not set
-      return 'Admin_UserManager_SmartForm::render: Invalid parameter ($form->user_id)';
-    }
-    
-    if ( empty($this->email) )
-    {
-      // @error One or more required parameters not set
-      return 'Admin_UserManager_SmartForm::render: Invalid parameter ($form->email)';
-    }
-    
-    $form_action = makeUrlNS('Special', 'Administration', 'module=' . $paths->cpage['module'], true);
-    $aes_javascript = $session->aes_javascript("useredit_$this->uuid", 'new_password');
-    
-    // build rank list
-    $q = $db->sql_query('SELECT rank_id, rank_title FROM ' . table_prefix . 'ranks');
-    if ( !$q )
-      $db->_die();
-    $rank_list = '<option value="NULL"' . ( $this->user_rank === NULL ? ' selected="selected"' : '' ) . '>--</option>' . "\n";
-    while ( $row = $db->fetchrow() )
-    {
-      $rank_list .= '<option value="' . $row['rank_id'] . '"' . ( $row['rank_id'] == $this->user_rank ? ' selected="selected"' : '' ) . '>' . htmlspecialchars($lang->get($row['rank_title'])) . '</option>' . "\n";
-    }
-    
-    $parser->assign_vars(array(
-        'UUID' => $this->uuid,
-        'USERNAME' => $this->username,
-        'EMAIL' => $this->email,
-        'USER_ID' => $this->user_id,
-        'AES_FORM' => $session->generate_aes_form(),
-        'REAL_NAME' => $this->real_name,
-        'SIGNATURE_FIELD' => $template->tinymce_textarea('signature', $this->signature, 10, 50),
-        'USER_TITLE' => $this->user_title,
-        'USER_LEVEL_MEMBER' => USER_LEVEL_CHPREF,
-        'USER_LEVEL_MOD' => USER_LEVEL_MOD,
-        'USER_LEVEL_ADMIN' => USER_LEVEL_ADMIN,
-        'AES_JAVASCRIPT' => $aes_javascript,
-        'IM_AIM' => $im_aim,
-        'IM_YAHOO' => $im_yahoo,
-        'IM_WLM' => $im_msn,
-        'IM_XMPP' => $im_xmpp,
-        'HOMEPAGE' => $homepage,
-        'LOCATION' => $location,
-        'JOB' => $job,
-        'HOBBIES' => $hobbies,
-        'FORM_ACTION' => $form_action,
-        'REG_IP_ADDR' => $this->reg_ip_addr,
-        'RANK_LIST' => $rank_list,
-        'GRAVATAR_URL' => make_gravatar_url($this->email, 16)
-      ));
-    
-    if ( $this->has_avatar )
-    {
-      $parser->assign_vars(array(
-          'AVATAR_SRC' => make_avatar_url($this->user_id, $this->avi_type),
-          'AVATAR_ALT' => $lang->get('usercp_avatar_image_alt', array('username' => $this->username), $this->email)
-        ));
-    }
-    
-    $parser->assign_bool(array(
-        'password_meter' => ( getConfig('pw_strength_enable') == '1' ),
-        'ul_member' => ( $this->user_level == USER_LEVEL_CHPREF ),
-        'ul_mod' => ( $this->user_level == USER_LEVEL_MOD ),
-        'ul_admin' => ( $this->user_level == USER_LEVEL_ADMIN ),
-        'account_active' => ( $this->account_active === true ),
-        'email_public' => ( $this->email_public === true ),
-        'same_user' => ( $this->user_id == $session->user_id ),
-        'user_has_avatar' => ( $this->has_avatar ),
-        'have_reg_ip' => ( intval(@strlen($this->reg_ip_addr)) > 0 && is_valid_ip($this->reg_ip_addr) )
-      ));
-    
-    $parsed = $parser->run();
-    return $parsed;
-  }
-  
+			$parser = $template->makeParserText($tpl_code);
+		}
+		
+		$this->username = htmlspecialchars($this->username);
+		$this->email = htmlspecialchars($this->email);
+		$this->user_id = intval($this->user_id);
+		$this->real_name = htmlspecialchars($this->real_name);
+		$this->signature = htmlspecialchars($this->signature);
+		$this->user_level = intval($this->user_level);
+		
+		$im_aim   = ( isset($this->im['aim']) )   ? $this->im['aim']   : false;
+		$im_yahoo = ( isset($this->im['yahoo']) ) ? $this->im['yahoo'] : false;
+		$im_msn   = ( isset($this->im['msn']) )   ? $this->im['msn']   : false;
+		$im_xmpp  = ( isset($this->im['xmpp']) )  ? $this->im['xmpp']  : false;
+		
+		$homepage = ( isset($this->contact['homepage']) ) ? $this->contact['homepage'] : false;
+		$location = ( isset($this->contact['location']) ) ? $this->contact['location'] : false;
+		$job = ( isset($this->contact['job']) ) ? $this->contact['job'] : false;
+		$hobbies = ( isset($this->contact['hobbies']) ) ? $this->contact['hobbies'] : false;
+		
+		if ( empty($this->username) )
+		{
+			// @error One or more required parameters not set
+			return 'Admin_UserManager_SmartForm::render: Invalid parameter ($form->username)';
+		}
+		
+		if ( empty($this->user_id) )
+		{
+			// @error One or more required parameters not set
+			return 'Admin_UserManager_SmartForm::render: Invalid parameter ($form->user_id)';
+		}
+		
+		if ( empty($this->email) )
+		{
+			// @error One or more required parameters not set
+			return 'Admin_UserManager_SmartForm::render: Invalid parameter ($form->email)';
+		}
+		
+		$form_action = makeUrlNS('Special', 'Administration', 'module=' . $paths->cpage['module'], true);
+		$aes_javascript = $session->aes_javascript("useredit_$this->uuid", 'new_password');
+		
+		// build rank list
+		$q = $db->sql_query('SELECT rank_id, rank_title FROM ' . table_prefix . 'ranks');
+		if ( !$q )
+			$db->_die();
+		$rank_list = '<option value="NULL"' . ( $this->user_rank === NULL ? ' selected="selected"' : '' ) . '>--</option>' . "\n";
+		while ( $row = $db->fetchrow() )
+		{
+			$rank_list .= '<option value="' . $row['rank_id'] . '"' . ( $row['rank_id'] == $this->user_rank ? ' selected="selected"' : '' ) . '>' . htmlspecialchars($lang->get($row['rank_title'])) . '</option>' . "\n";
+		}
+		
+		$parser->assign_vars(array(
+				'UUID' => $this->uuid,
+				'USERNAME' => $this->username,
+				'EMAIL' => $this->email,
+				'USER_ID' => $this->user_id,
+				'AES_FORM' => $session->generate_aes_form(),
+				'REAL_NAME' => $this->real_name,
+				'SIGNATURE_FIELD' => $template->tinymce_textarea('signature', $this->signature, 10, 50),
+				'USER_TITLE' => $this->user_title,
+				'USER_LEVEL_MEMBER' => USER_LEVEL_CHPREF,
+				'USER_LEVEL_MOD' => USER_LEVEL_MOD,
+				'USER_LEVEL_ADMIN' => USER_LEVEL_ADMIN,
+				'AES_JAVASCRIPT' => $aes_javascript,
+				'IM_AIM' => $im_aim,
+				'IM_YAHOO' => $im_yahoo,
+				'IM_WLM' => $im_msn,
+				'IM_XMPP' => $im_xmpp,
+				'HOMEPAGE' => $homepage,
+				'LOCATION' => $location,
+				'JOB' => $job,
+				'HOBBIES' => $hobbies,
+				'FORM_ACTION' => $form_action,
+				'REG_IP_ADDR' => $this->reg_ip_addr,
+				'RANK_LIST' => $rank_list,
+				'GRAVATAR_URL' => make_gravatar_url($this->email, 16)
+			));
+		
+		if ( $this->has_avatar )
+		{
+			$parser->assign_vars(array(
+					'AVATAR_SRC' => make_avatar_url($this->user_id, $this->avi_type),
+					'AVATAR_ALT' => $lang->get('usercp_avatar_image_alt', array('username' => $this->username), $this->email)
+				));
+		}
+		
+		$parser->assign_bool(array(
+				'password_meter' => ( getConfig('pw_strength_enable') == '1' ),
+				'ul_member' => ( $this->user_level == USER_LEVEL_CHPREF ),
+				'ul_mod' => ( $this->user_level == USER_LEVEL_MOD ),
+				'ul_admin' => ( $this->user_level == USER_LEVEL_ADMIN ),
+				'account_active' => ( $this->account_active === true ),
+				'email_public' => ( $this->email_public === true ),
+				'same_user' => ( $this->user_id == $session->user_id ),
+				'user_has_avatar' => ( $this->has_avatar ),
+				'have_reg_ip' => ( intval(@strlen($this->reg_ip_addr)) > 0 && is_valid_ip($this->reg_ip_addr) )
+			));
+		
+		$parsed = $parser->run();
+		return $parsed;
+	}
+	
 }
 
 function acp_usermanager_lockouts($homewrap = false)
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  
-  // Locked out users
-  
-  if ( !empty($_GET['clear_lockout']) && is_valid_ip($_GET['clear_lockout']) )
-  {
-    $ip = $db->escape($_GET['clear_lockout']);
-    $q = $db->sql_query('DELETE FROM ' . table_prefix . "lockout WHERE ipaddr = '$ip' AND timestamp > ( " . time() . " - (" . getConfig('lockout_duration', 15) . "*60) );");
-    if ( !$q )
-      $db->_die();
-    
-    echo '<div class="info-box">' . $lang->get('acphome_msg_lockout_clear_success', array('ip' => htmlspecialchars($ip))) . '</div>';
-  }
-  
-  $q = $db->sql_query('SELECT COUNT(id) AS fail_count, ipaddr, username, timestamp FROM ' . table_prefix . "lockout\n"
-                    . "  WHERE timestamp > ( " . time() . " - " . intval(getConfig('lockout_duration', 15)) . "*60 ) GROUP BY ipaddr ORDER BY COUNT(id) DESC, timestamp DESC;");
-  if ( !$q )
-    $db->_die();
-  
-  if ( $db->numrows() > 0 )
-  {
-    if ( $homewrap )
-      echo '<div class="acphome-box notice">';
-    echo '<h3>' . $lang->get('acphome_msg_users_locked_out') . '</h3>';
-    echo '<p>' . $lang->get('acphome_msg_users_locked_out_hint') . '</p>';
-    
-    ?>
-    <div class="tblholder" style="margin-bottom: 10px;">
-    <table width="100%" cellspacing="1" cellpadding="4">
-      <tr>
-        <th><?php echo $lang->get('acphome_th_locked_out_ip'); ?></th>
-        <th><?php echo $lang->get('acphome_th_locked_out_username'); ?></th>
-        <th><?php echo $lang->get('acphome_th_locked_out_status'); ?></th>
-        <th><?php echo $lang->get('acphome_th_locked_out_time'); ?></th>
-        <th></th>
-      </tr>
-    <?php
-    
-    while ( $row = $db->fetchrow() )
-    {
-      echo '<tr>';
-      echo '<td class="row1">' . htmlspecialchars($row['ipaddr']) . '</td>';
-      echo '<td class="row2">' . htmlspecialchars($row['username']) . '</td>';
-      // status
-      echo '<td class="row1" style="text-align: center;">' .
-            ( $row['fail_count'] >= getConfig('lockout_threshold', 5)
-                ? '<b>' . $lang->get('acphome_lbl_locked_out_banned') . '</b>'
-                : $lang->get('acphome_lbl_locked_out_warned', array('fail_count' => $row['fail_count']))
-            )
-            . '</td>';
-      // time left
-      if ( $row['fail_count'] >= getConfig('lockout_threshold', 5) )
-      {
-        $expire_time = $row['timestamp'] + ( getConfig('lockout_duration', 15) * 60 );
-        $time_left = round(($expire_time - time()) / 60);
-        $minutes = $time_left == 1 ? $lang->get('etc_unit_minute') : $lang->get('etc_unit_minutes');
-        echo '<td class="row2" style="text-align: center;">' . "$time_left $minutes" . '</td>';
-      }
-      else
-      {
-        echo '<td class="row2" style="text-align: center;">&ndash;</td>';
-      }
-      // action
-      $btn_text = $row['fail_count'] >= getConfig('lockout_threshold', 5) ? $lang->get('acphome_btn_lockout_unblock') : $lang->get('acphome_btn_lockout_clear');
-      echo '<td class="row1" style="text-align: center;"><a href="#" onclick="ajaxPage(\'' . $paths->nslist['Admin'] . 'UserManager\', \'clear_lockout=' . htmlspecialchars($row['ipaddr']) . '\'); return false;">' . $btn_text . '</a></td>';
-      echo '</tr>';
-    }
-    echo '</table>';
-    echo '</div>';
-    if ( $homewrap )
-      echo '</div>';
-  }
-  
-  $db->free_result();
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	
+	// Locked out users
+	
+	if ( !empty($_GET['clear_lockout']) && is_valid_ip($_GET['clear_lockout']) )
+	{
+		$ip = $db->escape($_GET['clear_lockout']);
+		$q = $db->sql_query('DELETE FROM ' . table_prefix . "lockout WHERE ipaddr = '$ip' AND timestamp > ( " . time() . " - (" . getConfig('lockout_duration', 15) . "*60) );");
+		if ( !$q )
+			$db->_die();
+		
+		echo '<div class="info-box">' . $lang->get('acphome_msg_lockout_clear_success', array('ip' => htmlspecialchars($ip))) . '</div>';
+	}
+	
+	$q = $db->sql_query('SELECT COUNT(id) AS fail_count, ipaddr, username, timestamp FROM ' . table_prefix . "lockout\n"
+										. "  WHERE timestamp > ( " . time() . " - " . intval(getConfig('lockout_duration', 15)) . "*60 ) GROUP BY ipaddr ORDER BY COUNT(id) DESC, timestamp DESC;");
+	if ( !$q )
+		$db->_die();
+	
+	if ( $db->numrows() > 0 )
+	{
+		if ( $homewrap )
+			echo '<div class="acphome-box notice">';
+		echo '<h3>' . $lang->get('acphome_msg_users_locked_out') . '</h3>';
+		echo '<p>' . $lang->get('acphome_msg_users_locked_out_hint') . '</p>';
+		
+		?>
+		<div class="tblholder" style="margin-bottom: 10px;">
+		<table width="100%" cellspacing="1" cellpadding="4">
+			<tr>
+				<th><?php echo $lang->get('acphome_th_locked_out_ip'); ?></th>
+				<th><?php echo $lang->get('acphome_th_locked_out_username'); ?></th>
+				<th><?php echo $lang->get('acphome_th_locked_out_status'); ?></th>
+				<th><?php echo $lang->get('acphome_th_locked_out_time'); ?></th>
+				<th></th>
+			</tr>
+		<?php
+		
+		while ( $row = $db->fetchrow() )
+		{
+			echo '<tr>';
+			echo '<td class="row1">' . htmlspecialchars($row['ipaddr']) . '</td>';
+			echo '<td class="row2">' . htmlspecialchars($row['username']) . '</td>';
+			// status
+			echo '<td class="row1" style="text-align: center;">' .
+						( $row['fail_count'] >= getConfig('lockout_threshold', 5)
+								? '<b>' . $lang->get('acphome_lbl_locked_out_banned') . '</b>'
+								: $lang->get('acphome_lbl_locked_out_warned', array('fail_count' => $row['fail_count']))
+						)
+						. '</td>';
+			// time left
+			if ( $row['fail_count'] >= getConfig('lockout_threshold', 5) )
+			{
+				$expire_time = $row['timestamp'] + ( getConfig('lockout_duration', 15) * 60 );
+				$time_left = round(($expire_time - time()) / 60);
+				$minutes = $time_left == 1 ? $lang->get('etc_unit_minute') : $lang->get('etc_unit_minutes');
+				echo '<td class="row2" style="text-align: center;">' . "$time_left $minutes" . '</td>';
+			}
+			else
+			{
+				echo '<td class="row2" style="text-align: center;">&ndash;</td>';
+			}
+			// action
+			$btn_text = $row['fail_count'] >= getConfig('lockout_threshold', 5) ? $lang->get('acphome_btn_lockout_unblock') : $lang->get('acphome_btn_lockout_clear');
+			echo '<td class="row1" style="text-align: center;"><a href="#" onclick="ajaxPage(\'' . $paths->nslist['Admin'] . 'UserManager\', \'clear_lockout=' . htmlspecialchars($row['ipaddr']) . '\'); return false;">' . $btn_text . '</a></td>';
+			echo '</tr>';
+		}
+		echo '</table>';
+		echo '</div>';
+		if ( $homewrap )
+			echo '</div>';
+	}
+	
+	$db->free_result();
 }
--- a/plugins/admin/UserRanks.php	Sun Mar 28 21:49:26 2010 -0400
+++ b/plugins/admin/UserRanks.php	Sun Mar 28 23:10:46 2010 -0400
@@ -13,239 +13,239 @@
 
 function page_Admin_UserRanks()
 {
-  global $db, $session, $paths, $template, $plugins; // Common objects
-  global $lang;
-  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
-  {
-    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
-    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
-    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
-    return;
-  }
-  
-  // This should be a constant somewhere
-  $protected_ranks = array(
-      RANK_ID_MEMBER,
-      RANK_ID_MOD,
-      RANK_ID_ADMIN,
-      RANK_ID_GUEST
-    );
-  
-  if ( $paths->getParam(0) == 'action.json' )
-  {
-    // ajax call, try to decode json request
-    header('Content-type: application/json');
-    
-    if ( !isset($_POST['r']) )
-    {
-      echo enano_json_encode(array(
-          'mode' => 'error',
-          'error' => 'Missing JSON request payload'
-        ));
-      return true;
-    }
-    try
-    {
-      $request = enano_json_decode($_POST['r']);
-    }
-    catch ( Exception $e )
-    {
-      echo enano_json_encode(array(
-          'mode' => 'error',
-          'error' => 'Invalid JSON request payload'
-        ));
-      return true;
-    }
-    
-    if ( !isset($request['mode']) )
-    {
-      echo enano_json_encode(array(
-          'mode' => 'error',
-          'error' => 'JSON request payload does not contain required parameter "mode"'
-        ));
-      return true;
-    }
-    
-    // we've got it
-    switch ( $request['mode'] )
-    {
-      case 'get_rank':
-        // easy enough, get a rank from the DB
-        $rank_id = intval(@$request['rank_id']);
-        if ( empty($rank_id) )
-        {
-          echo enano_json_encode(array(
-              'mode' => 'error',
-              'error' => 'Missing rank ID'
-            ));
-          return true;
-        }
-        // query and fetch
-        $q = $db->sql_query('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = $rank_id;");
-        if ( !$q || $db->numrows() < 1 )
-          $db->die_json();
-        
-        $row = $db->fetchrow();
-        $db->free_result();
-        
-        // why does mysql do this?
-        $row['rank_id'] = intval($row['rank_id']);
-        echo enano_json_encode($row);
-        break;
-      case 'save_rank':
-        // easy enough, get a rank from the DB
-        $rank_id = intval(@$request['rank_id']);
-        // note - an empty rank_style field is permitted
-        if ( empty($rank_id) )
-        {
-          echo enano_json_encode(array(
-              'mode' => 'error',
-              'error' => 'Missing rank ID'
-            ));
-          return true;
-        }
-        
-        if ( empty($request['rank_title']) )
-        {
-          echo enano_json_encode(array(
-              'mode' => 'error',
-              'error' => $lang->get('acpur_err_missing_rank_title')
-            ));
-          return true;
-        }
-        
-        // perform update
-        $rank_title = $db->escape($request['rank_title']);
-        $rank_style = $db->escape(@$request['rank_style']);
-        $q = $db->sql_query('UPDATE ' . table_prefix . "ranks SET rank_title = '$rank_title', rank_style = '$rank_style' WHERE rank_id = $rank_id;");
-        
-        // regenerate the ranks cache
-        generate_cache_userranks();
-        
-        echo enano_json_encode(array(
-            'mode' => 'success'
-          ));
-        break;
-      case 'create_rank':
-        if ( empty($request['rank_title']) )
-        {
-          echo enano_json_encode(array(
-              'mode' => 'error',
-              'error' => $lang->get('acpur_err_missing_rank_title')
-            ));
-          return true;
-        }
-        
-        $rank_title = $db->escape($request['rank_title']);
-        $rank_style = $db->escape(@$request['rank_style']);
-        
-        // perform insert
-        $q = $db->sql_query('INSERT INTO ' . table_prefix . "ranks ( rank_title, rank_style ) VALUES\n"
-                          . "  ( '$rank_title', '$rank_style' );");
-        if ( !$q )
-          $db->die_json();
-        
-        $rank_id = $db->insert_id();
-        if ( !$rank_id )
-        {
-          echo enano_json_encode(array(
-              'mode' => 'error',
-              'error' => 'Refetch of rank ID failed'
-            ));
-          return true;
-        }
-        
-        // regenerate the ranks cache
-        generate_cache_userranks();
-        
-        echo enano_json_encode(array(
-            'mode' => 'success',
-            'rank_id' => $rank_id
-          ));
-        break;
-      case 'delete_rank':
-        // nuke a rank
-        $rank_id = intval(@$request['rank_id']);
-        if ( empty($rank_id) )
-        {
-          echo enano_json_encode(array(
-              'mode' => 'error',
-              'error' => 'Missing rank ID'
-            ));
-          return true;
-        }
-        
-        // is this rank protected (e.g. a system rank)?
-        if ( in_array($rank_id, $protected_ranks) )
-        {
-          echo enano_json_encode(array(
-              'mode' => 'error',
-              'error' => $lang->get('acpur_err_cant_delete_system_rank')
-            ));
-          return true;
-        }
-        
-        // unset any user and groups that might be using it
-        $q = $db->sql_query('UPDATE ' . table_prefix . "users SET user_rank = NULL WHERE user_rank = $rank_id;");
-        if ( !$q )
-          $db->die_json();
-        $q = $db->sql_query('UPDATE ' . table_prefix . "groups SET group_rank = NULL WHERE group_rank = $rank_id;");
-        if ( !$q )
-          $db->die_json();
-        
-        // now remove the rank itself
-        $q = $db->sql_query('DELETE FROM ' . table_prefix . "ranks WHERE rank_id = $rank_id;");
-        if ( !$q )
-          $db->_die();
-        
-        // regenerate the ranks cache
-        generate_cache_userranks();
-        
-        echo enano_json_encode(array(
-            'mode' => 'success'
-          ));
-        break;
-      default:
-        echo enano_json_encode(array(
-          'mode' => 'error',
-          'error' => 'Unknown requested operation'
-        ));
-      return true;
-    }
-    return true;
-  }
-  
-  // draw initial interface
-  // yes, four paragraphs of introduction. Suck it up.
-  echo '<h3>' . $lang->get('acpur_heading_main') . '</h3>';
-  echo '<p>' . $lang->get('acpur_intro_para1') . '</p>';
-  echo '<p>' . $lang->get('acpur_intro_para2') . '</p>';
-  echo '<p>' . $lang->get('acpur_intro_para3') . '</p>';
-  echo '<p>' . $lang->get('acpur_intro_para4') . '</p>';
-  
-  // fetch ranks
-  $q = $db->sql_query('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks ORDER BY rank_title ASC;");
-  if ( !$q )
-    $db->_die();
-  
-  echo '<div class="rankadmin-left" id="admin_ranks_container_left">';
-  while ( $row = $db->fetchrow() )
-  {
-    // format rank according to what its users look like
-    // rank titles can be stored as language strings, so have the language manager fetch this
-    // normally it refetches (which takes time) if a string isn't found, but it won't try to fetch
-    // a string that isn't in the category_stringid format
-    $rank_title = $lang->get($row['rank_title']);
-    // FIXME: make sure htmlspecialchars() is escaping quotes and backslashes
-    echo '<a href="#rank_edit:' . $row['rank_id'] . '" onclick="ajaxInitRankEdit(' . $row['rank_id'] . '); return false;" class="rankadmin-editlink" style="' . htmlspecialchars($row['rank_style']) . '" id="rankadmin_editlink_' . $row['rank_id'] . '">' . htmlspecialchars($rank_title) . '</a> ';
-  }
-  echo '<a href="#rank_create" onclick="ajaxInitRankCreate(); return false;" class="rankadmin-editlink rankadmin-createlink" id="rankadmin_createlink">' . $lang->get('acpur_btn_create_init') . '</a> ';
-  echo '</div>';
-  
-  echo '<div class="rankadmin-right" id="admin_ranks_container_right">';
-  echo $lang->get('acpur_msg_select_rank');
-  echo '</div>';
-  echo '<span class="menuclear"></span>';
+	global $db, $session, $paths, $template, $plugins; // Common objects
+	global $lang;
+	if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+	{
+		$login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+		echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+		echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+		return;
+	}
+	
+	// This should be a constant somewhere
+	$protected_ranks = array(
+			RANK_ID_MEMBER,
+			RANK_ID_MOD,
+			RANK_ID_ADMIN,
+			RANK_ID_GUEST
+		);
+	
+	if ( $paths->getParam(0) == 'action.json' )
+	{
+		// ajax call, try to decode json request
+		header('Content-type: application/json');
+		
+		if ( !isset($_POST['r']) )
+		{
+			echo enano_json_encode(array(
+					'mode' => 'error',
+					'error' => 'Missing JSON request payload'
+				));
+			return true;
+		}
+		try
+		{
+			$request = enano_json_decode($_POST['r']);
+		}
+		catch ( Exception $e )
+		{
+			echo enano_json_encode(array(
+					'mode' => 'error',
+					'error' => 'Invalid JSON request payload'
+				));
+			return true;
+		}
+		
+		if ( !isset($request['mode']) )
+		{
+			echo enano_json_encode(array(
+					'mode' => 'error',
+					'error' => 'JSON request payload does not contain required parameter "mode"'
+				));
+			return true;
+		}
+		
+		// we've got it
+		switch ( $request['mode'] )
+		{
+			case 'get_rank':
+				// easy enough, get a rank from the DB
+				$rank_id = intval(@$request['rank_id']);
+				if ( empty($rank_id) )
+				{
+					echo enano_json_encode(array(
+							'mode' => 'error',
+							'error' => 'Missing rank ID'
+						));
+					return true;
+				}
+				// query and fetch
+				$q = $db->sql_query('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks WHERE rank_id = $rank_id;");
+				if ( !$q || $db->numrows() < 1 )
+					$db->die_json();
+				
+				$row = $db->fetchrow();
+				$db->free_result();
+				
+				// why does mysql do this?
+				$row['rank_id'] = intval($row['rank_id']);
+				echo enano_json_encode($row);
+				break;
+			case 'save_rank':
+				// easy enough, get a rank from the DB
+				$rank_id = intval(@$request['rank_id']);
+				// note - an empty rank_style field is permitted
+				if ( empty($rank_id) )
+				{
+					echo enano_json_encode(array(
+							'mode' => 'error',
+							'error' => 'Missing rank ID'
+						));
+					return true;
+				}
+				
+				if ( empty($request['rank_title']) )
+				{
+					echo enano_json_encode(array(
+							'mode' => 'error',
+							'error' => $lang->get('acpur_err_missing_rank_title')
+						));
+					return true;
+				}
+				
+				// perform update
+				$rank_title = $db->escape($request['rank_title']);
+				$rank_style = $db->escape(@$request['rank_style']);
+				$q = $db->sql_query('UPDATE ' . table_prefix . "ranks SET rank_title = '$rank_title', rank_style = '$rank_style' WHERE rank_id = $rank_id;");
+				
+				// regenerate the ranks cache
+				generate_cache_userranks();
+				
+				echo enano_json_encode(array(
+						'mode' => 'success'
+					));
+				break;
+			case 'create_rank':
+				if ( empty($request['rank_title']) )
+				{
+					echo enano_json_encode(array(
+							'mode' => 'error',
+							'error' => $lang->get('acpur_err_missing_rank_title')
+						));
+					return true;
+				}
+				
+				$rank_title = $db->escape($request['rank_title']);
+				$rank_style = $db->escape(@$request['rank_style']);
+				
+				// perform insert
+				$q = $db->sql_query('INSERT INTO ' . table_prefix . "ranks ( rank_title, rank_style ) VALUES\n"
+													. "  ( '$rank_title', '$rank_style' );");
+				if ( !$q )
+					$db->die_json();
+				
+				$rank_id = $db->insert_id();
+				if ( !$rank_id )
+				{
+					echo enano_json_encode(array(
+							'mode' => 'error',
+							'error' => 'Refetch of rank ID failed'
+						));
+					return true;
+				}
+				
+				// regenerate the ranks cache
+				generate_cache_userranks();
+				
+				echo enano_json_encode(array(
+						'mode' => 'success',
+						'rank_id' => $rank_id
+					));
+				break;
+			case 'delete_rank':
+				// nuke a rank
+				$rank_id = intval(@$request['rank_id']);
+				if ( empty($rank_id) )
+				{
+					echo enano_json_encode(array(
+							'mode' => 'error',
+							'error' => 'Missing rank ID'
+						));
+					return true;
+				}
+				
+				// is this rank protected (e.g. a system rank)?
+				if ( in_array($rank_id, $protected_ranks) )
+				{
+					echo enano_json_encode(array(
+							'mode' => 'error',
+							'error' => $lang->get('acpur_err_cant_delete_system_rank')
+						));
+					return true;
+				}
+				
+				// unset any user and groups that might be using it
+				$q = $db->sql_query('UPDATE ' . table_prefix . "users SET user_rank = NULL WHERE user_rank = $rank_id;");
+				if ( !$q )
+					$db->die_json();
+				$q = $db->sql_query('UPDATE ' . table_prefix . "groups SET group_rank = NULL WHERE group_rank = $rank_id;");
+				if ( !$q )
+					$db->die_json();
+				
+				// now remove the rank itself
+				$q = $db->sql_query('DELETE FROM ' . table_prefix . "ranks WHERE rank_id = $rank_id;");
+				if ( !$q )
+					$db->_die();
+				
+				// regenerate the ranks cache
+				generate_cache_userranks();
+				
+				echo enano_json_encode(array(
+						'mode' => 'success'
+					));
+				break;
+			default:
+				echo enano_json_encode(array(
+					'mode' => 'error',
+					'error' => 'Unknown requested operation'
+				));
+			return true;
+		}
+		return true;
+	}
+	
+	// draw initial interface
+	// yes, four paragraphs of introduction. Suck it up.
+	echo '<h3>' . $lang->get('acpur_heading_main') . '</h3>';
+	echo '<p>' . $lang->get('acpur_intro_para1') . '</p>';
+	echo '<p>' . $lang->get('acpur_intro_para2') . '</p>';
+	echo '<p>' . $lang->get('acpur_intro_para3') . '</p>';
+	echo '<p>' . $lang->get('acpur_intro_para4') . '</p>';
+	
+	// fetch ranks
+	$q = $db->sql_query('SELECT rank_id, rank_title, rank_style FROM ' . table_prefix . "ranks ORDER BY rank_title ASC;");
+	if ( !$q )
+		$db->_die();
+	
+	echo '<div class="rankadmin-left" id="admin_ranks_container_left">';
+	while ( $row = $db->fetchrow() )
+	{
+		// format rank according to what its users look like
+		// rank titles can be stored as language strings, so have the language manager fetch this
+		// normally it refetches (which takes time) if a string isn't found, but it won't try to fetch
+		// a string that isn't in the category_stringid format
+		$rank_title = $lang->get($row['rank_title']);
+		// FIXME: make sure htmlspecialchars() is escaping quotes and backslashes
+		echo '<a href="#rank_edit:' . $row['rank_id'] . '" onclick="ajaxInitRankEdit(' . $row['rank_id'] . '); return false;" class="rankadmin-editlink" style="' . htmlspecialchars($row['rank_style']) . '" id="rankadmin_editlink_' . $row['rank_id'] . '">' . htmlspecialchars($rank_title) . '</a> ';
+	}
+	echo '<a href="#rank_create" onclick="ajaxInitRankCreate(); return false;" class="rankadmin-editlink rankadmin-createlink" id="rankadmin_createlink">' . $lang->get('acpur_btn_create_init') . '</a> ';
+	echo '</div>';
+	
+	echo '<div class="rankadmin-right" id="admin_ranks_container_right">';
+	echo $lang->get('acpur_msg_select_rank');
+	echo '</div>';
+	echo '<span class="menuclear"></span>';
 }
 
 ?>
--- a/themes/admin/acledit.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/admin/acledit.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,32 +1,32 @@
 <!-- VAR acl_field_begin -->
 <div class="tblholder">
-  <table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
-    <tr>
-      <th></th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
-    </tr>
+	<table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
+		<tr>
+			<th></th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
+		</tr>
 <!-- ENDVAR acl_field_begin -->
 <!-- VAR acl_field_item -->
-    <tr>
-      <td class="{ROW_CLASS}">{FIELD_DESC}</td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
-    </tr>
+		<tr>
+			<td class="{ROW_CLASS}">{FIELD_DESC}</td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
+		</tr>
 <!-- ENDVAR acl_field_item -->
 <!-- VAR acl_field_end -->
-    <tr>
-      <td colspan="6" class="row3">
-        {lang:acl_lbl_help}
-      </td>
-    </tr>
-  </table>
+		<tr>
+			<td colspan="6" class="row3">
+				{lang:acl_lbl_help}
+			</td>
+		</tr>
+	</table>
 </div>
 <!-- ENDVAR acl_field_end -->
 
--- a/themes/admin/comment.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/admin/comment.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,58 +1,58 @@
 <div class="tblholder">
-  <table border="0" width="100%" cellspacing="1" cellpadding="4">
-    <tr>
-      <th colspan="2" style="text-align: left;">{DATETIME}</th>
-    </tr>
-    <tr>
-      <td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-        <table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
-          <tr>
-            <td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              <b>{NAME}</b><br />
-              <small>{USER_LEVEL}</small>
-              <!-- BEGIN user_has_avatar -->
-              <div class="avatar">
-                <a href="{USERPAGE_LINK}">
-                  <img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
-                </a>
-              </div>
-              <!-- END user_has_avatar -->
-            </td>
-          </tr>
-          <tr>
-            <td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              {SEND_PM_LINK} {ADD_BUDDY_LINK}
-            </td>
-          </tr>
-        </table>
-      </td>
-      <td class="row2">
-        <b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
-      </td>
-    </tr>
-    <tr>
-      <td class="row3">
-        <div id="comment_{ID}">{DATA}</div>
-        <!-- BEGIN signature -->
-          <hr style="margin-left: 1em; width: 200px;" />
-          {SIGNATURE}
-        <!-- END signature -->
-      </td>
-    </tr>
-    <!-- BEGIN can_edit -->
-    <tr>
-      <td class="row2">
-        [ {EDIT_LINK} | {DELETE_LINK} ]
-      </td>
-    </tr>
-    <!-- END can_edit -->
-    <!-- BEGIN auth_mod -->
-    <tr>
-      <td class="row1">
-        <b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
-      </td>
-    </tr>
-    <!-- END auth_mod -->
-  </table>
+	<table border="0" width="100%" cellspacing="1" cellpadding="4">
+		<tr>
+			<th colspan="2" style="text-align: left;">{DATETIME}</th>
+		</tr>
+		<tr>
+			<td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+				<table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
+					<tr>
+						<td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							<b>{NAME}</b><br />
+							<small>{USER_LEVEL}</small>
+							<!-- BEGIN user_has_avatar -->
+							<div class="avatar">
+								<a href="{USERPAGE_LINK}">
+									<img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
+								</a>
+							</div>
+							<!-- END user_has_avatar -->
+						</td>
+					</tr>
+					<tr>
+						<td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							{SEND_PM_LINK} {ADD_BUDDY_LINK}
+						</td>
+					</tr>
+				</table>
+			</td>
+			<td class="row2">
+				<b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
+			</td>
+		</tr>
+		<tr>
+			<td class="row3">
+				<div id="comment_{ID}">{DATA}</div>
+				<!-- BEGIN signature -->
+					<hr style="margin-left: 1em; width: 200px;" />
+					{SIGNATURE}
+				<!-- END signature -->
+			</td>
+		</tr>
+		<!-- BEGIN can_edit -->
+		<tr>
+			<td class="row2">
+				[ {EDIT_LINK} | {DELETE_LINK} ]
+			</td>
+		</tr>
+		<!-- END can_edit -->
+		<!-- BEGIN auth_mod -->
+		<tr>
+			<td class="row1">
+				<b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
+			</td>
+		</tr>
+		<!-- END auth_mod -->
+	</table>
 </div>
 <br />
--- a/themes/admin/css/default.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/admin/css/default.css	Sun Mar 28 23:10:46 2010 -0400
@@ -36,42 +36,42 @@
 /* Sidebar */
 td#td-sidebar                 { padding-right: 12px; height: 100%; }
 table#sidebar-show {
-  display: none;
-  width: 100%;
+	display: none;
+	width: 100%;
 }
 div#sidebar-hide {
-  width: 10px;
-  height: 100%;
-  background-color: #6587B8;
-  cursor: pointer;
-  position: absolute;
-  top: 0px;
-  background-repeat: no-repeat;
-  background-position: center center;
+	width: 10px;
+	height: 100%;
+	background-color: #6587B8;
+	cursor: pointer;
+	position: absolute;
+	top: 0px;
+	background-repeat: no-repeat;
+	background-position: center center;
 }
 div#sidebar-hide:hover {
-  background-color: #95B7E8;
+	background-color: #95B7E8;
 }
 div.expanded {
-  background-image: url(../images/collapse.gif);
-  right: 232px;
+	background-image: url(../images/collapse.gif);
+	right: 232px;
 }
 div.collapsed {
-  background-image: url(../images/expand.gif);
-  right: 0px;
+	background-image: url(../images/expand.gif);
+	right: 0px;
 }
 
 /* Content area */
 table.wrapper td.main h2.pagename {
-  border-bottom: 1px solid #456798;
-  margin-bottom: 0;
+	border-bottom: 1px solid #456798;
+	margin-bottom: 0;
 }
 
 table.wrapper td.main a {
-  color: #294F75;
+	color: #294F75;
 }
 table.wrapper td.main a:hover {
-  color: #597FA5;
+	color: #597FA5;
 }
 
 /*
@@ -79,194 +79,194 @@
  */
 
 div.menu, div.menu_nojs {
-  background-color: #B0D0F0;
-  font-size: 7pt;
-  border-width: 0;
+	background-color: #B0D0F0;
+	font-size: 7pt;
+	border-width: 0;
 }
 div.menu a, div.menu_nojs a, div.menu div.label, div.menu_nojs div.label {
-  padding: 2.5pt 5px;
-  margin-right: 3px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  color: #406080;
+	padding: 2.5pt 5px;
+	margin-right: 3px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	color: #406080;
 }
 div.menu div.label, div.menu_nojs div.label {
-  color: #001020;
-  cursor: default;
+	color: #001020;
+	cursor: default;
 }
 div.menu span.sep, div.menu_nojs span.sep {
-  display: block;
-  float: left;
-  width: 5px;
+	display: block;
+	float: left;
+	width: 5px;
 }
 div.menu div.multopts, div.menu_nojs div.multopts {
-  line-height: 17pt;
+	line-height: 17pt;
 }
 div.menu div.multopts a, div.menu_nojs div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts div.label {
-  float: none;
-  display: inline;
+	float: none;
+	display: inline;
 }
 div.menu a.liteselected, div.menu_nojs a.liteselected, div.menu a.liteselected:hover, div.menu_nojs a.liteselected:hover, div.menu a:hover, div.menu_nojs a:hover {
-  color: #406080;
-  background-color: #D0F0FF;
+	color: #406080;
+	background-color: #D0F0FF;
 }
 div.menu input[type ^="text"], div.menu_nojs input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="password"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 4px 5px;
-  max-width: 70px;
-  background-color: #D0F0FF;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 4px 5px;
+	max-width: 70px;
+	background-color: #D0F0FF;
 }
 div.menu input[type ^="text"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="password"]:hover {
-  background-color: #E0F0FF;
+	background-color: #E0F0FF;
 }
 div.menu input[type ^="text"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="password"]:focus {
-  background-color: #F0F0FF;
+	background-color: #F0F0FF;
 }
 div.menu input[type ^="button"], div.menu_nojs input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="submit"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 3px 5px;
-  max-width: 70px;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 3px 5px;
+	max-width: 70px;
 }
 div.menu a.current, div.menu_nojs a.current, div.menu a.current:hover, div.menu_nojs a.current:hover, div.menu a.selected, div.menu_nojs a.selected, div.menu a.selected:hover, div.menu_nojs a.selected:hover {
-  color: #000040;
-  background-color: #FFFFFF;
+	color: #000040;
+	background-color: #FFFFFF;
 }
 div.menu ul, div.menu_nojs ul {
-  display: none;
-  position: absolute;
-  padding: 0;
-  margin: 0;
-  background-color: #B0D0F0;
-  border-width: 0;
-  min-width: 120px;
+	display: none;
+	position: absolute;
+	padding: 0;
+	margin: 0;
+	background-color: #B0D0F0;
+	border-width: 0;
+	min-width: 120px;
 }
 div.menu ul li, div.menu_nojs ul li {
-  list-style: none;
+	list-style: none;
 }
 div.menu ul a, div.menu_nojs ul a {
-  float: none;
-  margin: 0;
+	float: none;
+	margin: 0;
 }
 span.menuclear {
-  font-size: 1px;
-  height: 0px;
-  width: 0px;
-  clear: left;
-  line-height: 0px;
-  display: block;
+	font-size: 1px;
+	height: 0px;
+	width: 0px;
+	clear: left;
+	line-height: 0px;
+	display: block;
 }
 
 /* Buttons - this is CSS3 */
 input[type ^="button"], input[type ^="submit"], button {
-  border-width: 1px;
-  border-color: #666;
-  border-style: solid;
-  background-color: #DDD;
-  color: #101010;
-  cursor: pointer;
-  font-size: 8pt;
-  font-family: arial, helvetica, sans-serif;
-  padding: 5px 3px;
-  background-image: url(../images/buttonbg.gif);
-  background-repeat: repeat-x;
+	border-width: 1px;
+	border-color: #666;
+	border-style: solid;
+	background-color: #DDD;
+	color: #101010;
+	cursor: pointer;
+	font-size: 8pt;
+	font-family: arial, helvetica, sans-serif;
+	padding: 5px 3px;
+	background-image: url(../images/buttonbg.gif);
+	background-repeat: repeat-x;
 }
 
 input[type ^="button"]:hover, input[type ^="submit"]:hover, button:hover {
-  border-color: #999;
+	border-color: #999;
 }
 
 input[type ^="button"]:active, input[type ^="submit"]:active, button:active {
-  padding: 6px 2px 4px 4px;
-  border-color: #333;
+	padding: 6px 2px 4px 4px;
+	border-color: #333;
 }
 
 input[type ^="text"], input[type ^="password"] {
-  background-color: #F8FBFF;
-  color: #202020;
-  border: 1px solid #254778;
-  font-size: 8pt;
-  font-family: arial, helvetica, sans-serif;
-  padding: 3px;
+	background-color: #F8FBFF;
+	color: #202020;
+	border: 1px solid #254778;
+	font-size: 8pt;
+	font-family: arial, helvetica, sans-serif;
+	padding: 3px;
 }
 
 input:disabled, input[disabled ^="disabled"], button:disabled, button[disabled ^="disabled"] {
-  border-color: #666666 !important;
-  background-image: none !important;
-  background-color: #DDD !important;
-  color: #888;
+	border-color: #666666 !important;
+	background-image: none !important;
+	background-color: #DDD !important;
+	color: #888;
 }
 
 /* ACP home elements */
 
 div.acphome-box {
-  border-radius: 4px;
-  -moz-border-radius: 4px;
-  padding: 14px 11px;
-  margin: 4px;
+	border-radius: 4px;
+	-moz-border-radius: 4px;
+	padding: 14px 11px;
+	margin: 4px;
 }
 
 div.acphome-box.halfwidth {
-  padding: 1.5%;
-  margin: 4px 0.5%;
-  float: left;
-  width: 46%;
+	padding: 1.5%;
+	margin: 4px 0.5%;
+	float: left;
+	width: 46%;
 }
 
 div.acphome-box h3 {
-  text-decoration: underline;
-  margin: 0 0 10px 0;
+	text-decoration: underline;
+	margin: 0 0 10px 0;
 }
 
 div.acphome-box p {
-  margin: 0 0 10px 0;
+	margin: 0 0 10px 0;
 }
 
 div.acphome-box div.tblholder table {
-  color: black;
+	color: black;
 }
 
 div.acphome-box.warning {
-  background-color: #900000;
-  color: #fff;
-  text-align: center;
-  font-weight: bold;
+	background-color: #900000;
+	color: #fff;
+	text-align: center;
+	font-weight: bold;
 }
 
 div.acphome-box.info {
-  background-color: #e2ecfe;
-  color: #202020;
+	background-color: #e2ecfe;
+	color: #202020;
 }
 
 div.acphome-box.notice {
-  background-color: #009000;
-  color: #fff;
+	background-color: #009000;
+	color: #fff;
 }
 
 div.acphome-box.warning a:link, div.acphome-box.notice a:link {
-  color: #eee;
+	color: #eee;
 }
 
 div.acphome-box.warning a:hover, div.acphome-box.notice a:hover {
-  color: #fff !important;
+	color: #fff !important;
 }
 
 div.acphome-box div.tblholder table a {
-  color: #294F75 !important;
+	color: #294F75 !important;
 }
 
 div.acphome-box div.tblholder table a:hover {
-  color: #597FA5 !important;
+	color: #597FA5 !important;
 }
 
 th.systemversion a {
-  font-weight: normal;
-  color: #fff !important;
+	font-weight: normal;
+	color: #fff !important;
 }
 
 th.systemversion a:hover {
-  font-weight: normal;
-  color: #ff0 !important;
+	font-weight: normal;
+	color: #ff0 !important;
 }
--- a/themes/admin/elements.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/admin/elements.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -17,24 +17,24 @@
 <!-- VAR sidebar_top -->
 <!-- ENDVAR sidebar_top -->
 <!-- VAR sidebar_section -->
-            <h4>
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-              {TITLE}
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-            </h4>
-            <ul>
-              {CONTENT}
-            </ul>
+						<h4>
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+							{TITLE}
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+						</h4>
+						<ul>
+							{CONTENT}
+						</ul>
 <!-- ENDVAR sidebar_section -->
 <!-- VAR sidebar_section_raw -->
-            <h4>
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-              {TITLE}
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-            </h4>
-            <div>
-              {CONTENT}
-            </div>
+						<h4>
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+							{TITLE}
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+						</h4>
+						<div>
+							{CONTENT}
+						</div>
 <!-- ENDVAR sidebar_section_raw -->
 <!-- VAR sidebar_bottom -->
 <!-- ENDVAR sidebar_bottom -->
--- a/themes/admin/footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/admin/footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,46 +1,46 @@
-          </div>
-          <div class="footer">
-            {COPYRIGHT}
-            <!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
-            <br />[[EnanoPoweredLink]] &bull; &copy; 2006-2009 Dan Fuhry
-          </div>
-        </td>
-        <td class="right"></td>
-      </tr>
-      <tr>
-        <td class="bottom-left"></td><td class="bottom"></td><td class="bottom-right"></td>
-      </tr>
-    </table>
-    
-    </td>
-    <td id="td-sidebar" valign="top">
-    
-      <table border="0" cellspacing="0" cellpadding="0" class="wrapper" id="sidebar-show">
-        <tr>
-          <td class="top-left"></td><td class="top">&nbsp;</td><td class="top-right"></td>
-        </tr>
-        <tr>
-          <td class="left"></td>
-          <td class="main">
-            <div id="sidebar">
-              {SIDEBAR_LEFT}
-              {SIDEBAR_RIGHT}
-            </div>
-          </td>
-          <td class="right"></td>
-        </tr>
-        <tr>
-          <td class="bottom-left"></td><td class="bottom"></td><td class="bottom-right"></td>
-        </tr>
-      </table>
-      
-      <div id="sidebar-hide" onclick="admin_expand();" class="collapsed" title="Click to expand the sidebar"></div>
-    
-    </td>
-    </tr>
-    </table>
-    
-    <script type="text/javascript" src="{CDNPATH}/themes/admin/js/menu.js"></script>
-    {JS_FOOTER}
-  </body>
+					</div>
+					<div class="footer">
+						{COPYRIGHT}
+						<!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
+						<br />[[EnanoPoweredLink]] &bull; &copy; 2006-2009 Dan Fuhry
+					</div>
+				</td>
+				<td class="right"></td>
+			</tr>
+			<tr>
+				<td class="bottom-left"></td><td class="bottom"></td><td class="bottom-right"></td>
+			</tr>
+		</table>
+		
+		</td>
+		<td id="td-sidebar" valign="top">
+		
+			<table border="0" cellspacing="0" cellpadding="0" class="wrapper" id="sidebar-show">
+				<tr>
+					<td class="top-left"></td><td class="top">&nbsp;</td><td class="top-right"></td>
+				</tr>
+				<tr>
+					<td class="left"></td>
+					<td class="main">
+						<div id="sidebar">
+							{SIDEBAR_LEFT}
+							{SIDEBAR_RIGHT}
+						</div>
+					</td>
+					<td class="right"></td>
+				</tr>
+				<tr>
+					<td class="bottom-left"></td><td class="bottom"></td><td class="bottom-right"></td>
+				</tr>
+			</table>
+			
+			<div id="sidebar-hide" onclick="admin_expand();" class="collapsed" title="Click to expand the sidebar"></div>
+		
+		</td>
+		</tr>
+		</table>
+		
+		<script type="text/javascript" src="{CDNPATH}/themes/admin/js/menu.js"></script>
+		{JS_FOOTER}
+	</body>
 </html>
--- a/themes/admin/header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/admin/header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,44 +1,44 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css?{ENANO_VERSION}" />
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css?{ENANO_VERSION}" />
-    <!--[if IE]>
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-ie/iefixes.css" />
-    <![endif]-->
-    {JS_DYNAMIC_VARS}
-    {JS_HEADER}
-    {ADDITIONAL_HEADERS}
-    </head>
-  <body>
-    <div id="header">
-      <div class="sitename">{SITE_NAME}</div>
-      <!-- div class="menulink"><a href="#" onclick="adminOpenMenu('sidebar', this); return false;">expand menu</a></div -->
-      [&nbsp;<a href="{SCRIPTPATH}/{ADMIN_SID_QUES}">{lang:etc_btn_main_page} &#0187;</a>&nbsp;]
-    </div>
-    <div class="menu_nojs" id="pagebar_main">
-      <div class="label">{lang:onpage_lbl_pagetools}</div>
-      {TOOLBAR}
-      <ul>
-        {TOOLBAR_EXTRAS}
-      </ul>
-      <span class="menuclear">&nbsp;</span>
-    </div>
-    <table border="0" cellspacing="0" cellpadding="0" id="sidebarholder">
-    <tr>
-    <td valign="top">
-    
-    <table border="0" cellspacing="0" cellpadding="0" class="wrapper">
-      <tr>
-        <td class="top-left"></td><td class="top">&nbsp;</td><td class="top-right"></td>
-      </tr>
-      <tr>
-        <td class="left"></td>
-        <td class="main">
-          <div style="float: right;">
-            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
-          </div>
-          <h2 class="pagename">{PAGE_NAME}</h2>
-          <div id="ajaxEditContainer">
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css?{ENANO_VERSION}" />
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css?{ENANO_VERSION}" />
+		<!--[if IE]>
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-ie/iefixes.css" />
+		<![endif]-->
+		{JS_DYNAMIC_VARS}
+		{JS_HEADER}
+		{ADDITIONAL_HEADERS}
+		</head>
+	<body>
+		<div id="header">
+			<div class="sitename">{SITE_NAME}</div>
+			<!-- div class="menulink"><a href="#" onclick="adminOpenMenu('sidebar', this); return false;">expand menu</a></div -->
+			[&nbsp;<a href="{SCRIPTPATH}/{ADMIN_SID_QUES}">{lang:etc_btn_main_page} &#0187;</a>&nbsp;]
+		</div>
+		<div class="menu_nojs" id="pagebar_main">
+			<div class="label">{lang:onpage_lbl_pagetools}</div>
+			{TOOLBAR}
+			<ul>
+				{TOOLBAR_EXTRAS}
+			</ul>
+			<span class="menuclear">&nbsp;</span>
+		</div>
+		<table border="0" cellspacing="0" cellpadding="0" id="sidebarholder">
+		<tr>
+		<td valign="top">
+		
+		<table border="0" cellspacing="0" cellpadding="0" class="wrapper">
+			<tr>
+				<td class="top-left"></td><td class="top">&nbsp;</td><td class="top-right"></td>
+			</tr>
+			<tr>
+				<td class="left"></td>
+				<td class="main">
+					<div style="float: right;">
+						<img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
+					</div>
+					<h2 class="pagename">{PAGE_NAME}</h2>
+					<div id="ajaxEditContainer">
--- a/themes/admin/simple-footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/admin/simple-footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,53 +1,53 @@
-          </div>
-          <div class="footer">
-            {COPYRIGHT}<br />Powered by <a href="{URL_ABOUT_ENANO}">Enano</a> &bull; Copyright &copy; 2007 Dan Fuhry
-          </div>
-        </td>
-        <td class="right"></td>
-      </tr>
-      <tr>
-        <td class="bottom-left"></td><td class="bottom"></td><td class="bottom-right"></td>
-      </tr>
-    </table>
-    
-    </td>
-    <td id="td-sidebar" valign="top">
-    
-      <table border="0" cellspacing="0" cellpadding="0" class="wrapper" id="sidebar-show">
-        <tr>
-          <td class="top-left"></td><td class="top">&nbsp;</td><td class="top-right"></td>
-        </tr>
-        <tr>
-          <td class="left"></td>
-          <td class="main">
-            <div id="sidebar">
-              {SIDEBAR_LEFT}
-              {SIDEBAR_RIGHT}
-            </div>
-          </td>
-          <td class="right"></td>
-        </tr>
-        <tr>
-          <td class="bottom-left"></td><td class="bottom"></td><td class="bottom-right"></td>
-        </tr>
-      </table>
-      
-      <div id="sidebar-hide" onclick="admin_expand();" class="collapsed" title="Click to expand the sidebar"></div>
-    
-    </td>
-    </tr>
-    </table>
-    
-    <div style="display: none;">
-    <h2>Your browser does not support CSS.</h2>
-     <p>If you can see this text, it means that your browser does not support Cascading Style Sheets (CSS). CSS is a fundemental aspect of XHTML, and as a result it is becoming very widely adopted by websites, including this one. You should consider switching to a more modern web browser, such as Mozilla Firefox or Opera 9.</p>
-     <p>Because of this, there are a few minor issues that you may experience while browsing this site, not the least of which is some visual elements below that would normally be hidden in most browsers. Please excuse these minor inconveniences.</p>
-    </div>
-    <div id="root3" class="jswindow" style="display: none;">
-      <div id="tb3" class="titlebar">Wiki formatting help</div>
-      <div class="content" id="cn3">
-        Loading...
-      </div>
-    </div>
-  </body>
+					</div>
+					<div class="footer">
+						{COPYRIGHT}<br />Powered by <a href="{URL_ABOUT_ENANO}">Enano</a> &bull; Copyright &copy; 2007 Dan Fuhry
+					</div>
+				</td>
+				<td class="right"></td>
+			</tr>
+			<tr>
+				<td class="bottom-left"></td><td class="bottom"></td><td class="bottom-right"></td>
+			</tr>
+		</table>
+		
+		</td>
+		<td id="td-sidebar" valign="top">
+		
+			<table border="0" cellspacing="0" cellpadding="0" class="wrapper" id="sidebar-show">
+				<tr>
+					<td class="top-left"></td><td class="top">&nbsp;</td><td class="top-right"></td>
+				</tr>
+				<tr>
+					<td class="left"></td>
+					<td class="main">
+						<div id="sidebar">
+							{SIDEBAR_LEFT}
+							{SIDEBAR_RIGHT}
+						</div>
+					</td>
+					<td class="right"></td>
+				</tr>
+				<tr>
+					<td class="bottom-left"></td><td class="bottom"></td><td class="bottom-right"></td>
+				</tr>
+			</table>
+			
+			<div id="sidebar-hide" onclick="admin_expand();" class="collapsed" title="Click to expand the sidebar"></div>
+		
+		</td>
+		</tr>
+		</table>
+		
+		<div style="display: none;">
+		<h2>Your browser does not support CSS.</h2>
+ 		<p>If you can see this text, it means that your browser does not support Cascading Style Sheets (CSS). CSS is a fundemental aspect of XHTML, and as a result it is becoming very widely adopted by websites, including this one. You should consider switching to a more modern web browser, such as Mozilla Firefox or Opera 9.</p>
+ 		<p>Because of this, there are a few minor issues that you may experience while browsing this site, not the least of which is some visual elements below that would normally be hidden in most browsers. Please excuse these minor inconveniences.</p>
+		</div>
+		<div id="root3" class="jswindow" style="display: none;">
+			<div id="tb3" class="titlebar">Wiki formatting help</div>
+			<div class="content" id="cn3">
+				Loading...
+			</div>
+		</div>
+	</body>
 </html>
--- a/themes/admin/simple-header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/admin/simple-header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,45 +1,45 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/includes/clientside/css/enano-shared.css" />
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
-    <!--[if IE]>
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-ie/iefixes.css" />
-    <![endif]-->
-    {JS_DYNAMIC_VARS}
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
-    <script type="text/javascript" src="{SCRIPTPATH}/themes/admin/js/menu.js"></script>
-    {ADDITIONAL_HEADERS}
-    </head>
-  <body>
-    <div id="header">
-      <div class="sitename">{SITE_NAME}</div>
-      <!-- div class="menulink"><a href="#" onclick="adminOpenMenu('sidebar', this); return false;">expand menu</a></div -->
-      [&nbsp;<a href="{SCRIPTPATH}/{ADMIN_SID_QUES}">Main page &#0187;</a>&nbsp;]
-    </div>
-    <div class="menu_nojs" id="pagebar_main">
-      <div class="label">Page tools</div>
-      {TOOLBAR}
-      <ul>
-        {TOOLBAR_EXTRAS}
-      </ul>
-      <span class="menuclear">&nbsp;</span>
-    </div>
-    <table border="0" cellspacing="0" cellpadding="0" id="sidebarholder">
-    <tr>
-    <td valign="top">
-    
-    <table border="0" cellspacing="0" cellpadding="0" class="wrapper">
-      <tr>
-        <td class="top-left"></td><td class="top">&nbsp;</td><td class="top-right"></td>
-      </tr>
-      <tr>
-        <td class="left"></td>
-        <td class="main">
-          <div style="float: right;">
-            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
-          </div>
-          <h2 class="pagename">{PAGE_NAME}</h2>
-          <div id="ajaxEditContainer">
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		<link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/includes/clientside/css/enano-shared.css" />
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
+		<!--[if IE]>
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-ie/iefixes.css" />
+		<![endif]-->
+		{JS_DYNAMIC_VARS}
+		<script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
+		<script type="text/javascript" src="{SCRIPTPATH}/themes/admin/js/menu.js"></script>
+		{ADDITIONAL_HEADERS}
+		</head>
+	<body>
+		<div id="header">
+			<div class="sitename">{SITE_NAME}</div>
+			<!-- div class="menulink"><a href="#" onclick="adminOpenMenu('sidebar', this); return false;">expand menu</a></div -->
+			[&nbsp;<a href="{SCRIPTPATH}/{ADMIN_SID_QUES}">Main page &#0187;</a>&nbsp;]
+		</div>
+		<div class="menu_nojs" id="pagebar_main">
+			<div class="label">Page tools</div>
+			{TOOLBAR}
+			<ul>
+				{TOOLBAR_EXTRAS}
+			</ul>
+			<span class="menuclear">&nbsp;</span>
+		</div>
+		<table border="0" cellspacing="0" cellpadding="0" id="sidebarholder">
+		<tr>
+		<td valign="top">
+		
+		<table border="0" cellspacing="0" cellpadding="0" class="wrapper">
+			<tr>
+				<td class="top-left"></td><td class="top">&nbsp;</td><td class="top-right"></td>
+			</tr>
+			<tr>
+				<td class="left"></td>
+				<td class="main">
+					<div style="float: right;">
+						<img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
+					</div>
+					<h2 class="pagename">{PAGE_NAME}</h2>
+					<div id="ajaxEditContainer">
--- a/themes/enanium/acledit.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/acledit.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,32 +1,32 @@
 <!-- VAR acl_field_begin -->
 <div class="tblholder">
-  <table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
-    <tr>
-      <th></th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
-    </tr>
+	<table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
+		<tr>
+			<th></th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
+		</tr>
 <!-- ENDVAR acl_field_begin -->
 <!-- VAR acl_field_item -->
-    <tr>
-      <td class="{ROW_CLASS}">{FIELD_DESC}</td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
-    </tr>
+		<tr>
+			<td class="{ROW_CLASS}">{FIELD_DESC}</td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
+		</tr>
 <!-- ENDVAR acl_field_item -->
 <!-- VAR acl_field_end -->
-    <tr>
-      <td colspan="6" class="row3">
-        {lang:acl_lbl_help}
-      </td>
-    </tr>
-  </table>
+		<tr>
+			<td colspan="6" class="row3">
+				{lang:acl_lbl_help}
+			</td>
+		</tr>
+	</table>
 </div>
 <!-- ENDVAR acl_field_end -->
 
--- a/themes/enanium/comment.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/comment.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,58 +1,58 @@
 <div class="tblholder">
-  <table border="0" width="100%" cellspacing="1" cellpadding="4">
-    <tr>
-      <th colspan="2" style="text-align: left;">{DATETIME}</th>
-    </tr>
-    <tr>
-      <td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-        <table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
-          <tr>
-            <td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              <b>{NAME}</b><br />
-              <small>{USER_LEVEL}</small>
-              <!-- BEGIN user_has_avatar -->
-              <div class="avatar">
-                <a href="{USERPAGE_LINK}">
-                  <img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
-                </a>
-              </div>
-              <!-- END user_has_avatar -->
-            </td>
-          </tr>
-          <tr>
-            <td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              {SEND_PM_LINK} {ADD_BUDDY_LINK}
-            </td>
-          </tr>
-        </table>
-      </td>
-      <td class="row2">
-        <b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
-      </td>
-    </tr>
-    <tr>
-      <td class="row3">
-        <div id="comment_{ID}">{DATA}</div>
-        <!-- BEGIN signature -->
-          <hr style="margin-left: 1em; width: 200px;" />
-          {SIGNATURE}
-        <!-- END signature -->
-      </td>
-    </tr>
-    <!-- BEGIN can_edit -->
-    <tr>
-      <td class="row2">
-        [ {EDIT_LINK} | {DELETE_LINK} ]
-      </td>
-    </tr>
-    <!-- END can_edit -->
-    <!-- BEGIN auth_mod -->
-    <tr>
-      <td class="row1">
-        <b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
-      </td>
-    </tr>
-    <!-- END auth_mod -->
-  </table>
+	<table border="0" width="100%" cellspacing="1" cellpadding="4">
+		<tr>
+			<th colspan="2" style="text-align: left;">{DATETIME}</th>
+		</tr>
+		<tr>
+			<td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+				<table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
+					<tr>
+						<td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							<b>{NAME}</b><br />
+							<small>{USER_LEVEL}</small>
+							<!-- BEGIN user_has_avatar -->
+							<div class="avatar">
+								<a href="{USERPAGE_LINK}">
+									<img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
+								</a>
+							</div>
+							<!-- END user_has_avatar -->
+						</td>
+					</tr>
+					<tr>
+						<td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							{SEND_PM_LINK} {ADD_BUDDY_LINK}
+						</td>
+					</tr>
+				</table>
+			</td>
+			<td class="row2">
+				<b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
+			</td>
+		</tr>
+		<tr>
+			<td class="row3">
+				<div id="comment_{ID}">{DATA}</div>
+				<!-- BEGIN signature -->
+					<hr style="margin-left: 1em; width: 200px;" />
+					{SIGNATURE}
+				<!-- END signature -->
+			</td>
+		</tr>
+		<!-- BEGIN can_edit -->
+		<tr>
+			<td class="row2">
+				[ {EDIT_LINK} | {DELETE_LINK} ]
+			</td>
+		</tr>
+		<!-- END can_edit -->
+		<!-- BEGIN auth_mod -->
+		<tr>
+			<td class="row1">
+				<b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
+			</td>
+		</tr>
+		<!-- END auth_mod -->
+	</table>
 </div>
 <br />
--- a/themes/enanium/css-extra/ie6.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/css-extra/ie6.css	Sun Mar 28 23:10:46 2010 -0400
@@ -1,5 +1,5 @@
 form.searchform, td#cell-sbleft {
-  background-color: #2b2b2b;
-  background-image: none;
+	background-color: #2b2b2b;
+	background-image: none;
 }
 
--- a/themes/enanium/css/babygrand.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/css/babygrand.css	Sun Mar 28 23:10:46 2010 -0400
@@ -7,423 +7,423 @@
 /* Core definitions - structure */
 
 html, body {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 
 div {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 
 body {
-  background-image: url(../images/background.gif);
-  background-color: #2a2a29;
-  font-family: trebuchet ms, verdana, tahoma, arial, helvetica, sans-serif;
-  font-size: 9pt;
+	background-image: url(../images/background.gif);
+	background-color: #2a2a29;
+	font-family: trebuchet ms, verdana, tahoma, arial, helvetica, sans-serif;
+	font-size: 9pt;
 }
 
 body#tinymce {
-  background-image: none;
-  background-color: #f0f0f0;
+	background-image: none;
+	background-color: #f0f0f0;
 }
 
 table#body-wrapper {
-  width: 100%;
+	width: 100%;
 }
 
 body.simple table#body-wrapper {
-  width: 70%;
-  margin: 0 auto;
+	width: 70%;
+	margin: 0 auto;
 }
 
 td#cell-sbleft {
-  background-image: url(../images/transb50.png);
+	background-image: url(../images/transb50.png);
 }
 
 td#cell-content {
-  background-color: #fff;
-  padding: 0;
-  width: 100%;
+	background-color: #fff;
+	padding: 0;
+	width: 100%;
 }
 
 /* Global - links */
 a {
-  text-decoration: none;
+	text-decoration: none;
 }
 
 a:link, a:visited {
-  color: #356a9e;
+	color: #356a9e;
 }
 
 div#content-wrapper a:visited {
-  color: #4c84bb;
+	color: #4c84bb;
 }
 
 a:link:hover {
-  color: #6a95c0;
+	color: #6a95c0;
 }
 
 /* Header */
 div#header {
-  line-height: 122px;
-  /* background-image: url(../images/top.gif); */
-  background-repeat: repeat-x;
-  color: #a9a9a9;
-  padding: 0 1.3em;
+	line-height: 122px;
+	/* background-image: url(../images/top.gif); */
+	background-repeat: repeat-x;
+	color: #a9a9a9;
+	padding: 0 1.3em;
 }
 
 body.simple div#header {
-  line-height: 78px;
-  width: 75%;
-  margin: 100px auto 0 auto;
+	line-height: 78px;
+	width: 75%;
+	margin: 100px auto 0 auto;
 }
 
 a.header-placeholder {
-  display: block;
-  width: 60%;
-  min-width: 500px;
-  position: absolute;
-  top: 0;
-  left: 0;
+	display: block;
+	width: 60%;
+	min-width: 500px;
+	position: absolute;
+	top: 0;
+	left: 0;
 }
 
 div#header h1 {
-  margin: 0;
-  font-weight: normal;
+	margin: 0;
+	font-weight: normal;
 }
 
 div#header h1 a {
-  color: white;
-  padding-right: 20px;
+	color: white;
+	padding-right: 20px;
 }
 
 div#header h1 a:hover {
-  background-image: url(../images/home.gif);
-  background-repeat: no-repeat;
-  background-position: center right;
+	background-image: url(../images/home.gif);
+	background-repeat: no-repeat;
+	background-position: center right;
 }
 
 div.logo {
-  width: 96px;
-  height: 122px;
-  background-image: url(../images/logo.png);
-  background-position: center center;
-  background-repeat: no-repeat;
-  float: left;
-  margin: 0 7px 0 -13px;
+	width: 96px;
+	height: 122px;
+	background-image: url(../images/logo.png);
+	background-position: center center;
+	background-repeat: no-repeat;
+	float: left;
+	margin: 0 7px 0 -13px;
 }
 
 /* Userlinks */
 ul.useropts {
-  position: absolute;
-  margin: 0;
-  right: 10px;
-  line-height: 19px;
-  top: 99px;
+	position: absolute;
+	margin: 0;
+	right: 10px;
+	line-height: 19px;
+	top: 99px;
 }
 
 ul.useropts li {
-  display: block;
-  float: left;
-  margin-right: 4px;
+	display: block;
+	float: left;
+	margin-right: 4px;
 }
 
 ul.useropts li a {
-  color: #a9b2e3;
-  display: block;
-  padding: 2px 12px;
-  background-color: #30475d;
-  -moz-border-radius: 6px 6px 0 0;
-  border-radius: 6px 6px 0 0;
+	color: #a9b2e3;
+	display: block;
+	padding: 2px 12px;
+	background-color: #30475d;
+	-moz-border-radius: 6px 6px 0 0;
+	border-radius: 6px 6px 0 0;
 }
 
 ul.useropts li.em a {
-  color: #b9d0e3;
-  background-color: #405f7c;
-  font-weight: bold;
+	color: #b9d0e3;
+	background-color: #405f7c;
+	font-weight: bold;
 }
 
 ul.useropts li:hover {
-  position: relative;
-  top: -2px;
+	position: relative;
+	top: -2px;
 }
 
 ul.useropts li:hover > a {
-  padding: 2px 12px 4px 12px;
-  color: #b9d0e3;
+	padding: 2px 12px 4px 12px;
+	color: #b9d0e3;
 }
 
 ul.useropts li.logout:hover > a {
-  color: #e9e9e9;
-  background-color: #701010;
+	color: #e9e9e9;
+	background-color: #701010;
 }
 
 /* Search form */
 
 form.searchform {
-  position: absolute;
-  /* background-color: #292929; */
-  background-image: url(../images/transb50.png);
-  top: 0px;
-  right: 10px;
-  padding: 7px;
-  text-align: right;
+	position: absolute;
+	/* background-color: #292929; */
+	background-image: url(../images/transb50.png);
+	top: 0px;
+	right: 10px;
+	padding: 7px;
+	text-align: right;
 }
 
 /* Sidebars */
 div.sidebar.left {
-  width: 150px;
+	width: 150px;
 }
 
 div.sidebar.right {
-  float: right;
-  width: 170px;
-  margin: 0 0 0 20px;
+	float: right;
+	width: 170px;
+	margin: 0 0 0 20px;
 }
 
 div.sidebar.right div.slider {
-  padding: 10px;
-  background-color: #bed8ef;
-  -moz-border-radius: 10px;
-  border-radius: 10px;
+	padding: 10px;
+	background-color: #bed8ef;
+	-moz-border-radius: 10px;
+	border-radius: 10px;
 }
 
 div.sidebar h4 {
-  margin: 0;
-  padding: 5px 3px;
-  color: #90B0D0;
-  border-bottom: 1px dotted #5b6f80;
-  font-size: 10pt;
+	margin: 0;
+	padding: 5px 3px;
+	color: #90B0D0;
+	border-bottom: 1px dotted #5b6f80;
+	font-size: 10pt;
 }
 
 div.sidebar div.slider {
-  font-size: 8pt;
+	font-size: 8pt;
 }
 
 div.sidebar h4 a {
-  cursor: pointer;
+	cursor: pointer;
 }
 
 div.sidebar.right h4 {
-  color: #5a87b3;
+	color: #5a87b3;
 }
 
 div.sidebar ul.linkblock {
-  margin: 0;
-  padding: 0;
-  list-style-type: none;
+	margin: 0;
+	padding: 0;
+	list-style-type: none;
 }
 
 div.sidebar ul.linkblock li a {
-  display: block;
-  color: #b2b2b2;
-  padding: 5px 3px 5px 0.9em;
+	display: block;
+	color: #b2b2b2;
+	padding: 5px 3px 5px 0.9em;
 }
 
 div.sidebar ul.linkblock li a:hover {
-  color: #c5c5c5;
-  background-color: #292929;
+	color: #c5c5c5;
+	background-color: #292929;
 }
 
 div.sidebar a.closebtn {
-  display: block;
-  float: right;
-  margin-right: 10px;
-  color: #456798;
-  background-color: #f0f0f0;
-  padding: 0 8px;
-  -moz-border-radius: 0 0 4px 4px;
-  cursor: pointer;
+	display: block;
+	float: right;
+	margin-right: 10px;
+	color: #456798;
+	background-color: #f0f0f0;
+	padding: 0 8px;
+	-moz-border-radius: 0 0 4px 4px;
+	cursor: pointer;
 }
 
 div.sidebar.left a.closebtn {
-  color: #f0f0f0;
-  background-color: #404040;
-  margin-right: 0px;
-  -moz-border-radius: 0 0 0 4px;
-  padding: 1pt 5px;
-  font-size: 10pt;
+	color: #f0f0f0;
+	background-color: #404040;
+	margin-right: 0px;
+	-moz-border-radius: 0 0 0 4px;
+	padding: 1pt 5px;
+	font-size: 10pt;
 }
 
 div.right-sidebar-hidden {
-  margin-left: 10px;
-  display: none;
+	margin-left: 10px;
+	display: none;
 }
 
 div.left-sidebar-hidden {
-  display: none;
-  position: absolute;
+	display: none;
+	position: absolute;
 }
 
 div.right-sidebar-hidden a.openbtn {
-  display: block;
-  float: right;
-  margin-right: -20px;
-  color: #456798;
-  background-color: #f0f0f0;
-  padding: 5px 8px;
-  -moz-border-radius: 4px 0 0 4px;
-  cursor: pointer;
+	display: block;
+	float: right;
+	margin-right: -20px;
+	color: #456798;
+	background-color: #f0f0f0;
+	padding: 5px 8px;
+	-moz-border-radius: 4px 0 0 4px;
+	cursor: pointer;
 }
 
 div.left-sidebar-hidden a.openbtn {
-  display: block;
-  float: left;
-  margin-top: 1.8em;
-  color: #456798;
-  background-color: #f0f0f0;
-  padding: 5px 4px;
-  -moz-border-radius: 0 4px 4px 0;
-  cursor: pointer;
+	display: block;
+	float: left;
+	margin-top: 1.8em;
+	color: #456798;
+	background-color: #f0f0f0;
+	padding: 5px 4px;
+	-moz-border-radius: 0 4px 4px 0;
+	cursor: pointer;
 }
 
 div.slider {
-  margin-bottom: 7px;
+	margin-bottom: 7px;
 }
 
 /* Content area */
 div#content-wrapper {
-  margin: 0;
-  padding: 20px;
+	margin: 0;
+	padding: 20px;
 }
 
 div.content {
-  /* Though very subtle, this makes the entire theme less harsh especially with Trebuchet. */
-  color: #202020;
+	/* Though very subtle, this makes the entire theme less harsh especially with Trebuchet. */
+	color: #202020;
 }
 
 div.content h1, div.content h2 {
-  border-bottom: 1px solid #90B0D0;
-  margin-top: 0.2em;
+	border-bottom: 1px solid #90B0D0;
+	margin-top: 0.2em;
 }
 
 div.content h1#h2PageName {
-  margin: 0.2em 0 0 0;
+	margin: 0.2em 0 0 0;
 }
 
 div.content p {
-  margin-left: 0.8em;
+	margin-left: 0.8em;
 }
 
 /* Wikilinks to pages that don't exist */
 div.content a.wikilink-nonexistent {
-  color: #B05020;
+	color: #B05020;
 }
 
 div.content a.wikilink-nonexistent:visited {
-  color: #906030 !important;
+	color: #906030 !important;
 }
 
 /* I know it's bad to support plugins in core code, but indented paragraphs tend to be specific to themes I designed.
-   This is for consistency between paragraphs and code blocks. */
+ 	This is for consistency between paragraphs and code blocks. */
 div.content pre, pre.geshi_highlighted {
-  margin-left: 0.8em;
+	margin-left: 0.8em;
 }
 
 /* Inline rename */
 
 input#pageheading {
-  font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
-  font-size: 18pt;
-  font-weight: bold;
-  border-width: 0 0 1px 0;
-  width: 100%;
-  border-bottom: 1px solid #90B0D0;
-  margin: 0;
-  padding: 0;
-  color: #202020;
-  background-color: rgb(240, 240, 240, 0.5);
+	font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
+	font-size: 18pt;
+	font-weight: bold;
+	border-width: 0 0 1px 0;
+	width: 100%;
+	border-bottom: 1px solid #90B0D0;
+	margin: 0;
+	padding: 0;
+	color: #202020;
+	background-color: rgb(240, 240, 240, 0.5);
 }
 
 input#pageheading:focus {
-  background-color: #fafafa;
+	background-color: #fafafa;
 }
 
 /* External links */
 
 div.content a[href ^="http://"], div#messageBox a[href ^="http://"] {
-  color: #4d78a2;
-  background: url(../images/links/external.gif) center right no-repeat;
-  padding-right: 16px;
+	color: #4d78a2;
+	background: url(../images/links/external.gif) center right no-repeat;
+	padding-right: 16px;
 }
 
 div.content a[href ^="https://"], div#messageBox a[href ^="https://"] {
-  color: #4d78a2;
-  background: url(../images/links/https.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #4d78a2;
+	background: url(../images/links/https.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.content a[href ^="mailto:"], div#messageBox a[href ^="mailto:"] {
-  color: #4d78a2;
-  background: url(../images/links/email.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #4d78a2;
+	background: url(../images/links/email.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.content a[href ^="irc://"], div#messageBox a[href ^="irc://"] {
-  color: #4d78a2;
-  background: url(../images/links/irc.gif)      center right no-repeat;
-  padding-right: 16px;
+	color: #4d78a2;
+	background: url(../images/links/irc.gif)      center right no-repeat;
+	padding-right: 16px;
 }
 
 div.content a[href ^="http://"]:hover, div#messageBox a[href ^="http://"]:hover {
-  color: #6488ad;
+	color: #6488ad;
 }
 
 div.content a[href ^="https://"]:hover, div#messageBox a[href ^="https://"]:hover {
-  color: #6488ad;
+	color: #6488ad;
 }
 
 div.content a[href ^="mailto:"]:hover, div#messageBox a[href ^="mailto:"]:hover {
-  color: #6488ad;
+	color: #6488ad;
 }
 
 div.content a[href ^="irc://"]:hover, div#messageBox a[href ^="irc://"]:hover {
-  color: #6488ad;
+	color: #6488ad;
 }
 
 div.content a.no_external, div#messageBox a.no_external {
-  background-image: none;
-  padding-right: 0px;
+	background-image: none;
+	padding-right: 0px;
 }
 
 /* Form controls */
 input {
-  border: 1px solid #353535;
-  background-color: #262626;
-  color: #b6b6b6;
-  font-size: 8pt;
-  font-family: arial, helvetica, sans-serif;
-  padding: 2px;
+	border: 1px solid #353535;
+	background-color: #262626;
+	color: #b6b6b6;
+	font-size: 8pt;
+	font-family: arial, helvetica, sans-serif;
+	padding: 2px;
 }
 
 div#messageBox input[type ^="text"], div#messageBox input[type ^="password"] {
-  background-color: #f4f4f4;
-  color: #202020;
-  border: 1px solid #aaa;
+	background-color: #f4f4f4;
+	color: #202020;
+	border: 1px solid #aaa;
 }
 
 div#messageBox input[type ^="text"]:focus, div#messageBox input[type ^="password"]:focus {
-  background-color: #fff;
-  border-color: #888;
+	background-color: #fff;
+	border-color: #888;
 }
 
 /* Footer */
 div#footer {
-  margin: 7px 0 0 0;
-  border-top: 1px solid #707070;
-  background-color: #000;
-  color: #909090;
-  padding: 4px;
-  font-size: smaller;
-  font-family: tahoma, arial, sans-serif;
+	margin: 7px 0 0 0;
+	border-top: 1px solid #707070;
+	background-color: #000;
+	color: #909090;
+	padding: 4px;
+	font-size: smaller;
+	font-family: tahoma, arial, sans-serif;
 }
 
 body.simple div#footer {
-  position: absolute;
-  bottom: 0px;
-  padding: 4px 0;
-  width: 100%;
+	position: absolute;
+	bottom: 0px;
+	padding: 4px 0;
+	width: 100%;
 }
 
 /*
@@ -431,28 +431,28 @@
  */
 
 ul.userpage_links li {
-  background-image: url('../images/buttonbg.gif');
-  background-repeat: repeat-x;
+	background-image: url('../images/buttonbg.gif');
+	background-repeat: repeat-x;
 }
 
 ul.userpage_links li a {
-  color: #202020;
+	color: #202020;
 }
 
 ul.userpage_links li.userpage_tab_active {
-  background-image: url('../images/buttonbg-lite.gif');
+	background-image: url('../images/buttonbg-lite.gif');
 }
 
 ul.userpage_links li:hover {
-  background-image: url('../images/buttonbg-lite.gif');
-  border-color: #404040 #404040 #ffffff #404040;
+	background-image: url('../images/buttonbg-lite.gif');
+	border-color: #404040 #404040 #ffffff #404040;
 }
 
 ul.userpage_links li.userpage_tab_active:hover {
 }
 
 ul.userpage_links li a:visited, ul.userpage_links li a:hover {
-  color: #202020 !important;
+	color: #202020 !important;
 }
 
 /*
@@ -460,8 +460,8 @@
  */
 
 div.tblholder th a {
-  text-decoration: underline;
-  color: #f0f0f0 !important;
+	text-decoration: underline;
+	color: #f0f0f0 !important;
 }
 
 /*
@@ -469,231 +469,231 @@
  */
 
 div.menu, div.menu_nojs {
-  background-image: url(../images/jbox.gif);
-  background-repeat: repeat-x;
-  background-color: #303030;
-  font-size: 7pt;
-  border-width: 0;
+	background-image: url(../images/jbox.gif);
+	background-repeat: repeat-x;
+	background-color: #303030;
+	font-size: 7pt;
+	border-width: 0;
 }
 .menu_bg {
-  background-color: #303030;
+	background-color: #303030;
 }
 div.menu ul, div.menu_nojs ul {
-  display: none;
-  position: absolute;
-  padding: 0;
-  margin: 0 !important;
-  background-color: #303030;
-  border-width: 0;
-  min-width: 120px;
-  text-transform: lowercase;
+	display: none;
+	position: absolute;
+	padding: 0;
+	margin: 0 !important;
+	background-color: #303030;
+	border-width: 0;
+	min-width: 120px;
+	text-transform: lowercase;
 }
 div.menu a, div.menu div.label, div.menu_nojs a, div.menu_nojs div.label {
-  padding: 2.5pt 5px;
-  margin-right: 3px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  color: #b2b2b2;
+	padding: 2.5pt 5px;
+	margin-right: 3px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	color: #b2b2b2;
 }
 div#content-wrapper div.menu a, div#content-wrapper div.menu_nojs a {
-  color: #b2b2b2;
+	color: #b2b2b2;
 }
 div.menu div.label, div.menu_nojs div.label {
-  color: #808080;
-  cursor: default;
+	color: #808080;
+	cursor: default;
 }
 div.menu span.sep, div.menu_nojs span.sep {
-  display: block;
-  float: left;
-  width: 5px;
+	display: block;
+	float: left;
+	width: 5px;
 }
 div.menu div.multopts, div.menu_nojs div.multopts {
-  line-height: 17pt;
+	line-height: 17pt;
 }
 div.menu div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts a, div.menu_nojs div.multopts div.label {
-  float: none;
-  display: inline;
+	float: none;
+	display: inline;
 }
 div.menu a.liteselected, div.menu a.liteselected:hover, div.menu a:hover, div.menu_nojs a.liteselected, div.menu_nojs a.liteselected:hover, div.menu_nojs a:hover {
-  color: #c9c9c9;
-  background-color: #484848;
-  background-image: url(../images/jbox.gif);
-  background-position: 0 -32px;
-  background-repeat: repeat-x;
+	color: #c9c9c9;
+	background-color: #484848;
+	background-image: url(../images/jbox.gif);
+	background-position: 0 -32px;
+	background-repeat: repeat-x;
 }
 div.menu input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="text"], div.menu_nojs input[type ^="password"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 2px 5px 3px 5px;
-  max-width: 70px;
-  color: #a9a9a9;
-  background-color: #191919;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 2px 5px 3px 5px;
+	max-width: 70px;
+	color: #a9a9a9;
+	background-color: #191919;
 }
 div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu_nojs input[type ^="password"]:hover {
-  background-color: #292929;
+	background-color: #292929;
 }
 div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu_nojs input[type ^="password"]:focus {
-  background-color: #373737;
+	background-color: #373737;
 }
 div.menu input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="button"], div.menu_nojs input[type ^="submit"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 3px 5px;
-  max-width: 70px;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 3px 5px;
+	max-width: 70px;
 }
 div.menu a.current, div.menu a.current:hover, div.menu a.selected, div.menu a.selected:hover, div.menu_nojs a.current, div.menu_nojs a.current:hover, div.menu_nojs a.selected, div.menu_nojs a.selected:hover {
-  color: #202020;
-  background-color: #FFFFFF;
+	color: #202020;
+	background-color: #FFFFFF;
 }
 div.menu a.current:hover, div.menu a.selected:hover, div.menu a.current.liteselected,
 div.menu_nojs a.current:hover, div.menu_nojs a.selected:hover, div.menu_nojs a.current.liteselected {
-  background-position: 0 -64px;
+	background-position: 0 -64px;
 }
 div.menu ul li, div.menu_nojs ul li {
-  list-style: none;
+	list-style: none;
 }
 div.menu ul a, div.menu_nojs ul a {
-  float: none;
-  margin: 0;
+	float: none;
+	margin: 0;
 }
 
 /* toolbar */
 
 div.toolbar {
-  border: 1px solid #3b619c;
-  background-color: #D0D0D0;
-  background-image: url(../../oxygen/images/bleu/sprite-horiz.gif);
-  background-position: 0 -90px;
-  padding: 1px 0;
-  height: 22px;
-  font-family: arial, sans-serif;
-  font-size: 8pt;
+	border: 1px solid #3b619c;
+	background-color: #D0D0D0;
+	background-image: url(../../oxygen/images/bleu/sprite-horiz.gif);
+	background-position: 0 -90px;
+	padding: 1px 0;
+	height: 22px;
+	font-family: arial, sans-serif;
+	font-size: 8pt;
 }
 div.toolbar ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar ul li {
-  list-style: none;
-  margin: 0;
-  float: left;
+	list-style: none;
+	margin: 0;
+	float: left;
 }
 div.toolbar a img {
-  opacity: 0.6;
+	opacity: 0.6;
 }
 div.toolbar a:hover img, div.toolbar a:focus img {
-  opacity: 1;
+	opacity: 1;
 }
 div.toolbar a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000 !important;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000 !important;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar a:hover, div.toolbar a:focus {
-  border: 1px solid #000080;
-  background-color: #ceceed;
-  background-image: url(../../oxygen/images/bleu/sprite-horiz.gif);
-  background-position: 0 -118px;
-  color: #000000 !important;
-  text-decoration: none;
+	border: 1px solid #000080;
+	background-color: #ceceed;
+	background-image: url(../../oxygen/images/bleu/sprite-horiz.gif);
+	background-position: 0 -118px;
+	color: #000000 !important;
+	text-decoration: none;
 }
 div.toolbar a:active {
-  background-color: #E0E0E0;
-  background-position: 0 -138px;
+	background-color: #E0E0E0;
+	background-position: 0 -138px;
 }
 div.toolbar img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar a span {
-  position: relative;
-  top: -3px !important;
+	position: relative;
+	top: -3px !important;
 }
 div.toolbar a span.noimage {
-  position: relative;
-  top: 0px !important;
-  height: 16px !important;
-  display: block;
-  padding-left: 2px !important;
+	position: relative;
+	top: 0px !important;
+	height: 16px !important;
+	display: block;
+	padding-left: 2px !important;
 }
 div.toolbar li span {
-  padding-left: 4px;
-  padding-right: 2px;
-  position: relative;
-  top: 4px;
+	padding-left: 4px;
+	padding-right: 2px;
+	position: relative;
+	top: 4px;
 }
 
 /* vertical toolbar */
 div.toolbar_vert {
-  border: 1px solid #82aae2;
-  background-color: #c9ddf8;
-  padding: 2px 0;
+	border: 1px solid #82aae2;
+	background-color: #c9ddf8;
+	padding: 2px 0;
 }
 div.toolbar_vert ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar_vert ul li {
-  list-style: none;
-  margin: 0;
+	list-style: none;
+	margin: 0;
 }
 div.toolbar_vert a img {
-  opacity: 0.6;
-  /*filter: alpha(opacity=60);*/
+	opacity: 0.6;
+	/*filter: alpha(opacity=60);*/
 }
 div.toolbar_vert a:hover img {
-  opacity: 1;
-  /*filter: alpha(opacity=100);*/
+	opacity: 1;
+	/*filter: alpha(opacity=100);*/
 }
 div.toolbar_vert a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000 !important;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000 !important;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar_vert a:hover {
-  border: 1px solid #202090;
-  background-color: #ceceed;
-  color: #000000 !important;
-  text-decoration: none;
-  background-image: url(../../oxygen/images/bleu/sprite-horiz.gif);
-  background-position: 0 -118px;
+	border: 1px solid #202090;
+	background-color: #ceceed;
+	color: #000000 !important;
+	text-decoration: none;
+	background-image: url(../../oxygen/images/bleu/sprite-horiz.gif);
+	background-position: 0 -118px;
 }
 div.toolbar_vert a:active {
-  border: 1px solid #A0A0A0;
-  background-color: #E0E0E0;
+	border: 1px solid #A0A0A0;
+	background-color: #E0E0E0;
 }
 div.toolbar_vert img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar_vert a span {
-  position: relative;
-  top: -4px;
+	position: relative;
+	top: -4px;
 }
 div.toolbar_vert li span {
-  padding-left: 2px;
-  padding-right: 5px;
+	padding-left: 2px;
+	padding-right: 5px;
 }
 
 div.toolbar_vert li > span {
-  display: block;
-  padding: 4px 5px;
+	display: block;
+	padding: 4px 5px;
 }
 
--- a/themes/enanium/elements.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/elements.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -17,30 +17,30 @@
 <!-- VAR sidebar_top -->
 <!-- ENDVAR sidebar_top -->
 <!-- VAR sidebar_section -->
-          <div class="slider">
-            <h4>
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-              <a>{TITLE}</a>
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-            </h4>
-            <div class="slideblock">
-              <ul class="linkblock">
-                {CONTENT}
-              </ul>
-            </div>
-          </div>
+					<div class="slider">
+						<h4>
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+							<a>{TITLE}</a>
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+						</h4>
+						<div class="slideblock">
+							<ul class="linkblock">
+								{CONTENT}
+							</ul>
+						</div>
+					</div>
 <!-- ENDVAR sidebar_section -->
 <!-- VAR sidebar_section_raw -->
-          <div class="slider">
-            <h4>
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-              <a>{TITLE}</a>
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-            </h4>
-            <div class="slideblock">
-              {CONTENT}
-            </div>
-          </div>
+					<div class="slider">
+						<h4>
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+							<a>{TITLE}</a>
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+						</h4>
+						<div class="slideblock">
+							{CONTENT}
+						</div>
+					</div>
 <!-- ENDVAR sidebar_section_raw -->
 <!-- VAR sidebar_bottom -->
 <!-- ENDVAR sidebar_bottom -->
--- a/themes/enanium/footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,31 +1,31 @@
-          </div> <!-- div#ajaxEditContainer -->
-          </td>
-          <!-- BEGIN right_sidebar -->
-          <td valign="top" class="td-right-sidebar">
-            <div class="right sidebar" id="enanium_sidebar_right">
-              <a class="closebtn" onclick="enanium_toggle_sidebar_right(); return false;">&raquo;</a>
-              {SIDEBAR_RIGHT}
-            </div>
-            <div class="right-sidebar-hidden" id="enanium_sidebar_right_hidden">
-              <a class="openbtn" onclick="enanium_toggle_sidebar_right(); return false;">&laquo;</a>
-            </div>
-            <!-- HOOK sidebar_right_post -->
-          </td>
-          <!-- END right_sidebar -->
-          </tr>
-          </table>
-        </div> <!-- div#content-wrapper -->
-      </td>
-    </tr>
-    </table>
-    <div id="footer">
-      <b>{COPYRIGHT}</b><br />
-      <!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
-      [[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<!-- BEGINNOT stupid_mode --><a href="http://validator.w3.org/check?uri=referer">{lang:page_w3c_valid_xhtml11}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">{lang:page_w3c_valid_css}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<!-- END stupid_mode -->[[StatsLong]]
-      <!-- Do not remove this line or scheduled tasks will not run. -->
-      <img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
-    </div>
-  {JS_FOOTER}
-  </body>
+					</div> <!-- div#ajaxEditContainer -->
+					</td>
+					<!-- BEGIN right_sidebar -->
+					<td valign="top" class="td-right-sidebar">
+						<div class="right sidebar" id="enanium_sidebar_right">
+							<a class="closebtn" onclick="enanium_toggle_sidebar_right(); return false;">&raquo;</a>
+							{SIDEBAR_RIGHT}
+						</div>
+						<div class="right-sidebar-hidden" id="enanium_sidebar_right_hidden">
+							<a class="openbtn" onclick="enanium_toggle_sidebar_right(); return false;">&laquo;</a>
+						</div>
+						<!-- HOOK sidebar_right_post -->
+					</td>
+					<!-- END right_sidebar -->
+					</tr>
+					</table>
+				</div> <!-- div#content-wrapper -->
+			</td>
+		</tr>
+		</table>
+		<div id="footer">
+			<b>{COPYRIGHT}</b><br />
+			<!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
+			[[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<!-- BEGINNOT stupid_mode --><a href="http://validator.w3.org/check?uri=referer">{lang:page_w3c_valid_xhtml11}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">{lang:page_w3c_valid_css}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<!-- END stupid_mode -->[[StatsLong]]
+			<!-- Do not remove this line or scheduled tasks will not run. -->
+			<img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
+		</div>
+	{JS_FOOTER}
+	</body>
 </html>
 
--- a/themes/enanium/header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,121 +1,121 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    {JS_DYNAMIC_VARS}
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css?{ENANO_VERSION}" />
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css?{ENANO_VERSION}" />
-    <!--[if lte IE 6]>
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/ie6.css" />
-    <![endif]-->
-    {JS_HEADER}
-    {ADDITIONAL_HEADERS}
-    <script type="text/javascript">//<![CDATA[
-    var auth_rename = <!-- BEGIN auth_rename -->true<!-- BEGINELSE auth_rename -->false<!-- END auth_rename -->;
-    // ]]>
-    </script>
-    <script type="text/javascript" src="{CDNPATH}/themes/{THEME_ID}/js/inlinerename.js"></script>
-  </head>
-  <body>
-    <div id="header">
-      <?php
-      global $session;
-      
-      if ( is_object($paths) && $head = $paths->sysMsg('SiteHeader') )
-      {
-        echo '<a class="header-placeholder" href="' . makeUrl(get_main_page()) . '">&nbsp;</a>';
-        echo $head;
-      }
-      else
-      {
-      ?>
-        <div class="logo"></div>
-        <h1><a href="<?php echo is_object($session) ? makeUrl(get_main_page(), false, true) : scriptPath . '/'; ?>">{SITE_NAME}</a></h1>
-      <?php
-        }
-      ?>
-    </div>
-    <!-- HOOK enanium_main_header -->
-    <!-- BEGINNOT stupid_mode -->
-    <form class="searchform" action="{SCRIPTPATH}/index.php" method="get">
-      <div>
-        <input type="hidden" name="title" value="{NS_SPECIAL}Search" />
-        <input type="hidden" name="auth" value="{ADMIN_SID_RAW}" />
-        <input type="text" name="q" value="" />
-        <input type="submit" value="search" />
-      </div>
-      <!-- HOOK enanium_search_form -->
-    </form>
-    <ul class="useropts">
-      <!-- BEGIN user_logged_in -->
-      <li class="em"><a href="{url:User:{USERNAME}}">{USERNAME}</a></li>
-      <li><a href="{url:Special:Preferences}">{lang:sidebar_btn_preferences_short}</a></li>
-      <li class="logout"><a href="{url:Special:Logout/{CSRF_TOKEN}/{PAGE_URLNAME}}" onclick="mb_logout(); return false;">{lang:sidebar_btn_logout}</a></li>
-      <!-- BEGINELSE user_logged_in -->
-      <li class="em"><a href="{url:Special:Login}" onclick="ajaxStartLogin(); return false;">{lang:sidebar_btn_login}</a></li>
-      <li><a href="{url:Special:Register}">{lang:sidebar_btn_register}</a></li>
-      <!-- END user_logged_in -->
-    </ul>
-    <!-- END stupid_mode -->
-    <!-- Yes this is table based. For reliability reasons. -->
-    <table border="0" cellspacing="0" cellpadding="0" id="body-wrapper">
-    <tr>
-      <td valign="top" id="cell-sbleft">
-        <div class="left sidebar" id="enanium_sidebar_left">
-          <a class="closebtn" onclick="enanium_toggle_sidebar_left(); return false;">&laquo;</a>
-          {SIDEBAR_LEFT}
-        </div>
-        <div class="left-sidebar-hidden" id="enanium_sidebar_left_hidden">
-          <a class="openbtn" onclick="enanium_toggle_sidebar_left(); return false;">&raquo;</a>
-        </div>
-        <!-- HOOK sidebar_left_post -->
-      </td>
-      <td valign="top" id="cell-content">
-        <!-- BEGINNOT stupid_mode -->
-        <div class="menu_nojs" style="float: right; margin-right: 10px;">
-          <a href="#" onclick="return false;">{lang:onpage_lbl_changes}</a>
-          <ul class="jbox_right">
-            <li><a href="{url:Special:Log/user={USERNAME}|escape}">{lang:onpage_btn_changes_mine}</a></li>
-            <li><a href="{url:Special:Log/within=1w|escape}">{lang:onpage_btn_changes_recent}</a></li>
-            <li><a href="{url:Special:Log/page={PAGE_URLNAME}|escape}">{lang:onpage_btn_changes_history}</a></li>
-          </ul>
-          <a href="#" onclick="return false;">{lang:onpage_lbl_sitetools}</a>
-          <ul class="jbox_right">
-            <li><a href="{url:Special:CreatePage|escape}">{lang:sidebar_btn_createpage}</a></li>
-            <li><a href="{url:Special:UploadFile|escape}">{lang:sidebar_btn_uploadfile}</a></li>
-            <li><a href="{url:Special:SpecialPages|escape}">{lang:sidebar_btn_specialpages}</a></li>
-            <!-- BEGIN user_logged_in -->
-            <li><a href="{url:Special:Memberlist|escape}">{lang:specialpage_member_list}</a></li>
-            <!-- END user_logged_in -->
-            <!-- BEGIN auth_admin -->
-            {SIDEBAR_LINK}
-            {ADMIN_LINK}
-            <!-- END auth_admin -->
-          </ul>
-          <span class="menuclear"></span>
-        </div>
-        <!-- END stupid_mode -->
-        <div class="menu_nojs" id="pagebar_main">
-          <div class="label">
-            <!-- BEGIN stupid_mode -->
-            Page tools
-            <!-- BEGINELSE stupid_mode -->
-            {lang:onpage_lbl_pagetools}
-            <!-- END stupid_mode -->
-          </div>
-          {TOOLBAR}
-          <ul>
-            {TOOLBAR_EXTRAS}
-          </ul>
-          <span class="menuclear"></span>
-        </div>
-        <div id="content-wrapper" class="content">
-          <table border="0" cellspacing="0" cellpadding="0" style="width: 100%;">
-          <tr>
-          <td valign="top" style="width: 100%;">
-          <div style="float: right;">
-            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
-          </div>
-          <h1 <!-- BEGIN auth_rename -->title="{lang:onpage_btn_rename_inline}" <!-- END auth_rename -->id="h2PageName">{PAGE_NAME}</h1>
-          <div id="ajaxEditContainer">
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		{JS_DYNAMIC_VARS}
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css?{ENANO_VERSION}" />
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css?{ENANO_VERSION}" />
+		<!--[if lte IE 6]>
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/ie6.css" />
+		<![endif]-->
+		{JS_HEADER}
+		{ADDITIONAL_HEADERS}
+		<script type="text/javascript">//<![CDATA[
+		var auth_rename = <!-- BEGIN auth_rename -->true<!-- BEGINELSE auth_rename -->false<!-- END auth_rename -->;
+		// ]]>
+		</script>
+		<script type="text/javascript" src="{CDNPATH}/themes/{THEME_ID}/js/inlinerename.js"></script>
+	</head>
+	<body>
+		<div id="header">
+			<?php
+			global $session;
+			
+			if ( is_object($paths) && $head = $paths->sysMsg('SiteHeader') )
+			{
+				echo '<a class="header-placeholder" href="' . makeUrl(get_main_page()) . '">&nbsp;</a>';
+				echo $head;
+			}
+			else
+			{
+			?>
+				<div class="logo"></div>
+				<h1><a href="<?php echo is_object($session) ? makeUrl(get_main_page(), false, true) : scriptPath . '/'; ?>">{SITE_NAME}</a></h1>
+			<?php
+				}
+			?>
+		</div>
+		<!-- HOOK enanium_main_header -->
+		<!-- BEGINNOT stupid_mode -->
+		<form class="searchform" action="{SCRIPTPATH}/index.php" method="get">
+			<div>
+				<input type="hidden" name="title" value="{NS_SPECIAL}Search" />
+				<input type="hidden" name="auth" value="{ADMIN_SID_RAW}" />
+				<input type="text" name="q" value="" />
+				<input type="submit" value="search" />
+			</div>
+			<!-- HOOK enanium_search_form -->
+		</form>
+		<ul class="useropts">
+			<!-- BEGIN user_logged_in -->
+			<li class="em"><a href="{url:User:{USERNAME}}">{USERNAME}</a></li>
+			<li><a href="{url:Special:Preferences}">{lang:sidebar_btn_preferences_short}</a></li>
+			<li class="logout"><a href="{url:Special:Logout/{CSRF_TOKEN}/{PAGE_URLNAME}}" onclick="mb_logout(); return false;">{lang:sidebar_btn_logout}</a></li>
+			<!-- BEGINELSE user_logged_in -->
+			<li class="em"><a href="{url:Special:Login}" onclick="ajaxStartLogin(); return false;">{lang:sidebar_btn_login}</a></li>
+			<li><a href="{url:Special:Register}">{lang:sidebar_btn_register}</a></li>
+			<!-- END user_logged_in -->
+		</ul>
+		<!-- END stupid_mode -->
+		<!-- Yes this is table based. For reliability reasons. -->
+		<table border="0" cellspacing="0" cellpadding="0" id="body-wrapper">
+		<tr>
+			<td valign="top" id="cell-sbleft">
+				<div class="left sidebar" id="enanium_sidebar_left">
+					<a class="closebtn" onclick="enanium_toggle_sidebar_left(); return false;">&laquo;</a>
+					{SIDEBAR_LEFT}
+				</div>
+				<div class="left-sidebar-hidden" id="enanium_sidebar_left_hidden">
+					<a class="openbtn" onclick="enanium_toggle_sidebar_left(); return false;">&raquo;</a>
+				</div>
+				<!-- HOOK sidebar_left_post -->
+			</td>
+			<td valign="top" id="cell-content">
+				<!-- BEGINNOT stupid_mode -->
+				<div class="menu_nojs" style="float: right; margin-right: 10px;">
+					<a href="#" onclick="return false;">{lang:onpage_lbl_changes}</a>
+					<ul class="jbox_right">
+						<li><a href="{url:Special:Log/user={USERNAME}|escape}">{lang:onpage_btn_changes_mine}</a></li>
+						<li><a href="{url:Special:Log/within=1w|escape}">{lang:onpage_btn_changes_recent}</a></li>
+						<li><a href="{url:Special:Log/page={PAGE_URLNAME}|escape}">{lang:onpage_btn_changes_history}</a></li>
+					</ul>
+					<a href="#" onclick="return false;">{lang:onpage_lbl_sitetools}</a>
+					<ul class="jbox_right">
+						<li><a href="{url:Special:CreatePage|escape}">{lang:sidebar_btn_createpage}</a></li>
+						<li><a href="{url:Special:UploadFile|escape}">{lang:sidebar_btn_uploadfile}</a></li>
+						<li><a href="{url:Special:SpecialPages|escape}">{lang:sidebar_btn_specialpages}</a></li>
+						<!-- BEGIN user_logged_in -->
+						<li><a href="{url:Special:Memberlist|escape}">{lang:specialpage_member_list}</a></li>
+						<!-- END user_logged_in -->
+						<!-- BEGIN auth_admin -->
+						{SIDEBAR_LINK}
+						{ADMIN_LINK}
+						<!-- END auth_admin -->
+					</ul>
+					<span class="menuclear"></span>
+				</div>
+				<!-- END stupid_mode -->
+				<div class="menu_nojs" id="pagebar_main">
+					<div class="label">
+						<!-- BEGIN stupid_mode -->
+						Page tools
+						<!-- BEGINELSE stupid_mode -->
+						{lang:onpage_lbl_pagetools}
+						<!-- END stupid_mode -->
+					</div>
+					{TOOLBAR}
+					<ul>
+						{TOOLBAR_EXTRAS}
+					</ul>
+					<span class="menuclear"></span>
+				</div>
+				<div id="content-wrapper" class="content">
+					<table border="0" cellspacing="0" cellpadding="0" style="width: 100%;">
+					<tr>
+					<td valign="top" style="width: 100%;">
+					<div style="float: right;">
+						<img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
+					</div>
+					<h1 <!-- BEGIN auth_rename -->title="{lang:onpage_btn_rename_inline}" <!-- END auth_rename -->id="h2PageName">{PAGE_NAME}</h1>
+					<div id="ajaxEditContainer">
--- a/themes/enanium/simple-footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/simple-footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,18 +1,18 @@
-          </div> <!-- div#ajaxEditContainer -->
-          </td>
-          </tr>
-          </table>
-        </div> <!-- div#content-wrapper -->
-      </td>
-    </tr>
-    </table>
-    <div id="footer">
-      <b>{COPYRIGHT}</b><br />
-      [[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<!-- BEGINNOT stupid_mode --><a href="http://validator.w3.org/check?uri=referer">{lang:page_w3c_valid_xhtml11}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">{lang:page_w3c_valid_css}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<!-- END stupid_mode -->[[StatsLong]]
-      <!-- Do not remove this line or scheduled tasks will not run. -->
-      <img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
-    </div>
-  {JS_FOOTER}
-  </body>
+					</div> <!-- div#ajaxEditContainer -->
+					</td>
+					</tr>
+					</table>
+				</div> <!-- div#content-wrapper -->
+			</td>
+		</tr>
+		</table>
+		<div id="footer">
+			<b>{COPYRIGHT}</b><br />
+			[[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<!-- BEGINNOT stupid_mode --><a href="http://validator.w3.org/check?uri=referer">{lang:page_w3c_valid_xhtml11}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">{lang:page_w3c_valid_css}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<!-- END stupid_mode -->[[StatsLong]]
+			<!-- Do not remove this line or scheduled tasks will not run. -->
+			<img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
+		</div>
+	{JS_FOOTER}
+	</body>
 </html>
 
--- a/themes/enanium/simple-header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/simple-header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,25 +1,25 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    {JS_DYNAMIC_VARS}
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/structure.css" />
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
-    {JS_HEADER}
-    {ADDITIONAL_HEADERS}
-  </head>
-  <body class="simple">
-    <div id="header">
-      <h1>{PAGE_NAME}</h1>
-    </div>
-    <!-- Yes this is table based. For reliability reasons. -->
-    <table border="0" cellspacing="0" cellpadding="0" id="body-wrapper">
-    <tr>
-      <td valign="top" id="cell-content">
-        <div id="content-wrapper" class="content">
-          <table border="0" cellspacing="0" cellpadding="0">
-          <tr>
-          <td valign="top">
-          <div id="ajaxEditContainer">
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		{JS_DYNAMIC_VARS}
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/structure.css" />
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
+		{JS_HEADER}
+		{ADDITIONAL_HEADERS}
+	</head>
+	<body class="simple">
+		<div id="header">
+			<h1>{PAGE_NAME}</h1>
+		</div>
+		<!-- Yes this is table based. For reliability reasons. -->
+		<table border="0" cellspacing="0" cellpadding="0" id="body-wrapper">
+		<tr>
+			<td valign="top" id="cell-content">
+				<div id="content-wrapper" class="content">
+					<table border="0" cellspacing="0" cellpadding="0">
+					<tr>
+					<td valign="top">
+					<div id="ajaxEditContainer">
--- a/themes/enanium/toolbar.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/enanium/toolbar.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,62 +1,62 @@
 <!-- Stuff related to toolbars and clickable buttons.
-     The plan was to use this on the toolbar for most pages. Never made it into the release,
-     but still provided as an otherwise-unused component for plugins to make use of.
-     -->
+ 		The plan was to use this on the toolbar for most pages. Never made it into the release,
+ 		but still provided as an otherwise-unused component for plugins to make use of.
+ 		-->
 
 <!-- VAR toolbar_start -->
-  <div class="toolbar">
-  <ul>
+	<div class="toolbar">
+	<ul>
 <!-- ENDVAR toolbar_start -->
 <!-- VAR toolbar_button -->
-  <li>
-    <a title="{TITLE}" {FLAGS}>
-      <!-- IFSET SPRITE -->
-        {SPRITE}
-      <!-- BEGINELSE SPRITE -->
-        <!-- IFSET IMAGE -->
-          <!-- BEGINNOT no_image -->
-            <img alt="{TITLE}" src="{IMAGE}" />
-          <!-- END no_image -->
-        <!-- END IMAGE -->
-      <!-- END SPRITE -->
-      <!-- BEGIN show_title -->
-        <!-- BEGIN no_image -->
-          <span class="noimage">{TITLE}</span>
-        <!-- BEGINELSE no_image -->
-          <span>{TITLE}</span>
-        <!-- END no_image -->
-      <!-- END show_title -->
-    </a>
-  </li>
+	<li>
+		<a title="{TITLE}" {FLAGS}>
+			<!-- IFSET SPRITE -->
+				{SPRITE}
+			<!-- BEGINELSE SPRITE -->
+				<!-- IFSET IMAGE -->
+					<!-- BEGINNOT no_image -->
+						<img alt="{TITLE}" src="{IMAGE}" />
+					<!-- END no_image -->
+				<!-- END IMAGE -->
+			<!-- END SPRITE -->
+			<!-- BEGIN show_title -->
+				<!-- BEGIN no_image -->
+					<span class="noimage">{TITLE}</span>
+				<!-- BEGINELSE no_image -->
+					<span>{TITLE}</span>
+				<!-- END no_image -->
+			<!-- END show_title -->
+		</a>
+	</li>
 <!-- ENDVAR toolbar_button -->
 <!-- VAR toolbar_label -->
-  <li>
-    <span>{TITLE}</span>
-  </li>
+	<li>
+		<span>{TITLE}</span>
+	</li>
 <!-- ENDVAR toolbar_label -->
 <!-- VAR toolbar_end -->
-  </ul>
-  </div>
+	</ul>
+	</div>
 <!-- ENDVAR toolbar_end -->
 
 <!-- VAR toolbar_vert_start -->
-  <div class="toolbar_vert">
-  <ul>
+	<div class="toolbar_vert">
+	<ul>
 <!-- ENDVAR toolbar_vert_start -->
 <!-- VAR toolbar_vert_button -->
-  <li>
-    <a title="{TITLE}" {FLAGS}>
-      <img alt="{TITLE}" src="{IMAGE}" />
-      <span>{TITLE}</span>
-    </a>
-  </li>
+	<li>
+		<a title="{TITLE}" {FLAGS}>
+			<img alt="{TITLE}" src="{IMAGE}" />
+			<span>{TITLE}</span>
+		</a>
+	</li>
 <!-- ENDVAR toolbar_vert_button -->
 <!-- VAR toolbar_vert_label -->
-  <li>
-    <span>{TITLE}</span>
-  </li>
+	<li>
+		<span>{TITLE}</span>
+	</li>
 <!-- ENDVAR toolbar_vert_label -->
 <!-- VAR toolbar_vert_end -->
-  </ul>
-  </div>
+	</ul>
+	</div>
 <!-- ENDVAR toolbar_vert_end -->
--- a/themes/oxygen/acledit.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/acledit.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,32 +1,32 @@
 <!-- VAR acl_field_begin -->
 <div class="tblholder">
-  <table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
-    <tr>
-      <th></th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
-    </tr>
+	<table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
+		<tr>
+			<th></th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
+		</tr>
 <!-- ENDVAR acl_field_begin -->
 <!-- VAR acl_field_item -->
-    <tr>
-      <td class="{ROW_CLASS}">{FIELD_DESC}</td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
-    </tr>
+		<tr>
+			<td class="{ROW_CLASS}">{FIELD_DESC}</td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
+		</tr>
 <!-- ENDVAR acl_field_item -->
 <!-- VAR acl_field_end -->
-    <tr>
-      <td colspan="6" class="row3">
-        {lang:acl_lbl_help}
-      </td>
-    </tr>
-  </table>
+		<tr>
+			<td colspan="6" class="row3">
+				{lang:acl_lbl_help}
+			</td>
+		</tr>
+	</table>
 </div>
 <!-- ENDVAR acl_field_end -->
 
--- a/themes/oxygen/comment.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/comment.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,58 +1,58 @@
 <div class="tblholder">
-  <table border="0" width="100%" cellspacing="1" cellpadding="4">
-    <tr>
-      <th colspan="2" style="text-align: left;">{DATETIME}</th>
-    </tr>
-    <tr>
-      <td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-        <table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
-          <tr>
-            <td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              <b>{NAME}</b><br />
-              <small>{USER_LEVEL}</small>
-              <!-- BEGIN user_has_avatar -->
-              <div class="avatar">
-                <a href="{USERPAGE_LINK}">
-                  <img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
-                </a>
-              </div>
-              <!-- END user_has_avatar -->
-            </td>
-          </tr>
-          <tr>
-            <td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              {SEND_PM_LINK} {ADD_BUDDY_LINK}
-            </td>
-          </tr>
-        </table>
-      </td>
-      <td class="row2">
-        <b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
-      </td>
-    </tr>
-    <tr>
-      <td class="row3">
-        <div id="comment_{ID}">{DATA}</div>
-        <!-- BEGIN signature -->
-          <hr style="margin-left: 1em; width: 200px;" />
-          {SIGNATURE}
-        <!-- END signature -->
-      </td>
-    </tr>
-    <!-- BEGIN can_edit -->
-    <tr>
-      <td class="row2">
-        [ {EDIT_LINK} | {DELETE_LINK} ]
-      </td>
-    </tr>
-    <!-- END can_edit -->
-    <!-- BEGIN auth_mod -->
-    <tr>
-      <td class="row1">
-        <b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
-      </td>
-    </tr>
-    <!-- END auth_mod -->
-  </table>
+	<table border="0" width="100%" cellspacing="1" cellpadding="4">
+		<tr>
+			<th colspan="2" style="text-align: left;">{DATETIME}</th>
+		</tr>
+		<tr>
+			<td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+				<table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
+					<tr>
+						<td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							<b>{NAME}</b><br />
+							<small>{USER_LEVEL}</small>
+							<!-- BEGIN user_has_avatar -->
+							<div class="avatar">
+								<a href="{USERPAGE_LINK}">
+									<img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
+								</a>
+							</div>
+							<!-- END user_has_avatar -->
+						</td>
+					</tr>
+					<tr>
+						<td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							{SEND_PM_LINK} {ADD_BUDDY_LINK}
+						</td>
+					</tr>
+				</table>
+			</td>
+			<td class="row2">
+				<b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
+			</td>
+		</tr>
+		<tr>
+			<td class="row3">
+				<div id="comment_{ID}">{DATA}</div>
+				<!-- BEGIN signature -->
+					<hr style="margin-left: 1em; width: 200px;" />
+					{SIGNATURE}
+				<!-- END signature -->
+			</td>
+		</tr>
+		<!-- BEGIN can_edit -->
+		<tr>
+			<td class="row2">
+				[ {EDIT_LINK} | {DELETE_LINK} ]
+			</td>
+		</tr>
+		<!-- END can_edit -->
+		<!-- BEGIN auth_mod -->
+		<tr>
+			<td class="row1">
+				<b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
+			</td>
+		</tr>
+		<!-- END auth_mod -->
+	</table>
 </div>
 <br />
--- a/themes/oxygen/css/bleu.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/css/bleu.css	Sun Mar 28 23:10:46 2010 -0400
@@ -6,174 +6,174 @@
 
 /* The basics */
 html,body {
-  height: 100%;
+	height: 100%;
 }
 
 body {
-  /* color added in 1.0.2 to fix light text in dark desktop themes */ 
-  color: #202020;
-  margin: 0;
-  padding: 0;
-  background: url(../images/bleu/bg.png);
-  font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
-  font-size: 9pt;
+	/* color added in 1.0.2 to fix light text in dark desktop themes */ 
+	color: #202020;
+	margin: 0;
+	padding: 0;
+	background: url(../images/bleu/bg.png);
+	font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
+	font-size: 9pt;
 }
 
 body#tinymce {
-  background-color: white;
-  background-image: none;
+	background-color: white;
+	background-image: none;
 }
 
 .holder {
-  border: 1px solid #CCCCCC;
-  padding: 1px;
-  background-color: #FFFFFF;
-  color: #444444
+	border: 1px solid #CCCCCC;
+	padding: 1px;
+	background-color: #FFFFFF;
+	color: #444444
 }
 
 div.pad {
-  padding: 10px;
+	padding: 10px;
 }
 
 table#title {
-  margin: 0;
-  padding: 0;
-  height: 100px;
-  background-color: #90B0D0;
-  text-align: center;
+	margin: 0;
+	padding: 0;
+	height: 100px;
+	background-color: #90B0D0;
+	text-align: center;
 }
 
 table.simple-layout td#mainhead {
-  margin: 0;
-  padding: 0;
-  background-color: #90B0D0;
-  text-align: center;
+	margin: 0;
+	padding: 0;
+	background-color: #90B0D0;
+	text-align: center;
 }
 
 table.simple-layout td#mainhead h1 {
-  margin: 15px 0;
-  padding: 0;
-  font-size: 14pt;
+	margin: 15px 0;
+	padding: 0;
+	font-size: 14pt;
 }
 
 /* Sidebar */
 td.mdgSidebarHolder {
-  width: 140px;
+	width: 140px;
 }
 
 div.sidebar {
-  width: 138px;
-  background-color: #F8F8F8; border-left: 1px solid #CCC; border-right: 1px solid #CCC; padding: 1px 0px 0px 0px;
+	width: 138px;
+	background-color: #F8F8F8; border-left: 1px solid #CCC; border-right: 1px solid #CCC; padding: 1px 0px 0px 0px;
 }
 
 div.sidebar .head {
-  background-color: #F0F0F0;
-  display: block;
-  margin: 0px 1px 1px 1px;
-  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
-  font-size: 7pt;
-  cursor: pointer;
-  text-decoration: none;
-  color: #111;
-  padding: 5px;
-  font-weight: bold;
+	background-color: #F0F0F0;
+	display: block;
+	margin: 0px 1px 1px 1px;
+	font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+	font-size: 7pt;
+	cursor: pointer;
+	text-decoration: none;
+	color: #111;
+	padding: 5px;
+	font-weight: bold;
 }
 
 div.sidebar .head:hover {
-  background-color: #F4F4F4;
-  display: block;
-  margin: 0px 1px 1px 1px;
-  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
-  font-size: 7pt;
-  cursor: pointer;
-  text-decoration: none;
-  color: #111;
-  padding: 5px;
-  font-weight: bold;
+	background-color: #F4F4F4;
+	display: block;
+	margin: 0px 1px 1px 1px;
+	font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+	font-size: 7pt;
+	cursor: pointer;
+	text-decoration: none;
+	color: #111;
+	padding: 5px;
+	font-weight: bold;
 }
 
 div.sidebar div.slideblock a {
-  background-color: #DDD;
-  display: block;
-  margin: 0px 1px;
-  border-bottom: 1px solid #FFF;
-  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
-  font-size: 7pt;
-  cursor: pointer;
-  text-decoration: none;
-  color: #666;
-  padding: 5px 5px 5px 9px;
-  list-style-type: none;
+	background-color: #DDD;
+	display: block;
+	margin: 0px 1px;
+	border-bottom: 1px solid #FFF;
+	font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+	font-size: 7pt;
+	cursor: pointer;
+	text-decoration: none;
+	color: #666;
+	padding: 5px 5px 5px 9px;
+	list-style-type: none;
 }
 
 div.sidebar div.slideblock a:hover {
-  background-color: #EEE;
+	background-color: #EEE;
 }
 
 div.recttop {
-  width: 140px;
-  height: 12px;
-  margin: 0;
-  padding: 0;
+	width: 140px;
+	height: 12px;
+	margin: 0;
+	padding: 0;
 }
 
 td.recttoptop {
-  width: 100%;
-  height: 12px;
-  background-image: url(../images/bleu/sprite-horiz.gif);
-  background-repeat: repeat-x;
-  background-position: 0 -12px;
-  margin: 0;
-  padding: 0;
+	width: 100%;
+	height: 12px;
+	background-image: url(../images/bleu/sprite-horiz.gif);
+	background-repeat: repeat-x;
+	background-position: 0 -12px;
+	margin: 0;
+	padding: 0;
 }
 
 td.recttoptop:hover {
-  width: 100%;
-  height: 12px;
-  background-image: url(../images/bleu/sprite-horiz.gif);
-  background-repeat: repeat-x;
-  background-position: 0 -24px;
-  margin: 0;
-  padding: 0;
-  cursor: pointer;
+	width: 100%;
+	height: 12px;
+	background-image: url(../images/bleu/sprite-horiz.gif);
+	background-repeat: repeat-x;
+	background-position: 0 -24px;
+	margin: 0;
+	padding: 0;
+	cursor: pointer;
 }
 
 div.rectbot {
-  width: 140px;
-  height: 12px;
-  margin: 0;
-  padding: 0;
+	width: 140px;
+	height: 12px;
+	margin: 0;
+	padding: 0;
 }
 
 td.rectbottop {
-  width: 100%;
-  height: 12px;
-  background-image: url(../images/bleu/sprite-horiz.gif);
-  background-repeat: repeat-x;
-  background-position: 0 -48px;
-  margin: 0;
-  padding: 0;
+	width: 100%;
+	height: 12px;
+	background-image: url(../images/bleu/sprite-horiz.gif);
+	background-repeat: repeat-x;
+	background-position: 0 -48px;
+	margin: 0;
+	padding: 0;
 }
 
 div.slideblock, .dbx-content {
-  overflow: hidden;
-  background-color: #DDD;
+	overflow: hidden;
+	background-color: #DDD;
 }
 
 div.slideblock2 {
-  overflow: hidden;
-  background-color: #DDD;
-  margin: 0px 1px 0px 1px;
-  border-bottom: 1px solid #FFF;
-  font-size: 8pt;
+	overflow: hidden;
+	background-color: #DDD;
+	margin: 0px 1px 0px 1px;
+	border-bottom: 1px solid #FFF;
+	font-size: 8pt;
 }
 
 div.slideblock2 p {
-  margin: 7px 4px;
+	margin: 7px 4px;
 }
 
 .dbx-handle {
-  cursor: move !important;
+	cursor: move !important;
 }
 
 /* The credits thingy at the bottom */
@@ -187,200 +187,200 @@
 
 /* Text, headings, and links inside the main div (usually #ajaxEditContainer but used some other places as well) */
 div.contentDiv h1 {
-  margin-top: 0.3em;
+	margin-top: 0.3em;
 }
 
 div.contentDiv h1, div.contentDiv h2 {
-  border-bottom: 1px solid #90B0D0;
-  margin-bottom: 0;
+	border-bottom: 1px solid #90B0D0;
+	margin-bottom: 0;
 }
 
 div.contentDiv h3 {
-  font-size: 11pt;
-  font-weight: bold;
+	font-size: 11pt;
+	font-weight: bold;
 }
 
 div.contentDiv ul li, div#messageBox ul li {
-  list-style: url(../images/bleu/bullet.gif);
+	list-style: url(../images/bleu/bullet.gif);
 }
 
 div.contentDiv p, div#messageBox p {
-  margin-left: 1.0em;
+	margin-left: 1.0em;
 }
 
 table.simple-layout div.contentDiv p {
-  margin-left: 0em;
+	margin-left: 0em;
 }
 
 div.contentDiv blockquote, div#messageBox blockquote {
-  background-color: #F4F4F4;
-  border: 1px dotted #406080;
-  margin: 1em;
-  padding: 10px;
-  max-height: 250px;
-  overflow: auto;
+	background-color: #F4F4F4;
+	border: 1px dotted #406080;
+	margin: 1em;
+	padding: 10px;
+	max-height: 250px;
+	overflow: auto;
 }
 
 div.contentDiv, div#messageBox {
-  font-size: 9pt;
+	font-size: 9pt;
 }
 
 a {
-  color: #7090B0;
+	color: #7090B0;
 }
 
 a:hover {
-  color: #90B0D0;
+	color: #90B0D0;
 }
 
 div.contentDiv a[href ^="http://"], div#messageBox a[href ^="http://"] {
-  color: #80A0C0;
-  background: url(../images/bleu/external.gif) center right no-repeat;
-  padding-right: 16px;
+	color: #80A0C0;
+	background: url(../images/bleu/external.gif) center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="https://"], div#messageBox a[href ^="https://"] {
-  color: #80A0C0;
-  background: url(../images/bleu/https.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #80A0C0;
+	background: url(../images/bleu/https.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="mailto:"], div#messageBox a[href ^="mailto:"] {
-  color: #80A0C0;
-  background: url(../images/bleu/email.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #80A0C0;
+	background: url(../images/bleu/email.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="irc://"], div#messageBox a[href ^="irc://"] {
-  color: #80A0C0;
-  background: url(../images/bleu/irc.gif)      center right no-repeat;
-  padding-right: 16px;
+	color: #80A0C0;
+	background: url(../images/bleu/irc.gif)      center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="http://"]:hover, div#messageBox a[href ^="http://"]:hover {
-  color: #A0C0E0;
-  background: url(../images/bleu/external.gif) center right no-repeat;
-  padding-right: 16px;
+	color: #A0C0E0;
+	background: url(../images/bleu/external.gif) center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="https://"]:hover, div#messageBox a[href ^="https://"]:hover {
-  color: #A0C0E0;
-  background: url(../images/bleu/https.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #A0C0E0;
+	background: url(../images/bleu/https.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="mailto:"]:hover, div#messageBox a[href ^="mailto:"]:hover {
-  color: #A0C0E0;
-  background: url(../images/bleu/email.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #A0C0E0;
+	background: url(../images/bleu/email.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="irc://"]:hover, div#messageBox a[href ^="irc://"]:hover {
-  color: #A0C0E0;
-  background: url(../images/bleu/irc.gif)      center right no-repeat;
-  padding-right: 16px;
+	color: #A0C0E0;
+	background: url(../images/bleu/irc.gif)      center right no-repeat;
+	padding-right: 16px;
 }
 
 /* Wikilinks to pages that don't exist */
 div.contentDiv a.wikilink-nonexistent {
-  color: #B05020;
+	color: #B05020;
 }
 
 div.contentDiv a.wikilink-nonexistent:hover {
-  color: #D06030;
+	color: #D06030;
 }
 
 /* Well, not Midget and not comments (usually), but that's what the class is called ;-). Basically an informational window or used as a wrapper for tables. */
 .mdg-comment, .mdg-infobox {
-  margin-left: 1em;
-  padding: 7px;
-  border: 1px solid #AAAAAA;
-  background-color: #E8E8E8;
+	margin-left: 1em;
+	padding: 7px;
+	border: 1px solid #AAAAAA;
+	background-color: #E8E8E8;
 }
 
 .tblholder {
-  margin: 10px 0 0 0;
-  padding: 0;
-  border: 1px solid #AAAAAA;
-  background-color: #E8E8E8;
+	margin: 10px 0 0 0;
+	padding: 0;
+	border: 1px solid #AAAAAA;
+	background-color: #E8E8E8;
 }
 
 /* The beautiful tables inside what may not obviously be mdg-comment divs */
 div.tblholder td.row1 {
-  padding: 4px;
-  background-color: #E0E0E0;
+	padding: 4px;
+	background-color: #E0E0E0;
 }
 
 div.tblholder td.row2 {
-  padding: 4px;
-  background-color: #F0F0F0;
+	padding: 4px;
+	background-color: #F0F0F0;
 }
 
 div.tblholder td.row3 {
-  padding: 4px;
-  background-color: #E8E8E8;
+	padding: 4px;
+	background-color: #E8E8E8;
 }
 
 div.tblholder th {
-  padding: 4px;
-  background-color: #7080A0;
-  font-weight: bold;
-  text-align: center;
-  color: #FFFFFF;
+	padding: 4px;
+	background-color: #7080A0;
+	font-weight: bold;
+	text-align: center;
+	color: #FFFFFF;
 }
 
 div.tblholder th.subhead {
-  padding: 4px;
-  background-color: #90A0B0;
-  font-weight: bold;
-  text-align: center;
-  color: #FFFFFF;
+	padding: 4px;
+	background-color: #90A0B0;
+	font-weight: bold;
+	text-align: center;
+	color: #FFFFFF;
 }
 
 div.tblholder table {
-  background-color: #FFFFFF;
-  width: 100%;
+	background-color: #FFFFFF;
+	width: 100%;
 }
 
 /* Colored table cells */
 div.tblholder td.row1_red {
-  padding: 4px;
-  background-color: #F8E0E0;
+	padding: 4px;
+	background-color: #F8E0E0;
 }
 
 div.tblholder td.row2_red {
-  padding: 4px;
-  background-color: #FFF0F0;
+	padding: 4px;
+	background-color: #FFF0F0;
 }
 
 div.tblholder td.row3_red {
-  padding: 4px;
-  background-color: #FFE8E8;
+	padding: 4px;
+	background-color: #FFE8E8;
 }
 
 div.tblholder td.row1_green {
-  padding: 4px;
-  background-color: #E0F8E0;
+	padding: 4px;
+	background-color: #E0F8E0;
 }
 
 div.tblholder td.row2_green {
-  padding: 4px;
-  background-color: #F0FFF0;
+	padding: 4px;
+	background-color: #F0FFF0;
 }
 
 div.tblholder td.row3_green {
-  padding: 4px;
-  background-color: #E8FFE8;
+	padding: 4px;
+	background-color: #E8FFE8;
 }
 
 div.tblholder th a {
-  color: #FFFFFF !important;
-  text-decoration: underline !important;
+	color: #FFFFFF !important;
+	text-decoration: underline !important;
 }
 
 div.tblholder th a:hover {
-  color: #FFFF00 !important;
-  text-decoration: underline !important;
+	color: #FFFF00 !important;
+	text-decoration: underline !important;
 }
 
 /*
@@ -388,95 +388,95 @@
  */
 
 div.menu, div.menu_nojs {
-  background-color: #B0D0F0;
-  font-size: 7pt;
-  border-width: 0;
+	background-color: #B0D0F0;
+	font-size: 7pt;
+	border-width: 0;
 }
 .menu_bg {
-  background-color: #B0D0F0;
+	background-color: #B0D0F0;
 }
 div.menu a, div.menu div.label {
-  padding: 2.5pt 5px;
-  margin-right: 3px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  color: #406080;
+	padding: 2.5pt 5px;
+	margin-right: 3px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	color: #406080;
 }
 div.menu_nojs a, div.menu_nojs div.label {
-  padding: 2.5pt 5px;
-  margin-right: 3px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  color: #406080;
+	padding: 2.5pt 5px;
+	margin-right: 3px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	color: #406080;
 }
 div.menu div.label, div.menu_nojs div.label {
-  color: #001020;
-  cursor: default;
+	color: #001020;
+	cursor: default;
 }
 div.menu span.sep, div.menu_nojs span.sep {
-  display: block;
-  float: left;
-  width: 5px;
+	display: block;
+	float: left;
+	width: 5px;
 }
 div.menu div.multopts, div.menu_nojs div.multopts {
-  line-height: 17pt;
+	line-height: 17pt;
 }
 div.menu div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts a, div.menu_nojs div.multopts div.label {
-  float: none;
-  display: inline;
+	float: none;
+	display: inline;
 }
 div.menu a.liteselected, div.menu a.liteselected:hover, div.menu a:hover, div.menu_nojs a.liteselected, div.menu_nojs a.liteselected:hover, div.menu_nojs a:hover {
-  color: #406080;
-  background-color: #D0F0FF;
+	color: #406080;
+	background-color: #D0F0FF;
 }
 div.menu input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="text"], div.menu_nojs input[type ^="password"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 2px 5px 3px 5px;
-  max-width: 70px;
-  background-color: #D0F0FF;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 2px 5px 3px 5px;
+	max-width: 70px;
+	background-color: #D0F0FF;
 }
 div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu_nojs input[type ^="password"]:hover {
-  background-color: #E0F0FF;
+	background-color: #E0F0FF;
 }
 div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu_nojs input[type ^="password"]:focus {
-  background-color: #F0F0FF;
+	background-color: #F0F0FF;
 }
 div.menu input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="button"], div.menu_nojs input[type ^="submit"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 3px 5px;
-  max-width: 70px;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 3px 5px;
+	max-width: 70px;
 }
 div.menu a.current, div.menu a.current:hover, div.menu a.selected, div.menu a.selected:hover, div.menu_nojs a.current, div.menu_nojs a.current:hover, div.menu_nojs a.selected, div.menu_nojs a.selected:hover {
-  color: #000040;
-  background-color: #FFFFFF;
+	color: #000040;
+	background-color: #FFFFFF;
 }
 div.menu ul, div.menu_nojs ul {
-  display: none;
-  position: absolute;
-  padding: 0;
-  margin: 0 !important;
-  background-color: #B0D0F0;
-  border-width: 0;
-  min-width: 120px;
+	display: none;
+	position: absolute;
+	padding: 0;
+	margin: 0 !important;
+	background-color: #B0D0F0;
+	border-width: 0;
+	min-width: 120px;
 }
 div.menu ul li, div.menu_nojs ul li {
-  list-style: none;
+	list-style: none;
 }
 div.menu ul a, div.menu_nojs ul a {
-  float: none;
-  margin: 0;
+	float: none;
+	margin: 0;
 }
 span.menuclear {
-  font-size: 1px;
-  height: 0px;
-  width: 0px;
-  clear: left;
-  line-height: 0px;
-  display: block;
+	font-size: 1px;
+	height: 0px;
+	width: 0px;
+	clear: left;
+	line-height: 0px;
+	display: block;
 }
 
 /* Rounded corners on nearly everything */
@@ -505,297 +505,297 @@
 
 /* Buttons and textboxes - these settings are used almost everywhere */
 input, textarea, select, button {
-  border: 1px solid #406080;
-  background-color: #F2F2F2;
-  color: #202020;
-  padding: 3px;
-  font-family: arial, helvetica, sans-serif;
-  font-size: 8pt;
+	border: 1px solid #406080;
+	background-color: #F2F2F2;
+	color: #202020;
+	padding: 3px;
+	font-family: arial, helvetica, sans-serif;
+	font-size: 8pt;
 }
 
 input:hover, textarea:hover, select:hover {
-  border: 1px solid #6080A0;
-  background-color: #F8F8F8;
-  padding: 3px;
+	border: 1px solid #6080A0;
+	background-color: #F8F8F8;
+	padding: 3px;
 }
 
 input:focus, textarea:focus, select:focus {
-  border: 1px solid #90B0D0;
-  background-color: #FFFFFF;
-  padding: 3px;
+	border: 1px solid #90B0D0;
+	background-color: #FFFFFF;
+	padding: 3px;
 }
 
 input.ac_loading {
-  background-image: url(../../../images/loading.gif);
-  background-position: right center;
-  background-repeat: no-repeat;
+	background-image: url(../../../images/loading.gif);
+	background-position: right center;
+	background-repeat: no-repeat;
 }
 
 label {
-  padding: 3px;
-  cursor: pointer;
-  font-family: arial, helvetica, sans-serif;
-  font-size: 8pt;
+	padding: 3px;
+	cursor: pointer;
+	font-family: arial, helvetica, sans-serif;
+	font-size: 8pt;
 }
 
 label:hover {
-  padding: 3px;
-  cursor: pointer;
-  background-color: #F0F0F0;
+	padding: 3px;
+	cursor: pointer;
+	background-color: #F0F0F0;
 }
 
 input#pageheading {
-  font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
-  font-size: 18pt;
-  font-weight: bold;
-  border-width: 0 0 1px 0;
-  width: 100%;
-  border-bottom: 1px solid #90B0D0;
-  margin: 0;
-  padding: 0;
-  color: #202020;
+	font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
+	font-size: 18pt;
+	font-weight: bold;
+	border-width: 0 0 1px 0;
+	width: 100%;
+	border-bottom: 1px solid #90B0D0;
+	margin: 0;
+	padding: 0;
+	color: #202020;
 }
 
 input#pageheading:focus {
-  background-color: #fafafa;
+	background-color: #fafafa;
 }
 
 input[type ^="button"], input[type ^="submit"], button {
-  background-image: url(../images/bleu/sprite-horiz.gif);
-  background-position: 0% -60px;
-  background-repeat: repeat-x;
-  color: #202020;
+	background-image: url(../images/bleu/sprite-horiz.gif);
+	background-position: 0% -60px;
+	background-repeat: repeat-x;
+	color: #202020;
 }
 
 input[type ^="image"][disabled ^="disabled"] {
-  opacity: 0.5;
-  filter: alpha(opacity=50);
+	opacity: 0.5;
+	filter: alpha(opacity=50);
 }
 
 input[type ^="button"][disabled ^="disabled"], input[type ^="submit"][disabled ^="disabled"], button[disabled ^="disabled"], .btn-disabled {
-  color: #808080 !important;
-  background-image: none !important;
-  background-color: #e0e0e0 !important;
+	color: #808080 !important;
+	background-image: none !important;
+	background-color: #e0e0e0 !important;
 }
 
 /* The Wordpress-like fills behind checkboxes and their labels */
 .catCheck {
-  padding: 3px;
+	padding: 3px;
 }
 
 .catCheck:hover {
-  padding: 3px;
-  background-color: #F0F0F0;
+	padding: 3px;
+	background-color: #F0F0F0;
 }
 
 /* Information, warning, question, error, and wait boxes */
 div.error-box {
-  background-image: url(../../../images/error.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #FFF4F4;
-  border: 1px dashed #406080;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/error.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #FFF4F4;
+	border: 1px dashed #406080;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 div.info-box {
-  background-image: url(../../../images/info.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #F4F4FF;
-  border: 1px dashed #406080;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/info.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #F4F4FF;
+	border: 1px dashed #406080;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 div.warning-box {
-  background-image: url(../../../images/warning.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #FFFFF4;
-  border: 1px dashed #406080;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/warning.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #FFFFF4;
+	border: 1px dashed #406080;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 div.question-box {
-  background-image: url(../../../images/question.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #F4FFF4;
-  border: 1px dashed #406080;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/question.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #F4FFF4;
+	border: 1px dashed #406080;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 div.wait-box {
-  background-image: url(../../../images/wait.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #FFF4FF;
-  border: 1px dashed #406080;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/wait.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #FFF4FF;
+	border: 1px dashed #406080;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 /* This stuff is mostly unused, left in for compatibility */
 div#ajaxEditContainer table {
-  border: 0px solid #FFFFFF;
+	border: 0px solid #FFFFFF;
 }
 
 div#ajaxEditContainer td {
-  margin: 1px;
+	margin: 1px;
 }
 
 div#ajaxEditContainer pre {
-  margin-left: 1em;
-  background-color: #F8F8F8;
-  border: 1px dashed #90B0D0;
-  padding: 10px;
-  overflow: auto;
-  max-height: 150px;
+	margin-left: 1em;
+	background-color: #F8F8F8;
+	border: 1px dashed #90B0D0;
+	padding: 10px;
+	overflow: auto;
+	max-height: 150px;
 }
 
 /* toolbar */
 div.toolbar {
-  border: 1px solid #3b619c;
-  background-color: #D0D0D0;
-  background-image: url(../images/bleu/sprite-horiz.gif);
-  background-position: 0 -90px;
-  padding: 1px 0;
-  height: 22px;
-  font-family: arial, sans-serif;
-  font-size: 8pt;
+	border: 1px solid #3b619c;
+	background-color: #D0D0D0;
+	background-image: url(../images/bleu/sprite-horiz.gif);
+	background-position: 0 -90px;
+	padding: 1px 0;
+	height: 22px;
+	font-family: arial, sans-serif;
+	font-size: 8pt;
 }
 div.toolbar ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar ul li {
-  list-style: none;
-  margin: 0;
-  float: left;
+	list-style: none;
+	margin: 0;
+	float: left;
 }
 div.toolbar a img {
-  opacity: 0.6;
+	opacity: 0.6;
 }
 div.toolbar a:hover img, div.toolbar a:focus img {
-  opacity: 1;
+	opacity: 1;
 }
 div.toolbar a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar a:hover, div.toolbar a:focus {
-  border: 1px solid #000080;
-  background-color: #ceceed;
-  background-image: url(../images/bleu/sprite-horiz.gif);
-  background-position: 0 -118px;
-  color: #000000;
-  text-decoration: none;
+	border: 1px solid #000080;
+	background-color: #ceceed;
+	background-image: url(../images/bleu/sprite-horiz.gif);
+	background-position: 0 -118px;
+	color: #000000;
+	text-decoration: none;
 }
 div.toolbar a:active {
-  background-color: #E0E0E0;
-  background-position: 0 -138px;
+	background-color: #E0E0E0;
+	background-position: 0 -138px;
 }
 div.toolbar img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar a span {
-  position: relative;
-  top: -3px !important;
+	position: relative;
+	top: -3px !important;
 }
 div.toolbar a span.noimage {
-  position: relative;
-  top: 0px !important;
-  height: 16px !important;
-  display: block;
-  padding-left: 2px !important;
+	position: relative;
+	top: 0px !important;
+	height: 16px !important;
+	display: block;
+	padding-left: 2px !important;
 }
 div.toolbar li span {
-  padding-left: 4px;
-  padding-right: 2px;
-  position: relative;
-  top: 4px;
+	padding-left: 4px;
+	padding-right: 2px;
+	position: relative;
+	top: 4px;
 }
 
 /* vertical toolbar */
 div.toolbar_vert {
-  border: 1px solid #909090;
-  background-color: #D0D0D0;
-  padding: 2px 0;
+	border: 1px solid #909090;
+	background-color: #D0D0D0;
+	padding: 2px 0;
 }
 div.toolbar_vert ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar_vert ul li {
-  list-style: none;
-  margin: 0;
+	list-style: none;
+	margin: 0;
 }
 div.toolbar_vert a img {
-  opacity: 0.6;
-  /*filter: alpha(opacity=60);*/
+	opacity: 0.6;
+	/*filter: alpha(opacity=60);*/
 }
 div.toolbar_vert a:hover img {
-  opacity: 1;
-  /*filter: alpha(opacity=100);*/
+	opacity: 1;
+	/*filter: alpha(opacity=100);*/
 }
 div.toolbar_vert a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar_vert a:hover {
-  border: 1px solid #202090;
-  background-color: #ceceed;
-  color: #000000;
-  text-decoration: none;
+	border: 1px solid #202090;
+	background-color: #ceceed;
+	color: #000000;
+	text-decoration: none;
 }
 div.toolbar_vert a:active {
-  border: 1px solid #A0A0A0;
-  background-color: #E0E0E0;
+	border: 1px solid #A0A0A0;
+	background-color: #E0E0E0;
 }
 div.toolbar_vert img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar_vert a span {
-  position: relative;
-  top: -4px;
+	position: relative;
+	top: -4px;
 }
 div.toolbar_vert li span {
-  padding-left: 2px;
-  padding-right: 5px;
+	padding-left: 2px;
+	padding-right: 5px;
 }
 
 div.toolbar_vert li > span {
-  display: block;
-  padding: 4px 5px;
+	display: block;
+	padding: 4px 5px;
 }
 
 /*
@@ -803,24 +803,24 @@
  */
 
 ul.userpage_links li {
-  background-image: url('../images/buttonbg.gif');
-  background-repeat: repeat-x;
+	background-image: url('../images/buttonbg.gif');
+	background-repeat: repeat-x;
 }
 
 ul.userpage_links li a {
-  color: #202020;
+	color: #202020;
 }
 
 ul.userpage_links li.userpage_tab_active {
-  background-image: url('../images/buttonbg-lite.gif');
+	background-image: url('../images/buttonbg-lite.gif');
 }
 
 ul.userpage_links li:hover {
-  background-image: url('../images/buttonbg-lite.gif');
-  border-color: #404040 #404040 #ffffff #404040;
-  border-bottom-width: 0;
+	background-image: url('../images/buttonbg-lite.gif');
+	border-color: #404040 #404040 #ffffff #404040;
+	border-bottom-width: 0;
 }
 
 ul.userpage_links li.userpage_tab_active:hover {
-  border-bottom-width: 1px;
+	border-bottom-width: 1px;
 }
--- a/themes/oxygen/css/mint.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/css/mint.css	Sun Mar 28 23:10:46 2010 -0400
@@ -6,178 +6,178 @@
 
 /* The basics */
 html,body {
-  height: 100%;
+	height: 100%;
 }
 
 body {
-  /* color added in 1.0.2 to fix light text in dark desktop themes */ 
-  color: #202020;
-  margin: 0;
-  padding: 0;
-  background: url(../images/mint/bg.png);
-  font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
-  font-size: 9pt;
+	/* color added in 1.0.2 to fix light text in dark desktop themes */ 
+	color: #202020;
+	margin: 0;
+	padding: 0;
+	background: url(../images/mint/bg.png);
+	font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
+	font-size: 9pt;
 }
 
 body#tinymce {
-  background-color: white;
-  background-image: none;
+	background-color: white;
+	background-image: none;
 }
 
 .holder {
-  border: 1px solid #CCCCCC;
-  padding: 1px;
-  background-color: #FFFFFF;
-  color: #444444
+	border: 1px solid #CCCCCC;
+	padding: 1px;
+	background-color: #FFFFFF;
+	color: #444444
 }
 
 div.pad {
-  padding: 10px;
+	padding: 10px;
 }
 
 table#title {
-  margin: 0;
-  padding: 0;
-  height: 100px;
-  background-color: #90D0B0;
-  text-align: center;
+	margin: 0;
+	padding: 0;
+	height: 100px;
+	background-color: #90D0B0;
+	text-align: center;
 }
 
 table.simple-layout td#mainhead {
-  margin: 0;
-  padding: 0;
-  background-color: #90D0B0;
-  text-align: center;
+	margin: 0;
+	padding: 0;
+	background-color: #90D0B0;
+	text-align: center;
 }
 
 table.simple-layout td#mainhead h1 {
-  margin: 15px 0;
-  padding: 0;
-  font-size: 14pt;
+	margin: 15px 0;
+	padding: 0;
+	font-size: 14pt;
 }
 
 /* Sidebar */
 td.mdgSidebarHolder {
-  width: 140px;
+	width: 140px;
 }
 
 div.sidebar, .dbx-group {
-  width: 138px;
-  background-color: #F8F8F8; border-left: 1px solid #CCC; border-right: 1px solid #CCC; padding: 1px 0px 0px 0px;
+	width: 138px;
+	background-color: #F8F8F8; border-left: 1px solid #CCC; border-right: 1px solid #CCC; padding: 1px 0px 0px 0px;
 }
 
 div.sidebar .head, .dbx-handle {
-  background-color: #F0F0F0;
-  display: block;
-  margin: 0px 1px 1px 1px;
-  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
-  font-size: 7pt;
-  cursor: pointer;
-  text-decoration: none;
-  color: #111;
-  padding: 5px;
-  font-weight: bold;
+	background-color: #F0F0F0;
+	display: block;
+	margin: 0px 1px 1px 1px;
+	font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+	font-size: 7pt;
+	cursor: pointer;
+	text-decoration: none;
+	color: #111;
+	padding: 5px;
+	font-weight: bold;
 }
 
 div.sidebar .head:hover, .dbx-handle:hover {
-  background-color: #F4F4F4;
-  display: block;
-  margin: 0px 1px 1px 1px;
-  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
-  font-size: 7pt;
-  cursor: pointer;
-  text-decoration: none;
-  color: #111;
-  padding: 5px;
-  font-weight: bold;
+	background-color: #F4F4F4;
+	display: block;
+	margin: 0px 1px 1px 1px;
+	font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+	font-size: 7pt;
+	cursor: pointer;
+	text-decoration: none;
+	color: #111;
+	padding: 5px;
+	font-weight: bold;
 }
 
 div.sidebar div.slideblock a, .dbx-content li {
-  background-color: #DDD;
-  display: block;
-  margin: 0px 1px;
-  border-bottom: 1px solid #FFF;
-  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
-  font-size: 7pt;
-  cursor: pointer;
-  text-decoration: none;
-  color: #666;
-  padding: 5px 5px 5px 9px;
-  list-style-type: none;
+	background-color: #DDD;
+	display: block;
+	margin: 0px 1px;
+	border-bottom: 1px solid #FFF;
+	font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+	font-size: 7pt;
+	cursor: pointer;
+	text-decoration: none;
+	color: #666;
+	padding: 5px 5px 5px 9px;
+	list-style-type: none;
 }
 
 div.sidebar div.slideblock a:hover, .dbx-content li:hover {
-  background-color: #EEE;
-  display: block;
-  margin: 0px 1px;
-  border-bottom: 1px solid #FFF;
-  font-family: Trebuchet MS, Arial, helvetica, sans-serif;
-  font-size: 7pt;
-  cursor: pointer;
-  text-decoration: none;
-  color: #666;
-  padding: 5px 5px 5px 9px;
+	background-color: #EEE;
+	display: block;
+	margin: 0px 1px;
+	border-bottom: 1px solid #FFF;
+	font-family: Trebuchet MS, Arial, helvetica, sans-serif;
+	font-size: 7pt;
+	cursor: pointer;
+	text-decoration: none;
+	color: #666;
+	padding: 5px 5px 5px 9px;
 }
 
 div.recttop {
-  width: 140px;
-  height: 12px;
-  margin: 0;
-  padding: 0;
+	width: 140px;
+	height: 12px;
+	margin: 0;
+	padding: 0;
 }
 
 td.recttoptop {
-  width: 100%;
-  height: 12px;
-  background-image: url(../images/mint/sprite-horiz.gif);
-  background-repeat: repeat-x;
-  background-position: 0 -12px;
-  margin: 0;
-  padding: 0;
+	width: 100%;
+	height: 12px;
+	background-image: url(../images/mint/sprite-horiz.gif);
+	background-repeat: repeat-x;
+	background-position: 0 -12px;
+	margin: 0;
+	padding: 0;
 }
 
 td.recttoptop:hover {
-  width: 100%;
-  height: 12px;
-  background-image: url(../images/mint/sprite-horiz.gif);
-  background-repeat: repeat-x;
-  background-position: 0 -24px;
-  margin: 0;
-  padding: 0;
-  cursor: pointer;
+	width: 100%;
+	height: 12px;
+	background-image: url(../images/mint/sprite-horiz.gif);
+	background-repeat: repeat-x;
+	background-position: 0 -24px;
+	margin: 0;
+	padding: 0;
+	cursor: pointer;
 }
 
 div.rectbot {
-  width: 140px;
-  height: 12px;
-  margin: 0;
-  padding: 0;
+	width: 140px;
+	height: 12px;
+	margin: 0;
+	padding: 0;
 }
 
 td.rectbottop {
-  width: 100%;
-  height: 12px;
-  background-image: url(../images/mint/sprite-horiz.gif);
-  background-repeat: repeat-x;
-  background-position: 0 -48px;
-  margin: 0;
-  padding: 0;
+	width: 100%;
+	height: 12px;
+	background-image: url(../images/mint/sprite-horiz.gif);
+	background-repeat: repeat-x;
+	background-position: 0 -48px;
+	margin: 0;
+	padding: 0;
 }
 
 div.slideblock, .dbx-content {
-  overflow: hidden;
-  background-color: #DDD;
+	overflow: hidden;
+	background-color: #DDD;
 }
 
 div.slideblock2 {
-  overflow: hidden;
-  background-color: #DDD;
-  margin: 0px 1px 0px 1px;
-  border-bottom: 1px solid #FFF;
+	overflow: hidden;
+	background-color: #DDD;
+	margin: 0px 1px 0px 1px;
+	border-bottom: 1px solid #FFF;
 }
 
 .dbx-handle {
-  cursor: move !important;
+	cursor: move !important;
 }
 
 /* The credits thingy at the bottom */
@@ -191,200 +191,200 @@
 
 /* Text, headings, and links inside the main div (usually #ajaxEditContainer but used some other places as well) */
 div.contentDiv h1 {
-  margin-top: 0.3em;
+	margin-top: 0.3em;
 }
 
 div.contentDiv h1, div.contentDiv h2 {
-  border-bottom: 1px solid #90D0B0;
-  margin-bottom: 0;
+	border-bottom: 1px solid #90D0B0;
+	margin-bottom: 0;
 }
 
 div.contentDiv h3 {
-  font-size: 11pt;
-  font-weight: bold;
+	font-size: 11pt;
+	font-weight: bold;
 }
 
 div.contentDiv ul li, div#messageBox ul li {
-  list-style: url(../images/mint/bullet.gif);
+	list-style: url(../images/mint/bullet.gif);
 }
 
 div.contentDiv p, div#messageBox p {
-  margin-left: 1.0em;
+	margin-left: 1.0em;
 }
 
 table.simple-layout div.contentDiv p {
-  margin-left: 0em;
+	margin-left: 0em;
 }
 
 div.contentDiv blockquote, div#messageBox blockquote {
-  background-color: #F4F4F4;
-  border: 1px dotted #408060;
-  margin: 1em;
-  padding: 10px;
-  max-height: 250px;
-  overflow: auto;
+	background-color: #F4F4F4;
+	border: 1px dotted #408060;
+	margin: 1em;
+	padding: 10px;
+	max-height: 250px;
+	overflow: auto;
 }
 
 div.contentDiv, div#messageBox {
-  font-size: 9pt;
+	font-size: 9pt;
 }
 
 a {
-  color: #70B090;
+	color: #70B090;
 }
 
 a:hover {
-  color: #90D0B0;
+	color: #90D0B0;
 }
 
 div.contentDiv a[href ^="http://"], div#messageBox a[href ^="http://"] {
-  color: #80C0A0;
-  background: url(../images/mint/external.gif) center right no-repeat;
-  padding-right: 16px;
+	color: #80C0A0;
+	background: url(../images/mint/external.gif) center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="https://"], div#messageBox a[href ^="https://"] {
-  color: #80C0A0;
-  background: url(../images/mint/https.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #80C0A0;
+	background: url(../images/mint/https.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="mailto:"], div#messageBox a[href ^="mailto:"] {
-  color: #80C0A0;
-  background: url(../images/mint/email.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #80C0A0;
+	background: url(../images/mint/email.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="irc://"], div#messageBox a[href ^="irc://"] {
-  color: #80C0A0;
-  background: url(../images/mint/irc.gif)      center right no-repeat;
-  padding-right: 16px;
+	color: #80C0A0;
+	background: url(../images/mint/irc.gif)      center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="http://"]:hover, div#messageBox a[href ^="http://"]:hover {
-  color: #A0E0C0;
-  background: url(../images/mint/external.gif) center right no-repeat;
-  padding-right: 16px;
+	color: #A0E0C0;
+	background: url(../images/mint/external.gif) center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="https://"]:hover, div#messageBox a[href ^="https://"]:hover {
-  color: #A0E0C0;
-  background: url(../images/mint/https.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #A0E0C0;
+	background: url(../images/mint/https.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="mailto:"]:hover, div#messageBox a[href ^="mailto:"]:hover {
-  color: #A0E0C0;
-  background: url(../images/mint/email.gif)    center right no-repeat;
-  padding-right: 16px;
+	color: #A0E0C0;
+	background: url(../images/mint/email.gif)    center right no-repeat;
+	padding-right: 16px;
 }
 
 div.contentDiv a[href ^="irc://"]:hover, div#messageBox a[href ^="irc://"]:hover {
-  color: #A0E0C0;
-  background: url(../images/mint/irc.gif)      center right no-repeat;
-  padding-right: 16px;
+	color: #A0E0C0;
+	background: url(../images/mint/irc.gif)      center right no-repeat;
+	padding-right: 16px;
 }
 
 /* Wikilinks to pages that don't exist */
 div.contentDiv a.wikilink-nonexistent {
-  color: #B02050;
+	color: #B02050;
 }
 
 div.contentDiv a.wikilink-nonexistent:hover {
-  color: #D03060;
+	color: #D03060;
 }
 
 /* Well, not Midget and not comments (usually), but that's what the class is called ;-). Basically an informational window or used as a wrapper for tables. */
 .mdg-comment, .mdg-infobox {
-  margin-left: 1em;
-  padding: 7px;
-  border: 1px solid #AAAAAA;
-  background-color: #E8E8E8;
+	margin-left: 1em;
+	padding: 7px;
+	border: 1px solid #AAAAAA;
+	background-color: #E8E8E8;
 }
 
 .tblholder {
-  margin: 10px 0 0 0;
-  padding: 0;
-  border: 1px solid #AAAAAA;
-  background-color: #E8E8E8;
+	margin: 10px 0 0 0;
+	padding: 0;
+	border: 1px solid #AAAAAA;
+	background-color: #E8E8E8;
 }
 
 /* The beautiful tables inside what may not obviously be mdg-comment divs */
 div.tblholder td.row1 {
-  padding: 4px;
-  background-color: #E0E0E0;
+	padding: 4px;
+	background-color: #E0E0E0;
 }
 
 div.tblholder td.row2 {
-  padding: 4px;
-  background-color: #F0F0F0;
+	padding: 4px;
+	background-color: #F0F0F0;
 }
 
 div.tblholder td.row3 {
-  padding: 4px;
-  background-color: #E8E8E8;
+	padding: 4px;
+	background-color: #E8E8E8;
 }
 
 div.tblholder th {
-  padding: 4px;
-  background-color: #70A080;
-  font-weight: bold;
-  text-align: center;
-  color: #FFFFFF;
+	padding: 4px;
+	background-color: #70A080;
+	font-weight: bold;
+	text-align: center;
+	color: #FFFFFF;
 }
 
 div.tblholder th.subhead {
-  padding: 4px;
-  background-color: #90B0A0;
-  font-weight: bold;
-  text-align: center;
-  color: #FFFFFF;
+	padding: 4px;
+	background-color: #90B0A0;
+	font-weight: bold;
+	text-align: center;
+	color: #FFFFFF;
 }
 
 div.tblholder table {
-  background-color: #FFFFFF;
-  width: 100%;
+	background-color: #FFFFFF;
+	width: 100%;
 }
 
 /* Colored table cells */
 div.tblholder td.row1_red {
-  padding: 4px;
-  background-color: #F8E0E0;
+	padding: 4px;
+	background-color: #F8E0E0;
 }
 
 div.tblholder td.row2_red {
-  padding: 4px;
-  background-color: #FFF0F0;
+	padding: 4px;
+	background-color: #FFF0F0;
 }
 
 div.tblholder td.row3_red {
-  padding: 4px;
-  background-color: #FFE8E8;
+	padding: 4px;
+	background-color: #FFE8E8;
 }
 
 div.tblholder td.row1_green {
-  padding: 4px;
-  background-color: #E0E0F8;
+	padding: 4px;
+	background-color: #E0E0F8;
 }
 
 div.tblholder td.row2_green {
-  padding: 4px;
-  background-color: #F0F0FF;
+	padding: 4px;
+	background-color: #F0F0FF;
 }
 
 div.tblholder td.row3_green {
-  padding: 4px;
-  background-color: #E8E8FF;
+	padding: 4px;
+	background-color: #E8E8FF;
 }
 
 div.tblholder th a {
-  color: #FFFFFF !important;
-  text-decoration: underline !important;
+	color: #FFFFFF !important;
+	text-decoration: underline !important;
 }
 
 div.tblholder th a:hover {
-  color: #FFFF00 !important;
-  text-decoration: underline !important;
+	color: #FFFF00 !important;
+	text-decoration: underline !important;
 }
 
 /*
@@ -392,95 +392,95 @@
  */
 
 div.menu, div.menu_nojs {
-  background-color: #B0F0D0;
-  font-size: 7pt;
-  border-width: 0;
+	background-color: #B0F0D0;
+	font-size: 7pt;
+	border-width: 0;
 }
 .menu_bg {
-  background-color: #B0F0D0;
+	background-color: #B0F0D0;
 }
 div.menu a, div.menu div.label {
-  padding: 2.5pt 5px;
-  margin-right: 3px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  color: #408060;
+	padding: 2.5pt 5px;
+	margin-right: 3px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	color: #408060;
 }
 div.menu_nojs a, div.menu_nojs div.label {
-  padding: 2.5pt 5px;
-  margin-right: 3px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  color: #408060;
+	padding: 2.5pt 5px;
+	margin-right: 3px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	color: #408060;
 }
 div.menu div.label, div.menu_nojs div.label {
-  color: #002010;
-  cursor: default;
+	color: #002010;
+	cursor: default;
 }
 div.menu span.sep, div.menu_nojs span.sep {
-  display: block;
-  float: left;
-  width: 5px;
+	display: block;
+	float: left;
+	width: 5px;
 }
 div.menu div.multopts, div.menu_nojs div.multopts {
-  line-height: 17pt;
+	line-height: 17pt;
 }
 div.menu div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts a, div.menu_nojs div.multopts div.label {
-  float: none;
-  display: inline;
+	float: none;
+	display: inline;
 }
 div.menu a.liteselected, div.menu a.liteselected:hover, div.menu a:hover, div.menu_nojs a.liteselected, div.menu_nojs a.liteselected:hover, div.menu_nojs a:hover {
-  color: #408060;
-  background-color: #D0FFF0;
+	color: #408060;
+	background-color: #D0FFF0;
 }
 div.menu input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="text"], div.menu_nojs input[type ^="password"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 2px 5px 3px 5px;
-  max-width: 70px;
-  background-color: #D0FFF0;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 2px 5px 3px 5px;
+	max-width: 70px;
+	background-color: #D0FFF0;
 }
 div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu_nojs input[type ^="password"]:hover {
-  background-color: #E0FFF0;
+	background-color: #E0FFF0;
 }
 div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu_nojs input[type ^="password"]:focus {
-  background-color: #F0FFF0;
+	background-color: #F0FFF0;
 }
 div.menu input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="button"], div.menu_nojs input[type ^="submit"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 3px 5px;
-  max-width: 70px;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 3px 5px;
+	max-width: 70px;
 }
 div.menu a.current, div.menu a.current:hover, div.menu a.selected, div.menu a.selected:hover, div.menu_nojs a.current, div.menu_nojs a.current:hover, div.menu_nojs a.selected, div.menu_nojs a.selected:hover {
-  color: #004000;
-  background-color: #FFFFFF;
+	color: #004000;
+	background-color: #FFFFFF;
 }
 div.menu ul, div.menu_nojs ul {
-  display: none;
-  position: absolute;
-  padding: 0;
-  margin: 0 !important;
-  background-color: #B0F0D0;
-  border-width: 0;
-  min-width: 120px;
+	display: none;
+	position: absolute;
+	padding: 0;
+	margin: 0 !important;
+	background-color: #B0F0D0;
+	border-width: 0;
+	min-width: 120px;
 }
 div.menu ul li, div.menu_nojs ul li {
-  list-style: none;
+	list-style: none;
 }
 div.menu ul a, div.menu_nojs ul a {
-  float: none;
-  margin: 0;
+	float: none;
+	margin: 0;
 }
 span.menuclear {
-  font-size: 1px;
-  height: 0px;
-  width: 0px;
-  clear: left;
-  line-height: 0px;
-  display: block;
+	font-size: 1px;
+	height: 0px;
+	width: 0px;
+	clear: left;
+	line-height: 0px;
+	display: block;
 }
 
 /* Rounded corners on nearly everything */
@@ -509,290 +509,290 @@
 
 /* Buttons and textboxes - these settings are used almost everywhere */
 input, textarea, select, button {
-  border: 1px solid #408060;
-  background-color: #F2F2F2;
-  padding: 3px;
-  font-family: arial, helvetica, sans-serif;
-  font-size: 8pt;
+	border: 1px solid #408060;
+	background-color: #F2F2F2;
+	padding: 3px;
+	font-family: arial, helvetica, sans-serif;
+	font-size: 8pt;
 }
 
 input:hover, textarea:hover, select:hover {
-  border: 1px solid #60A080;
-  background-color: #F8F8F8;
-  padding: 3px;
+	border: 1px solid #60A080;
+	background-color: #F8F8F8;
+	padding: 3px;
 }
 
 input:focus, textarea:focus, select:focus {
-  border: 1px solid #90D0B0;
-  background-color: #FFFFFF;
-  padding: 3px;
+	border: 1px solid #90D0B0;
+	background-color: #FFFFFF;
+	padding: 3px;
 }
 
 input.ac_loading {
-  background-image: url(../../../images/loading.gif);
-  background-position: right center;
-  background-repeat: no-repeat;
+	background-image: url(../../../images/loading.gif);
+	background-position: right center;
+	background-repeat: no-repeat;
 }
 
 label {
-  padding: 3px;
-  cursor: pointer;
-  font-family: arial, helvetica, sans-serif;
-  font-size: 8pt;
+	padding: 3px;
+	cursor: pointer;
+	font-family: arial, helvetica, sans-serif;
+	font-size: 8pt;
 }
 
 label:hover {
-  padding: 3px;
-  cursor: pointer;
-  background-color: #F0F0F0;
+	padding: 3px;
+	cursor: pointer;
+	background-color: #F0F0F0;
 }
 
 input#pageheading {
-  font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
-  font-size: 18pt;
-  font-weight: bold;
-  border-width: 0 0 1px 0;
-  width: 100%;
-  border-bottom: 1px solid #90D0B0;
-  margin: 0;
-  padding: 0;
+	font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
+	font-size: 18pt;
+	font-weight: bold;
+	border-width: 0 0 1px 0;
+	width: 100%;
+	border-bottom: 1px solid #90D0B0;
+	margin: 0;
+	padding: 0;
 }
 
 input#pageheading:focus {
-  background-color: #fafafa;
+	background-color: #fafafa;
 }
 
 input[type ^="button"], input[type ^="submit"], button {
-  background-image: url(../images/mint/sprite-horiz.gif);
-  background-position: 0% -60px;
-  background-repeat: repeat-x;
-  color: #202020;
+	background-image: url(../images/mint/sprite-horiz.gif);
+	background-position: 0% -60px;
+	background-repeat: repeat-x;
+	color: #202020;
 }
 
 input[type ^="image"][disabled ^="disabled"] {
-  opacity: 0.5;
-  filter: alpha(opacity=50);
+	opacity: 0.5;
+	filter: alpha(opacity=50);
 }
 
 input[type ^="button"][disabled ^="disabled"], input[type ^="submit"][disabled ^="disabled"], button[disabled ^="disabled"], .btn-disabled {
-  color: #808080 !important;
-  background-image: none !important;
-  background-color: #e0e0e0 !important;
+	color: #808080 !important;
+	background-image: none !important;
+	background-color: #e0e0e0 !important;
 }
 
 /* The Wordpress-like fills behind checkboxes and their labels */
 .catCheck {
-  padding: 3px;
+	padding: 3px;
 }
 
 .catCheck:hover {
-  padding: 3px;
-  background-color: #F0F0F0;
+	padding: 3px;
+	background-color: #F0F0F0;
 }
 
 /* Information, warning, question, error, and wait boxes */
 div.error-box {
-  background-image: url(../../../images/error.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #FFF4F4;
-  border: 1px dashed #408060;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/error.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #FFF4F4;
+	border: 1px dashed #408060;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 div.info-box {
-  background-image: url(../../../images/info.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #F4FFF4;
-  border: 1px dashed #408060;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/info.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #F4FFF4;
+	border: 1px dashed #408060;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 div.warning-box {
-  background-image: url(../../../images/warning.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #FFF4FF;
-  border: 1px dashed #408060;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/warning.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #FFF4FF;
+	border: 1px dashed #408060;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 div.question-box {
-  background-image: url(../../../images/question.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #F4F4FF;
-  border: 1px dashed #408060;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/question.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #F4F4FF;
+	border: 1px dashed #408060;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 div.wait-box {
-  background-image: url(../../../images/wait.png);
-  background-position: 8px 8px;
-  background-repeat: no-repeat;
-  background-color: #FFFFF4;
-  border: 1px dashed #408060;
-  padding: 10px 10px 10px 50px;
-  margin: 0.5em 0 0 0;
-  min-height: 25px;
+	background-image: url(../../../images/wait.png);
+	background-position: 8px 8px;
+	background-repeat: no-repeat;
+	background-color: #FFFFF4;
+	border: 1px dashed #408060;
+	padding: 10px 10px 10px 50px;
+	margin: 0.5em 0 0 0;
+	min-height: 25px;
 }
 
 /* This stuff is mostly unused, left in for compatibility */
 div#ajaxEditContainer table {
-  border: 0px solid #FFFFFF;
+	border: 0px solid #FFFFFF;
 }
 
 div#ajaxEditContainer td {
-  margin: 1px;
+	margin: 1px;
 }
 
 div#ajaxEditContainer pre {
-  margin-left: 1em;
-  background-color: #F8F8F8;
-  border: 1px dashed #90D0B0;
-  padding: 10px;
-  overflow: auto;
-  max-height: 150px;
+	margin-left: 1em;
+	background-color: #F8F8F8;
+	border: 1px dashed #90D0B0;
+	padding: 10px;
+	overflow: auto;
+	max-height: 150px;
 }
 
 /* toolbar */
 div.toolbar {
-  border: 1px solid #3b9c61;
-  background-color: #D0D0D0;
-  background-image: url(../images/mint/sprite-horiz.gif);
-  background-position: 0 -90px;
-  padding: 1px 0;
-  height: 22px;
-  font-family: arial, sans-serif;
-  font-size: 8pt;
+	border: 1px solid #3b9c61;
+	background-color: #D0D0D0;
+	background-image: url(../images/mint/sprite-horiz.gif);
+	background-position: 0 -90px;
+	padding: 1px 0;
+	height: 22px;
+	font-family: arial, sans-serif;
+	font-size: 8pt;
 }
 div.toolbar ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar ul li {
-  list-style: none;
-  margin: 0;
-  float: left;
+	list-style: none;
+	margin: 0;
+	float: left;
 }
 div.toolbar a img {
-  opacity: 0.6;
+	opacity: 0.6;
 }
 div.toolbar a:hover img, div.toolbar a:focus img {
-  opacity: 1;
+	opacity: 1;
 }
 div.toolbar a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar a:hover, div.toolbar a:focus {
-  border: 1px solid #008000;
-  background-color: #ceedce;
-  background-image: url(../images/mint/sprite-horiz.gif);
-  background-position: 0 -118px;
-  color: #000000;
-  text-decoration: none;
+	border: 1px solid #008000;
+	background-color: #ceedce;
+	background-image: url(../images/mint/sprite-horiz.gif);
+	background-position: 0 -118px;
+	color: #000000;
+	text-decoration: none;
 }
 div.toolbar a:active {
-  background-color: #E0E0E0;
-  background-position: 0 -138px;
+	background-color: #E0E0E0;
+	background-position: 0 -138px;
 }
 div.toolbar img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar a span {
-  position: relative;
-  top: -3px !important;
+	position: relative;
+	top: -3px !important;
 }
 div.toolbar a span.noimage {
-  position: relative;
-  top: 0px !important;
-  height: 16px !important;
-  display: block;
-  padding-left: 2px !important;
+	position: relative;
+	top: 0px !important;
+	height: 16px !important;
+	display: block;
+	padding-left: 2px !important;
 }
 div.toolbar li span {
-  padding-left: 4px;
-  padding-right: 2px;
-  position: relative;
-  top: 4px;
+	padding-left: 4px;
+	padding-right: 2px;
+	position: relative;
+	top: 4px;
 }
 
 /* vertical toolbar */
 div.toolbar_vert {
-  border: 1px solid #909090;
-  background-color: #D0D0D0;
-  padding: 2px 0;
+	border: 1px solid #909090;
+	background-color: #D0D0D0;
+	padding: 2px 0;
 }
 div.toolbar_vert ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar_vert ul li {
-  list-style: none;
-  margin: 0;
+	list-style: none;
+	margin: 0;
 }
 div.toolbar_vert a img {
-  opacity: 0.6;
-  /*filter: alpha(opacity=60);*/
+	opacity: 0.6;
+	/*filter: alpha(opacity=60);*/
 }
 div.toolbar_vert a:hover img {
-  opacity: 1;
-  /*filter: alpha(opacity=100);*/
+	opacity: 1;
+	/*filter: alpha(opacity=100);*/
 }
 div.toolbar_vert a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar_vert a:hover {
-  border: 1px solid #209020;
-  background-color: #ceedce;
-  color: #000000;
-  text-decoration: none;
+	border: 1px solid #209020;
+	background-color: #ceedce;
+	color: #000000;
+	text-decoration: none;
 }
 div.toolbar_vert a:active {
-  border: 1px solid #A0A0A0;
-  background-color: #E0E0E0;
+	border: 1px solid #A0A0A0;
+	background-color: #E0E0E0;
 }
 div.toolbar_vert img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar_vert a span {
-  position: relative;
-  top: -4px;
+	position: relative;
+	top: -4px;
 }
 div.toolbar_vert li span {
-  padding-left: 2px;
-  padding-right: 5px;
+	padding-left: 2px;
+	padding-right: 5px;
 }
 
 /*
@@ -800,24 +800,24 @@
  */
 
 ul.userpage_links li {
-  background-image: url('../images/buttonbg.gif');
-  background-repeat: repeat-x;
+	background-image: url('../images/buttonbg.gif');
+	background-repeat: repeat-x;
 }
 
 ul.userpage_links li a {
-  color: #202020;
+	color: #202020;
 }
 
 ul.userpage_links li.userpage_tab_active {
-  background-image: url('../images/buttonbg-lite.gif');
+	background-image: url('../images/buttonbg-lite.gif');
 }
 
 ul.userpage_links li:hover {
-  background-image: url('../images/buttonbg-lite.gif');
-  border-color: #404040 #404040 #ffffff #404040;
-  border-bottom-width: 0;
+	background-image: url('../images/buttonbg-lite.gif');
+	border-color: #404040 #404040 #ffffff #404040;
+	border-bottom-width: 0;
 }
 
 ul.userpage_links li.userpage_tab_active:hover {
-  border-bottom-width: 1px;
+	border-bottom-width: 1px;
 }
--- a/themes/oxygen/elements.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/elements.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -15,50 +15,50 @@
 <!-- VAR sidebar_heading --><div class="heading">{TEXT}</div>
 <!-- ENDVAR sidebar_heading -->
 <!-- VAR sidebar_top -->
-          <div class="recttop">
-            <table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
-              <tr>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: 0 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
-                <td style="margin: 0; padding: 0; height: 12px;" class="recttoptop" onclick="var id = this.parentNode.parentNode.parentNode.parentNode.parentNode.id; var side = id.substr(0, id.indexOf('-')); collapseSidebar(side);"></td>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -12px 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
-              </tr>
-            </table>
-          </div>
-          <div class="sidebar">
+					<div class="recttop">
+						<table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
+							<tr>
+								<td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: 0 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
+								<td style="margin: 0; padding: 0; height: 12px;" class="recttoptop" onclick="var id = this.parentNode.parentNode.parentNode.parentNode.parentNode.id; var side = id.substr(0, id.indexOf('-')); collapseSidebar(side);"></td>
+								<td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -12px 0; background-repeat: no-repeat;" width="12" height="12" /> </td>
+							</tr>
+						</table>
+					</div>
+					<div class="sidebar">
 <!-- ENDVAR sidebar_top -->
 <!-- VAR sidebar_section -->
-            <div class="slider">
-              <div class="heading">
-                <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-                <br style="display: none;" /><br style="display: none;" />
-                <a class="head" href="#">{TITLE}</a>
-                <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-                <br style="display: none;" /><br style="display: none;" />
-              </div>
-              <div class="slideblock">{CONTENT}</div>
-            </div>
+						<div class="slider">
+							<div class="heading">
+								<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+								<br style="display: none;" /><br style="display: none;" />
+								<a class="head" href="#">{TITLE}</a>
+								<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+								<br style="display: none;" /><br style="display: none;" />
+							</div>
+							<div class="slideblock">{CONTENT}</div>
+						</div>
 <!-- ENDVAR sidebar_section -->
 <!-- VAR sidebar_section_raw -->
-            <div class="slider">
-              <div class="heading">
-                <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-                <br style="display: none;" /><br style="display: none;" />
-                <a class="head" href="#">{TITLE}</a>
-                <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-                <br style="display: none;" /><br style="display: none;" />
-              </div>
-              <div class="slideblock2">{CONTENT}</div>
-            </div>
+						<div class="slider">
+							<div class="heading">
+								<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+								<br style="display: none;" /><br style="display: none;" />
+								<a class="head" href="#">{TITLE}</a>
+								<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+								<br style="display: none;" /><br style="display: none;" />
+							</div>
+							<div class="slideblock2">{CONTENT}</div>
+						</div>
 <!-- ENDVAR sidebar_section_raw -->
 <!-- VAR sidebar_bottom -->
-          </div>
-          <div class="rectbot">
-            <table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
-              <tr>
-                <td style="margin: 0; padding: 0;"><img                    alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -24px 0; background-repeat: no-repeat;" width="12" height="12" /></td>
-                <td style="margin: 0; padding: 0;" class="rectbottop"><img alt=" " src="{CDNPATH}/images/spacer.gif" width="12" height="12" /></td>
-                <td style="margin: 0; padding: 0;"><img                    alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -36px 0; background-repeat: no-repeat;" width="12" height="12" /></td>
-              </tr>
-            </table>
-          </div>
+					</div>
+					<div class="rectbot">
+						<table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
+							<tr>
+								<td style="margin: 0; padding: 0;"><img                    alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -24px 0; background-repeat: no-repeat;" width="12" height="12" /></td>
+								<td style="margin: 0; padding: 0;" class="rectbottop"><img alt=" " src="{CDNPATH}/images/spacer.gif" width="12" height="12" /></td>
+								<td style="margin: 0; padding: 0;"><img                    alt=" " src="{CDNPATH}/images/spacer.gif" style="background-image: url({CDNPATH}/themes/oxygen/images/{STYLE_ID}/sprite-horiz.gif); background-position: -36px 0; background-repeat: no-repeat;" width="12" height="12" /></td>
+							</tr>
+						</table>
+					</div>
 <!-- ENDVAR sidebar_bottom -->
--- a/themes/oxygen/footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,57 +1,57 @@
-              </div>
-              <div id="mdgCommentContainer">
-              </div>
-            </div></div>
-          </td><td id="mdg-mr"></td></tr>
-          <tr><td id="mdg-btl"></td><td>
-            <!-- We strongly request that you leave the notice below in its place; it helps to attract users to Enano in exchange for providing you
-                 with your CMS. Enano is still new; therefore we are looking to attract users, and we feel that this notice will help. If you refuse
-                 to include even this tiny little notice, support on the Enano forums may be affected. Thanks guys.
-                 
-                 -Dan
-                 -->
-            <div id="credits">
-              <b>{COPYRIGHT}</b>
-              <!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
-              <br />[[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<!-- BEGINNOT stupid_mode --><a href="http://validator.w3.org/check?uri=referer">{lang:page_w3c_valid_xhtml11}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">{lang:page_w3c_valid_css}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<!-- END stupid_mode -->[[StatsLong]]
-              <!-- Do not remove this line or scheduled tasks will not run. -->
-              <img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
-            </div>
-          
-          </td><td id="mdg-btr"></td></tr>
-          <tr><td id="mdg-btcl"></td><td id="mdg-btm"></td><td id="mdg-btcr"></td></tr>
-          </table>
-            
-        </td>
-        
-        <!-- BEGIN sidebar_right -->
-        <!-- BEGINNOT in_admin -->
-        <td class="mdgSidebarHolder" valign="top">
-          <div id="right-sidebar">
-            
-            {SIDEBAR_RIGHT}
-              
-          </div>
-          <div id="right-sidebar-showbutton" style="display: none; position: fixed; top: 3px; right: 3px;">
-            <input type="button" onclick="collapseSidebar('right');" value="&lt;&lt;" />
-          </div>
-        </td>
-        <!-- END in_admin -->
-        <!-- END sidebar_right -->
-      
-      </tr>
-    </table>
-    <div style="display: none;">
-    <h2>Your browser does not support CSS.</h2>
-     <p>If you can see this text, it means that your browser does not support Cascading Style Sheets (CSS). CSS is a fundemental aspect of XHTML, and as a result it is becoming very widely adopted by websites, including this one. You should consider switching to a more modern web browser, such as Mozilla Firefox or Opera 9.</p>
-     <p>Because of this, there are a few minor issues that you may experience while browsing this site, not the least of which is some visual elements below that would normally be hidden in most browsers. Please excuse these minor inconveniences.</p>
-    </div>
-    <div id="root3" class="jswindow" style="display: none;">
-      <div id="tb3" class="titlebar">Wiki formatting help</div>
-      <div class="content" id="cn3">
-        Loading...
-      </div>
-    </div>
-    {JS_FOOTER}
-  </body>
+							</div>
+							<div id="mdgCommentContainer">
+							</div>
+						</div></div>
+					</td><td id="mdg-mr"></td></tr>
+					<tr><td id="mdg-btl"></td><td>
+						<!-- We strongly request that you leave the notice below in its place; it helps to attract users to Enano in exchange for providing you
+ 								with your CMS. Enano is still new; therefore we are looking to attract users, and we feel that this notice will help. If you refuse
+ 								to include even this tiny little notice, support on the Enano forums may be affected. Thanks guys.
+ 								
+ 								-Dan
+ 								-->
+						<div id="credits">
+							<b>{COPYRIGHT}</b>
+							<!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
+							<br />[[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<!-- BEGINNOT stupid_mode --><a href="http://validator.w3.org/check?uri=referer">{lang:page_w3c_valid_xhtml11}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">{lang:page_w3c_valid_css}</a>&nbsp;&nbsp;|&nbsp;&nbsp;<!-- END stupid_mode -->[[StatsLong]]
+							<!-- Do not remove this line or scheduled tasks will not run. -->
+							<img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
+						</div>
+					
+					</td><td id="mdg-btr"></td></tr>
+					<tr><td id="mdg-btcl"></td><td id="mdg-btm"></td><td id="mdg-btcr"></td></tr>
+					</table>
+						
+				</td>
+				
+				<!-- BEGIN sidebar_right -->
+				<!-- BEGINNOT in_admin -->
+				<td class="mdgSidebarHolder" valign="top">
+					<div id="right-sidebar">
+						
+						{SIDEBAR_RIGHT}
+							
+					</div>
+					<div id="right-sidebar-showbutton" style="display: none; position: fixed; top: 3px; right: 3px;">
+						<input type="button" onclick="collapseSidebar('right');" value="&lt;&lt;" />
+					</div>
+				</td>
+				<!-- END in_admin -->
+				<!-- END sidebar_right -->
+			
+			</tr>
+		</table>
+		<div style="display: none;">
+		<h2>Your browser does not support CSS.</h2>
+ 		<p>If you can see this text, it means that your browser does not support Cascading Style Sheets (CSS). CSS is a fundemental aspect of XHTML, and as a result it is becoming very widely adopted by websites, including this one. You should consider switching to a more modern web browser, such as Mozilla Firefox or Opera 9.</p>
+ 		<p>Because of this, there are a few minor issues that you may experience while browsing this site, not the least of which is some visual elements below that would normally be hidden in most browsers. Please excuse these minor inconveniences.</p>
+		</div>
+		<div id="root3" class="jswindow" style="display: none;">
+			<div id="tb3" class="titlebar">Wiki formatting help</div>
+			<div class="content" id="cn3">
+				Loading...
+			</div>
+		</div>
+		{JS_FOOTER}
+	</body>
 </html>
--- a/themes/oxygen/header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,209 +1,209 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css?{ENANO_VERSION}" />
-    <!-- BEGIN msie -->
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared-ie.css?{ENANO_VERSION}" />
-    <!-- END msie -->
-    <link id="mdgCss" rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css?{ENANO_VERSION}" type="text/css" />
-    {JS_DYNAMIC_VARS}
-    {JS_HEADER}
-    
-    <script type="text/javascript">
-      var tinymce_skin = 'o2k7';
-    </script>
-    
-    {ADDITIONAL_HEADERS}
-    
-    <script type="text/javascript">
-    // <![CDATA[
-    
-      function collapseSidebar(side)
-      {
-        elem = document.getElementById(side+'-sidebar');
-        if(!elem) return;
-        counter = document.getElementById(side+'-sidebar-showbutton');
-        if(elem.style.display=='none')
-        {
-          elem.style.display = 'block';
-          counter.style.display = 'none';
-          elem.parentNode.style.width = '';
-          if ( !KILL_SWITCH )
-          {
-            createCookie(side+'_sidebar', 'open', 365);
-          }
-        } else {
-          elem.style.display = 'none';
-          counter.style.display = 'block';
-          elem.parentNode.style.width = '25px';
-          if ( !KILL_SWITCH )
-          {
-            createCookie(side+'_sidebar', 'collapsed', 365);
-          }
-        }
-      }
-      
-      /*
-      window.onload = function() {
-        if(typeof readCookie == 'function')
-        {
-          if(readCookie('left_sidebar') =='collapsed') collapseSidebar('left');
-          if(readCookie('right_sidebar')=='collapsed') collapseSidebar('right');
-        }
-        if(typeof mdgInnerLoader == 'function')
-          mdgInnerLoader();
-      }
-      */
-      
-      if ( typeof(KILL_SWITCH) != 'undefined' )
-      {
-        if ( !KILL_SWITCH )
-        {
-          var oxygenSidebarSetup = function() {
-              if(typeof readCookie == 'function')
-              {
-                if(readCookie('left_sidebar') =='collapsed') collapseSidebar('left');
-                if(readCookie('right_sidebar')=='collapsed') collapseSidebar('right');
-              }
-            };
-          addOnloadHook(oxygenSidebarSetup);
-        }
-      }
-      
-      function ajaxRenameInline()
-      {
-        if ( KILL_SWITCH || IE )
-          return false;
-        // This trick is _so_ vBulletin...
-        elem = document.getElementById('h2PageName');
-        if(!elem) return;
-        elem.style.display = 'none';
-        name = elem.firstChild.nodeValue;
-        textbox = document.createElement('input');
-        textbox.type = 'text';
-        textbox.value = name;
-        textbox.id = 'pageheading';
-        textbox.size = name.length + 7;
-        textbox.onkeyup = function(e) { if(!e) return; if(e.keyCode == 13) ajaxRenameInlineSave(); if(e.keyCode == 27) ajaxRenameInlineCancel(); };
-        textbox.oldname = name;
-        elem.parentNode.insertBefore(textbox, elem);
-        document.onclick = ajaxRenameInlineCancel;
-        
-        load_component(['l10n', 'fadefilter', 'messagebox']);
-        textbox.focus();
-        textbox.select();
-      }
-      function ajaxRenameInlineSave()
-      {
-        elem1 = document.getElementById('h2PageName');
-        elem2 = document.getElementById('pageheading');
-        if(!elem1 || !elem2) return;
-        value = elem2.value;
-        elem2.parentNode.removeChild(elem2); // just destroy the thing
-        elem1.removeChild(elem1.firstChild);
-        elem1.appendChild(document.createTextNode(value));
-        elem1.style.display = 'block';
-        if(!value || value=='' || value==elem2.oldname) return;
-        setAjaxLoading();
-        ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+ajaxEscape(value), function() {
-          if ( ajax.readyState == 4 )
-          {
-            unsetAjaxLoading();
-            var response = String(ajax.responseText);
-            if ( !check_json_response(response) )
-            {
-              handle_invalid_json(response);
-              return false;
-            }
-            response = parseJSON(response);
-            if ( response.success )
-            {
-              new MessageBox( MB_OK|MB_ICONINFORMATION, $lang.get('ajax_rename_success_title'), $lang.get('ajax_rename_success_body', { page_name_new: value }) );
-            }
-            else
-            {
-              alert(response.error);
-            }
-          }
-        });
-      }
-      function ajaxRenameInlineCancel(e)
-      {
-        if ( typeof(e) != 'object' && IE )
-          e = window.event;
-        elem1 = document.getElementById('h2PageName');
-        elem2 = document.getElementById('pageheading');
-        if(!elem1 || !elem2) return;
-        if ( typeof(e) == 'object' && e.target )
-        {
-          if(e.target == elem2)
-            return;
-        }
-        //value = elem2.value;
-        elem2.parentNode.removeChild(elem2); // just destroy the thing
-        //elem1.innerHTML = value;
-        elem1.style.display = 'block';
-        document.onclick = null;
-      }
-    // ]]>
-    </script>
-    
-  </head>
-  <body>
-    <table border="0" cellspacing="0" cellpadding="3" id="enano-master" width="100%">
-      <tr>
-      <!-- BEGIN sidebar_left -->
-      <td class="mdgSidebarHolder" valign="top">
-        <div id="left-sidebar">
-          {SIDEBAR_LEFT}
-        </div>
-        <div id="left-sidebar-showbutton" style="display: none; position: fixed; top: 3px; left: 3px;">
-          <input type="button" onclick="collapseSidebar('left');" value="&gt;&gt;" />
-        </div>
-      </td>
-      <!-- END sidebar_left -->
-      <td valign="top">
-        <table border="0" width="100%" cellspacing="0" cellpadding="0">
-      
-        <tr><td id="mdg-tl"></td><td id="mdg-top"></td><td id="mdg-tr"></td></tr>
-                                                                                  
-        <tr><td id="mdg-l"></td><td>
-        <table border="0" width="100%" id="title" cellspacing="0" cellpadding="0">
-            <tr>
-              <td id="mainhead">
-                <h2><a href="{SCRIPTPATH}/{ADMIN_SID_QUES}">{SITE_NAME}</a></h2>
-                <h4>{SITE_DESC}</h4>
-              </td>
-            </tr>            
-          </table>
-        </td><td id="mdg-r"></td></tr>
-        
-        <tr><td id="mdg-brl"></td><td style="background-color: #FFFFFF;"></td><td id="mdg-brr"></td></tr>
-        
-        <tr>
-          <td id="mdg-bl"></td>
-          <td class="menu_bg">
-          <div class="menu_nojs" id="pagebar_main">
-            <div class="label">
-              <!-- BEGIN stupid_mode -->
-              Page tools
-              <!-- BEGINELSE stupid_mode -->
-              {lang:onpage_lbl_pagetools}
-              <!-- END stupid_mode -->
-            </div>
-            {TOOLBAR}
-            <ul>
-              {TOOLBAR_EXTRAS}
-            </ul>
-            <span class="menuclear"></span>
-          </div>
-        </td><td id="mdg-br"></td></tr>
-        <tr><td id="mdg-ml"></td><td style="background-color: #FFFFFF;">
-          <div class="pad"><div class="contentDiv">
-          <div style="float: right;">
-            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
-          </div>
-          <h1 <!-- BEGIN auth_rename --> ondblclick="ajaxRenameInline();" title="{lang:onpage_btn_rename_inline}" <!-- END auth_rename --> id="h2PageName">{PAGE_NAME}</h1>
-            <div id="ajaxEditContainer">
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css?{ENANO_VERSION}" />
+		<!-- BEGIN msie -->
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared-ie.css?{ENANO_VERSION}" />
+		<!-- END msie -->
+		<link id="mdgCss" rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css?{ENANO_VERSION}" type="text/css" />
+		{JS_DYNAMIC_VARS}
+		{JS_HEADER}
+		
+		<script type="text/javascript">
+			var tinymce_skin = 'o2k7';
+		</script>
+		
+		{ADDITIONAL_HEADERS}
+		
+		<script type="text/javascript">
+		// <![CDATA[
+		
+			function collapseSidebar(side)
+			{
+				elem = document.getElementById(side+'-sidebar');
+				if(!elem) return;
+				counter = document.getElementById(side+'-sidebar-showbutton');
+				if(elem.style.display=='none')
+				{
+					elem.style.display = 'block';
+					counter.style.display = 'none';
+					elem.parentNode.style.width = '';
+					if ( !KILL_SWITCH )
+					{
+						createCookie(side+'_sidebar', 'open', 365);
+					}
+				} else {
+					elem.style.display = 'none';
+					counter.style.display = 'block';
+					elem.parentNode.style.width = '25px';
+					if ( !KILL_SWITCH )
+					{
+						createCookie(side+'_sidebar', 'collapsed', 365);
+					}
+				}
+			}
+			
+			/*
+			window.onload = function() {
+				if(typeof readCookie == 'function')
+				{
+					if(readCookie('left_sidebar') =='collapsed') collapseSidebar('left');
+					if(readCookie('right_sidebar')=='collapsed') collapseSidebar('right');
+				}
+				if(typeof mdgInnerLoader == 'function')
+					mdgInnerLoader();
+			}
+			*/
+			
+			if ( typeof(KILL_SWITCH) != 'undefined' )
+			{
+				if ( !KILL_SWITCH )
+				{
+					var oxygenSidebarSetup = function() {
+							if(typeof readCookie == 'function')
+							{
+								if(readCookie('left_sidebar') =='collapsed') collapseSidebar('left');
+								if(readCookie('right_sidebar')=='collapsed') collapseSidebar('right');
+							}
+						};
+					addOnloadHook(oxygenSidebarSetup);
+				}
+			}
+			
+			function ajaxRenameInline()
+			{
+				if ( KILL_SWITCH || IE )
+					return false;
+				// This trick is _so_ vBulletin...
+				elem = document.getElementById('h2PageName');
+				if(!elem) return;
+				elem.style.display = 'none';
+				name = elem.firstChild.nodeValue;
+				textbox = document.createElement('input');
+				textbox.type = 'text';
+				textbox.value = name;
+				textbox.id = 'pageheading';
+				textbox.size = name.length + 7;
+				textbox.onkeyup = function(e) { if(!e) return; if(e.keyCode == 13) ajaxRenameInlineSave(); if(e.keyCode == 27) ajaxRenameInlineCancel(); };
+				textbox.oldname = name;
+				elem.parentNode.insertBefore(textbox, elem);
+				document.onclick = ajaxRenameInlineCancel;
+				
+				load_component(['l10n', 'fadefilter', 'messagebox']);
+				textbox.focus();
+				textbox.select();
+			}
+			function ajaxRenameInlineSave()
+			{
+				elem1 = document.getElementById('h2PageName');
+				elem2 = document.getElementById('pageheading');
+				if(!elem1 || !elem2) return;
+				value = elem2.value;
+				elem2.parentNode.removeChild(elem2); // just destroy the thing
+				elem1.removeChild(elem1.firstChild);
+				elem1.appendChild(document.createTextNode(value));
+				elem1.style.display = 'block';
+				if(!value || value=='' || value==elem2.oldname) return;
+				setAjaxLoading();
+				ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+ajaxEscape(value), function() {
+					if ( ajax.readyState == 4 )
+					{
+						unsetAjaxLoading();
+						var response = String(ajax.responseText);
+						if ( !check_json_response(response) )
+						{
+							handle_invalid_json(response);
+							return false;
+						}
+						response = parseJSON(response);
+						if ( response.success )
+						{
+							new MessageBox( MB_OK|MB_ICONINFORMATION, $lang.get('ajax_rename_success_title'), $lang.get('ajax_rename_success_body', { page_name_new: value }) );
+						}
+						else
+						{
+							alert(response.error);
+						}
+					}
+				});
+			}
+			function ajaxRenameInlineCancel(e)
+			{
+				if ( typeof(e) != 'object' && IE )
+					e = window.event;
+				elem1 = document.getElementById('h2PageName');
+				elem2 = document.getElementById('pageheading');
+				if(!elem1 || !elem2) return;
+				if ( typeof(e) == 'object' && e.target )
+				{
+					if(e.target == elem2)
+						return;
+				}
+				//value = elem2.value;
+				elem2.parentNode.removeChild(elem2); // just destroy the thing
+				//elem1.innerHTML = value;
+				elem1.style.display = 'block';
+				document.onclick = null;
+			}
+		// ]]>
+		</script>
+		
+	</head>
+	<body>
+		<table border="0" cellspacing="0" cellpadding="3" id="enano-master" width="100%">
+			<tr>
+			<!-- BEGIN sidebar_left -->
+			<td class="mdgSidebarHolder" valign="top">
+				<div id="left-sidebar">
+					{SIDEBAR_LEFT}
+				</div>
+				<div id="left-sidebar-showbutton" style="display: none; position: fixed; top: 3px; left: 3px;">
+					<input type="button" onclick="collapseSidebar('left');" value="&gt;&gt;" />
+				</div>
+			</td>
+			<!-- END sidebar_left -->
+			<td valign="top">
+				<table border="0" width="100%" cellspacing="0" cellpadding="0">
+			
+				<tr><td id="mdg-tl"></td><td id="mdg-top"></td><td id="mdg-tr"></td></tr>
+																																									
+				<tr><td id="mdg-l"></td><td>
+				<table border="0" width="100%" id="title" cellspacing="0" cellpadding="0">
+						<tr>
+							<td id="mainhead">
+								<h2><a href="{SCRIPTPATH}/{ADMIN_SID_QUES}">{SITE_NAME}</a></h2>
+								<h4>{SITE_DESC}</h4>
+							</td>
+						</tr>            
+					</table>
+				</td><td id="mdg-r"></td></tr>
+				
+				<tr><td id="mdg-brl"></td><td style="background-color: #FFFFFF;"></td><td id="mdg-brr"></td></tr>
+				
+				<tr>
+					<td id="mdg-bl"></td>
+					<td class="menu_bg">
+					<div class="menu_nojs" id="pagebar_main">
+						<div class="label">
+							<!-- BEGIN stupid_mode -->
+							Page tools
+							<!-- BEGINELSE stupid_mode -->
+							{lang:onpage_lbl_pagetools}
+							<!-- END stupid_mode -->
+						</div>
+						{TOOLBAR}
+						<ul>
+							{TOOLBAR_EXTRAS}
+						</ul>
+						<span class="menuclear"></span>
+					</div>
+				</td><td id="mdg-br"></td></tr>
+				<tr><td id="mdg-ml"></td><td style="background-color: #FFFFFF;">
+					<div class="pad"><div class="contentDiv">
+					<div style="float: right;">
+						<img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
+					</div>
+					<h1 <!-- BEGIN auth_rename --> ondblclick="ajaxRenameInline();" title="{lang:onpage_btn_rename_inline}" <!-- END auth_rename --> id="h2PageName">{PAGE_NAME}</h1>
+						<div id="ajaxEditContainer">
--- a/themes/oxygen/simple-footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/simple-footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,27 +1,27 @@
 
-            </div>
-          </td>
-          <td id="mdg-mr"></td>
-        </tr>
-        <tr>
-          <td id="mdg-btl"></td>
-          <td>
-            <div id="credits">
-              {COPYRIGHT}
-            </div>
-          </td>
-          <td id="mdg-btr"></td>
-        </tr>
-        <tr>
-          <td id="mdg-btcl"></td>
-          <td id="mdg-btm"></td>
-          <td id="mdg-btcr"></td>
-        </tr>
-      </table>
-    </td>
-    <td style="width: 10%;"></td>
-    </tr>
-    </table>
-  </body>
+						</div>
+					</td>
+					<td id="mdg-mr"></td>
+				</tr>
+				<tr>
+					<td id="mdg-btl"></td>
+					<td>
+						<div id="credits">
+							{COPYRIGHT}
+						</div>
+					</td>
+					<td id="mdg-btr"></td>
+				</tr>
+				<tr>
+					<td id="mdg-btcl"></td>
+					<td id="mdg-btm"></td>
+					<td id="mdg-btcr"></td>
+				</tr>
+			</table>
+		</td>
+		<td style="width: 10%;"></td>
+		</tr>
+		</table>
+	</body>
 </html>
 
--- a/themes/oxygen/simple-header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/simple-header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,33 +1,33 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html>
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" id="mdgCss" />
-    {JS_DYNAMIC_VARS}
-    <!-- This script automatically loads the other 15 JS files -->
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/jsres.php"></script>
-    {ADDITIONAL_HEADERS}
-  </head>
-  <body>
-    <table class="simple-layout" border="0" style="width: 100%; height: 100%;">
-    <tr>
-    <td style="width: 10%;"></td>
-    <td valign="middle">
-      <table id="enano-main" border="0" cellspacing="0" cellpadding="0" style="margin: 0 auto;">
-        <tr>
-          <td id="mdg-tl"></td>
-          <td id="mdg-top"></td>
-          <td id="mdg-tr"></td>
-        </tr>
-        <tr>
-          <td id="mdg-l"></td>
-          <td id="mainhead">
-            <h1>{PAGE_NAME}</h1>
-          </td>
-          <td id="mdg-r"></td>
-        </tr>
-        <tr>
-          <td id="mdg-ml"></td>
-          <td style="background-color: #FFF;">
-            <div id="ajaxEditContainer" class="contentDiv">
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		<link rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" id="mdgCss" />
+		{JS_DYNAMIC_VARS}
+		<!-- This script automatically loads the other 15 JS files -->
+		<script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/jsres.php"></script>
+		{ADDITIONAL_HEADERS}
+	</head>
+	<body>
+		<table class="simple-layout" border="0" style="width: 100%; height: 100%;">
+		<tr>
+		<td style="width: 10%;"></td>
+		<td valign="middle">
+			<table id="enano-main" border="0" cellspacing="0" cellpadding="0" style="margin: 0 auto;">
+				<tr>
+					<td id="mdg-tl"></td>
+					<td id="mdg-top"></td>
+					<td id="mdg-tr"></td>
+				</tr>
+				<tr>
+					<td id="mdg-l"></td>
+					<td id="mainhead">
+						<h1>{PAGE_NAME}</h1>
+					</td>
+					<td id="mdg-r"></td>
+				</tr>
+				<tr>
+					<td id="mdg-ml"></td>
+					<td style="background-color: #FFF;">
+						<div id="ajaxEditContainer" class="contentDiv">
--- a/themes/oxygen/toolbar.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/oxygen/toolbar.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,62 +1,62 @@
 <!-- Stuff related to toolbars and clickable buttons.
-     The plan was to use this on the toolbar for most pages. Never made it into the release,
-     but still provided as an otherwise-unused component for plugins to make use of.
-     -->
+ 		The plan was to use this on the toolbar for most pages. Never made it into the release,
+ 		but still provided as an otherwise-unused component for plugins to make use of.
+ 		-->
 
 <!-- VAR toolbar_start -->
-  <div class="toolbar">
-  <ul>
+	<div class="toolbar">
+	<ul>
 <!-- ENDVAR toolbar_start -->
 <!-- VAR toolbar_button -->
-  <li>
-    <a title="{TITLE}" {FLAGS}>
-      <!-- IFSET SPRITE -->
-        {SPRITE}
-      <!-- BEGINELSE SPRITE -->
-        <!-- IFSET IMAGE -->
-          <!-- BEGINNOT no_image -->
-            <img alt="{TITLE}" src="{IMAGE}" />
-          <!-- END no_image -->
-        <!-- END IMAGE -->
-      <!-- END SPRITE -->
-      <!-- BEGIN show_title -->
-        <!-- BEGIN no_image -->
-          <span class="noimage">{TITLE}</span>
-        <!-- BEGINELSE no_image -->
-          <span>{TITLE}</span>
-        <!-- END no_image -->
-      <!-- END show_title -->
-    </a>
-  </li>
+	<li>
+		<a title="{TITLE}" {FLAGS}>
+			<!-- IFSET SPRITE -->
+				{SPRITE}
+			<!-- BEGINELSE SPRITE -->
+				<!-- IFSET IMAGE -->
+					<!-- BEGINNOT no_image -->
+						<img alt="{TITLE}" src="{IMAGE}" />
+					<!-- END no_image -->
+				<!-- END IMAGE -->
+			<!-- END SPRITE -->
+			<!-- BEGIN show_title -->
+				<!-- BEGIN no_image -->
+					<span class="noimage">{TITLE}</span>
+				<!-- BEGINELSE no_image -->
+					<span>{TITLE}</span>
+				<!-- END no_image -->
+			<!-- END show_title -->
+		</a>
+	</li>
 <!-- ENDVAR toolbar_button -->
 <!-- VAR toolbar_label -->
-  <li>
-    <span>{TITLE}</span>
-  </li>
+	<li>
+		<span>{TITLE}</span>
+	</li>
 <!-- ENDVAR toolbar_label -->
 <!-- VAR toolbar_end -->
-  </ul>
-  </div>
+	</ul>
+	</div>
 <!-- ENDVAR toolbar_end -->
 
 <!-- VAR toolbar_vert_start -->
-  <div class="toolbar_vert">
-  <ul>
+	<div class="toolbar_vert">
+	<ul>
 <!-- ENDVAR toolbar_vert_start -->
 <!-- VAR toolbar_vert_button -->
-  <li>
-    <a title="{TITLE}" {FLAGS}>
-      <img alt="{TITLE}" src="{IMAGE}" />
-      <span>{TITLE}</span>
-    </a>
-  </li>
+	<li>
+		<a title="{TITLE}" {FLAGS}>
+			<img alt="{TITLE}" src="{IMAGE}" />
+			<span>{TITLE}</span>
+		</a>
+	</li>
 <!-- ENDVAR toolbar_vert_button -->
 <!-- VAR toolbar_vert_label -->
-  <li>
-    <span>{TITLE}</span>
-  </li>
+	<li>
+		<span>{TITLE}</span>
+	</li>
 <!-- ENDVAR toolbar_vert_label -->
 <!-- VAR toolbar_vert_end -->
-  </ul>
-  </div>
+	</ul>
+	</div>
 <!-- ENDVAR toolbar_vert_end -->
--- a/themes/printable/acledit.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/acledit.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,32 +1,32 @@
 <!-- VAR acl_field_begin -->
 <div class="tblholder">
-  <table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
-    <tr>
-      <th></th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
-    </tr>
+	<table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
+		<tr>
+			<th></th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
+		</tr>
 <!-- ENDVAR acl_field_begin -->
 <!-- VAR acl_field_item -->
-    <tr>
-      <td class="{ROW_CLASS}">{FIELD_DESC}</td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
-    </tr>
+		<tr>
+			<td class="{ROW_CLASS}">{FIELD_DESC}</td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
+		</tr>
 <!-- ENDVAR acl_field_item -->
 <!-- VAR acl_field_end -->
-    <tr>
-      <td colspan="6" class="row3">
-        {lang:acl_lbl_help}
-      </td>
-    </tr>
-  </table>
+		<tr>
+			<td colspan="6" class="row3">
+				{lang:acl_lbl_help}
+			</td>
+		</tr>
+	</table>
 </div>
 <!-- ENDVAR acl_field_end -->
 
--- a/themes/printable/comment.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/comment.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,58 +1,58 @@
 <div class="tblholder">
-  <table border="0" width="100%" cellspacing="1" cellpadding="4">
-    <tr>
-      <th colspan="2" style="text-align: left;">{DATETIME}</th>
-    </tr>
-    <tr>
-      <td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-        <table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
-          <tr>
-            <td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              <b>{NAME}</b><br />
-              <small>{USER_LEVEL}</small>
-              <!-- BEGIN user_has_avatar -->
-              <div class="avatar">
-                <a href="{USERPAGE_LINK}">
-                  <img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
-                </a>
-              </div>
-              <!-- END user_has_avatar -->
-            </td>
-          </tr>
-          <tr>
-            <td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              {SEND_PM_LINK} {ADD_BUDDY_LINK}
-            </td>
-          </tr>
-        </table>
-      </td>
-      <td class="row2">
-        <b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
-      </td>
-    </tr>
-    <tr>
-      <td class="row3">
-        <div id="comment_{ID}">{DATA}</div>
-        <!-- BEGIN signature -->
-          <hr style="margin-left: 1em; width: 200px;" />
-          {SIGNATURE}
-        <!-- END signature -->
-      </td>
-    </tr>
-    <!-- BEGIN can_edit -->
-    <tr>
-      <td class="row2">
-        [ {EDIT_LINK} | {DELETE_LINK} ]
-      </td>
-    </tr>
-    <!-- END can_edit -->
-    <!-- BEGIN auth_mod -->
-    <tr>
-      <td class="row1">
-        <b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
-      </td>
-    </tr>
-    <!-- END auth_mod -->
-  </table>
+	<table border="0" width="100%" cellspacing="1" cellpadding="4">
+		<tr>
+			<th colspan="2" style="text-align: left;">{DATETIME}</th>
+		</tr>
+		<tr>
+			<td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+				<table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
+					<tr>
+						<td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							<b>{NAME}</b><br />
+							<small>{USER_LEVEL}</small>
+							<!-- BEGIN user_has_avatar -->
+							<div class="avatar">
+								<a href="{USERPAGE_LINK}">
+									<img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
+								</a>
+							</div>
+							<!-- END user_has_avatar -->
+						</td>
+					</tr>
+					<tr>
+						<td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							{SEND_PM_LINK} {ADD_BUDDY_LINK}
+						</td>
+					</tr>
+				</table>
+			</td>
+			<td class="row2">
+				<b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
+			</td>
+		</tr>
+		<tr>
+			<td class="row3">
+				<div id="comment_{ID}">{DATA}</div>
+				<!-- BEGIN signature -->
+					<hr style="margin-left: 1em; width: 200px;" />
+					{SIGNATURE}
+				<!-- END signature -->
+			</td>
+		</tr>
+		<!-- BEGIN can_edit -->
+		<tr>
+			<td class="row2">
+				[ {EDIT_LINK} | {DELETE_LINK} ]
+			</td>
+		</tr>
+		<!-- END can_edit -->
+		<!-- BEGIN auth_mod -->
+		<tr>
+			<td class="row1">
+				<b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
+			</td>
+		</tr>
+		<!-- END auth_mod -->
+	</table>
 </div>
 <br />
--- a/themes/printable/css-simple/bleu.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/css-simple/bleu.css	Sun Mar 28 23:10:46 2010 -0400
@@ -5,15 +5,15 @@
 /* Basic definitions */
  
 html, body {
-  height: 100%;
-  margin: 0;
-  padding: 0;
+	height: 100%;
+	margin: 0;
+	padding: 0;
 }
 
 body {
-  background-image: url(../images/bleu/bg.png);
-  font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
-  font-size: 9pt;
+	background-image: url(../images/bleu/bg.png);
+	font-family: trebuchet ms, verdana, arial, helvetica, sans-serif;
+	font-size: 9pt;
 }
 
 /* Dummy cells and backgrounds */
@@ -63,11 +63,11 @@
 /* Header */
 
 table#enano-main td#head-main {
-  text-align: center;
+	text-align: center;
 }
 
 table#enano-main td#head-main h1 {
-  font-size: 14pt;
+	font-size: 14pt;
 }
 
 /* The "page tools" bar below the site logo but above the page content */
@@ -85,7 +85,7 @@
 
 /* Content area */
 table#enano-main td#main-main {
-  padding: 10px 0;
+	padding: 10px 0;
 }
 
 /* Text, headings, and links inside the main div (usually #ajaxEditContainer but used some other places as well) * /
@@ -107,8 +107,8 @@
 /* Footer */
 
 table#enano-main td#foot-main {
-  color: #AAA;
-  font-size: 7pt;
+	color: #AAA;
+	font-size: 7pt;
 }
 
 /* Styled boxes */
--- a/themes/printable/css/default.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/css/default.css	Sun Mar 28 23:10:46 2010 -0400
@@ -86,84 +86,84 @@
  */
 
 div.menu {
-  background-color: #B0D0F0;
-  font-size: 7pt;
-  border-width: 0;
+	background-color: #B0D0F0;
+	font-size: 7pt;
+	border-width: 0;
 }
 div.menu a, div.menu div.label {
-  padding: 2.5pt 5px;
-  margin-right: 3px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  color: #406080;
+	padding: 2.5pt 5px;
+	margin-right: 3px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	color: #406080;
 }
 div.menu div.label {
-  color: #001020;
-  cursor: default;
+	color: #001020;
+	cursor: default;
 }
 div.menu span.sep {
-  display: block;
-  float: left;
-  width: 5px;
+	display: block;
+	float: left;
+	width: 5px;
 }
 div.menu div.multopts {
-  line-height: 17pt;
+	line-height: 17pt;
 }
 div.menu div.multopts a, div.menu div.multopts div.label {
-  float: none;
-  display: inline;
+	float: none;
+	display: inline;
 }
 div.menu a.liteselected, div.menu a.liteselected:hover, div.menu a:hover {
-  color: #406080;
-  background-color: #D0F0FF;
+	color: #406080;
+	background-color: #D0F0FF;
 }
 div.menu input[type ^="text"], div.menu input[type ^="password"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 4px 5px;
-  max-width: 70px;
-  background-color: #D0F0FF;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 4px 5px;
+	max-width: 70px;
+	background-color: #D0F0FF;
 }
 div.menu input[type ^="text"]:hover, div.menu input[type ^="password"]:hover {
-  background-color: #E0F0FF;
+	background-color: #E0F0FF;
 }
 div.menu input[type ^="text"]:focus, div.menu input[type ^="password"]:focus {
-  background-color: #F0F0FF;
+	background-color: #F0F0FF;
 }
 div.menu input[type ^="button"], div.menu input[type ^="submit"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 3px 5px;
-  max-width: 70px;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 3px 5px;
+	max-width: 70px;
 }
 div.menu a.current, div.menu a.current:hover, div.menu a.selected, div.menu a.selected:hover {
-  color: #000040;
-  background-color: #FFFFFF;
+	color: #000040;
+	background-color: #FFFFFF;
 }
 div.menu ul {
-  display: none;
-  position: absolute;
-  padding: 0;
-  margin: 0;
-  background-color: #B0D0F0;
-  border-width: 0;
-  min-width: 120px;
+	display: none;
+	position: absolute;
+	padding: 0;
+	margin: 0;
+	background-color: #B0D0F0;
+	border-width: 0;
+	min-width: 120px;
 }
 div.menu ul li {
-  list-style: none;
+	list-style: none;
 }
 div.menu ul a {
-  float: none;
-  margin: 0;
+	float: none;
+	margin: 0;
 }
 span.menuclear {
-  font-size: 1px;
-  height: 0px;
-  width: 0px;
-  clear: left;
-  line-height: 0px;
-  display: block;
+	font-size: 1px;
+	height: 0px;
+	width: 0px;
+	clear: left;
+	line-height: 0px;
+	display: block;
 }
 
 /* Rounded corners on nearly everything */
@@ -232,125 +232,125 @@
 
 /* toolbar */
 div.toolbar {
-  border-bottom: 1px solid #909090;
-  background-color: #D0D0D0;
-  padding: 2px 0;
-  height: 22px;
-  font-family: arial, sans-serif;
-  font-size: 8pt;
+	border-bottom: 1px solid #909090;
+	background-color: #D0D0D0;
+	padding: 2px 0;
+	height: 22px;
+	font-family: arial, sans-serif;
+	font-size: 8pt;
 }
 div.toolbar ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar ul li {
-  list-style: none;
-  margin: 0;
-  float: left;
+	list-style: none;
+	margin: 0;
+	float: left;
 }
 div.toolbar a img {
-  opacity: 0.6;
-  /*filter: alpha(opacity=60);*/
+	opacity: 0.6;
+	/*filter: alpha(opacity=60);*/
 }
 div.toolbar a:hover img {
-  opacity: 1;
-  /*filter: alpha(opacity=100);*/
+	opacity: 1;
+	/*filter: alpha(opacity=100);*/
 }
 div.toolbar a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar a:hover {
-  border: 1px solid #202090;
-  background-color: #ceceed;
-  color: #000000;
-  text-decoration: none;
+	border: 1px solid #202090;
+	background-color: #ceceed;
+	color: #000000;
+	text-decoration: none;
 }
 div.toolbar a:active {
-  border: 1px solid #A0A0A0;
-  background-color: #E0E0E0;
+	border: 1px solid #A0A0A0;
+	background-color: #E0E0E0;
 }
 div.toolbar img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar a span {
-  position: relative;
-  top: -4px;
+	position: relative;
+	top: -4px;
 }
 div.toolbar li span {
-  padding-left: 2px;
-  padding-right: 5px;
+	padding-left: 2px;
+	padding-right: 5px;
 }
 
 /* vertical toolbar */
 div.toolbar_vert {
-  border: 1px solid #909090;
-  background-color: #D0D0D0;
-  padding: 2px 0;
+	border: 1px solid #909090;
+	background-color: #D0D0D0;
+	padding: 2px 0;
 }
 div.toolbar_vert ul {
-  margin: 0;
-  padding: 0;
+	margin: 0;
+	padding: 0;
 }
 div.toolbar_vert ul li {
-  list-style: none;
-  margin: 0;
+	list-style: none;
+	margin: 0;
 }
 div.toolbar_vert a img {
-  opacity: 0.6;
-  /*filter: alpha(opacity=60);*/
+	opacity: 0.6;
+	/*filter: alpha(opacity=60);*/
 }
 div.toolbar_vert a:hover img {
-  opacity: 1;
-  /*filter: alpha(opacity=100);*/
+	opacity: 1;
+	/*filter: alpha(opacity=100);*/
 }
 div.toolbar_vert a {
-  display: block;
-  padding: 2px;
-  border: 1px solid transparent;
-  cursor: default;
-  width: auto;
-  color: #000000;
-  margin: 0 2px;
-  max-height: 16px;
-  text-decoration: none;
+	display: block;
+	padding: 2px;
+	border: 1px solid transparent;
+	cursor: default;
+	width: auto;
+	color: #000000;
+	margin: 0 2px;
+	max-height: 16px;
+	text-decoration: none;
 }
 div.toolbar_vert a:hover {
-  border: 1px solid #202090;
-  background-color: #ceceed;
-  color: #000000;
-  text-decoration: none;
+	border: 1px solid #202090;
+	background-color: #ceceed;
+	color: #000000;
+	text-decoration: none;
 }
 div.toolbar_vert a:active {
-  border: 1px solid #A0A0A0;
-  background-color: #E0E0E0;
+	border: 1px solid #A0A0A0;
+	background-color: #E0E0E0;
 }
 div.toolbar_vert img {
-  margin: 0;
-  padding: 0;
-  display: inline;
-  border-width: 0px;
+	margin: 0;
+	padding: 0;
+	display: inline;
+	border-width: 0px;
 }
 div.toolbar_vert a span {
-  position: relative;
-  top: -4px;
+	position: relative;
+	top: -4px;
 }
 div.toolbar_vert li span {
-  padding-left: 2px;
-  padding-right: 5px;
+	padding-left: 2px;
+	padding-right: 5px;
 }
 
 div.mdg-comment {
-  display: none;
+	display: none;
 }
 
--- a/themes/printable/elements.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/elements.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -15,50 +15,50 @@
 <!-- VAR sidebar_heading --><div class="heading">{TEXT}</div>
 <!-- ENDVAR sidebar_heading -->
 <!-- VAR sidebar_top -->
-          <div class="recttop">
-            <table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
-              <tr>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/border-menu-l.gif" width="12" height="12" /> </td>
-                <td style="margin: 0; padding: 0; height: 12px;" class="recttoptop"></td>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/border-menu-r.gif" width="12" height="12" /> </td>
-              </tr>
-            </table>
-          </div>
-          <div class="sidebar">
+					<div class="recttop">
+						<table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
+							<tr>
+								<td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/border-menu-l.gif" width="12" height="12" /> </td>
+								<td style="margin: 0; padding: 0; height: 12px;" class="recttoptop"></td>
+								<td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/border-menu-r.gif" width="12" height="12" /> </td>
+							</tr>
+						</table>
+					</div>
+					<div class="sidebar">
 <!-- ENDVAR sidebar_top -->
 <!-- VAR sidebar_section -->
-            <div class="slider">
-              <div class="heading">
-                <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-                <br style="display: none;" /><br style="display: none;" />
-                <a class="head" onclick="toggle(this); return false" href="#">{TITLE}</a>
-                <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-                <br style="display: none;" /><br style="display: none;" />
-              </div>
-              <div class="slideblock">{CONTENT}</div>
-            </div>
+						<div class="slider">
+							<div class="heading">
+								<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+								<br style="display: none;" /><br style="display: none;" />
+								<a class="head" onclick="toggle(this); return false" href="#">{TITLE}</a>
+								<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+								<br style="display: none;" /><br style="display: none;" />
+							</div>
+							<div class="slideblock">{CONTENT}</div>
+						</div>
 <!-- ENDVAR sidebar_section -->
 <!-- VAR sidebar_section_raw -->
-            <div class="slider">
-              <div class="heading">
-                <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-                <br style="display: none;" /><br style="display: none;" />
-                <a class="head" onclick="toggle(this); return false" href="#">{TITLE}</a>
-                <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-                <br style="display: none;" /><br style="display: none;" />
-              </div>
-              <div class="slideblock2">{CONTENT}</div>
-            </div>
+						<div class="slider">
+							<div class="heading">
+								<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+								<br style="display: none;" /><br style="display: none;" />
+								<a class="head" onclick="toggle(this); return false" href="#">{TITLE}</a>
+								<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+								<br style="display: none;" /><br style="display: none;" />
+							</div>
+							<div class="slideblock2">{CONTENT}</div>
+						</div>
 <!-- ENDVAR sidebar_section_raw -->
 <!-- VAR sidebar_bottom -->
-          </div>
-          <div class="rectbot">
-            <table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
-              <tr>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/border-bl.gif" width="12" height="12" /> </td>
-                <td style="margin: 0; padding: 0; height: 12px;" class="rectbottop"></td>
-                <td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/border-br.gif" width="12" height="12" /> </td>
-              </tr>
-            </table>
-          </div>
+					</div>
+					<div class="rectbot">
+						<table border="0" width="100%" cellspacing="0" cellpadding="0" style="font-size: 1px;">
+							<tr>
+								<td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/border-bl.gif" width="12" height="12" /> </td>
+								<td style="margin: 0; padding: 0; height: 12px;" class="rectbottop"></td>
+								<td style="margin: 0; padding: 0; height: 12px;"> <img alt=" " src="{SCRIPTPATH}/themes/oxygen/images/{STYLE_ID}/border-br.gif" width="12" height="12" /> </td>
+							</tr>
+						</table>
+					</div>
 <!-- ENDVAR sidebar_bottom -->
--- a/themes/printable/footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,17 +1,17 @@
-              </div>
-            </div></div>
-            
-            <!-- We strongly request that you leave the notice below in its place; it helps to attract users to Enano in exchange for providing you
-                 with your CMS. Enano is still new; therefore we are looking to attract users, and we feel that this notice will help. If you refuse
-                 to include even this tiny little notice, support on the Enano forums may be affected. Thanks guys.
-                 
-                 -Dan
-                 -->
-            <div id="credits">
-              {COPYRIGHT}
-              <!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
-              <br />Powered by <a href="http://enanocms.org">Enano</a>  |  <a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.1</a>  |  <a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">Valid CSS</a>  |  [[Stats]]
-            </div>
-            
-  </body>
+							</div>
+						</div></div>
+						
+						<!-- We strongly request that you leave the notice below in its place; it helps to attract users to Enano in exchange for providing you
+ 								with your CMS. Enano is still new; therefore we are looking to attract users, and we feel that this notice will help. If you refuse
+ 								to include even this tiny little notice, support on the Enano forums may be affected. Thanks guys.
+ 								
+ 								-Dan
+ 								-->
+						<div id="credits">
+							{COPYRIGHT}
+							<!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
+							<br />Powered by <a href="http://enanocms.org">Enano</a>  |  <a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.1</a>  |  <a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">Valid CSS</a>  |  [[Stats]]
+						</div>
+						
+	</body>
 </html>
--- a/themes/printable/header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,24 +1,24 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
-    <link id="mdgCss" rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" />
-    {ADDITIONAL_HEADERS}
-    
-    <style type="text/css" media="print">
-    span.normallink {
-      display: none;
-    }
-    </style>
-  </head>
-  <body>
-    <div class="pad"><div class="contentDiv">
-      <!-- BEGINNOT export -->
-      <div style="float: right;">
-        <span class="normallink"><a href="#" onclick="window.print(); return false;">print page</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="{CONTENTPATH}{PAGE_URLNAME}{ADMIN_SID_AUTO}">view normal version</a></span>&nbsp;<image alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
-      </div>
-      <!-- END export -->
-      <h2>{PAGE_NAME}</h2>
-        <div id="ajaxEditContainer">
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css" />
+		<link id="mdgCss" rel="stylesheet" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" type="text/css" />
+		{ADDITIONAL_HEADERS}
+		
+		<style type="text/css" media="print">
+		span.normallink {
+			display: none;
+		}
+		</style>
+	</head>
+	<body>
+		<div class="pad"><div class="contentDiv">
+			<!-- BEGINNOT export -->
+			<div style="float: right;">
+				<span class="normallink"><a href="#" onclick="window.print(); return false;">print page</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="{CONTENTPATH}{PAGE_URLNAME}{ADMIN_SID_AUTO}">view normal version</a></span>&nbsp;<image alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
+			</div>
+			<!-- END export -->
+			<h2>{PAGE_NAME}</h2>
+				<div id="ajaxEditContainer">
--- a/themes/printable/simple-footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/simple-footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,25 +1,25 @@
 
-            </div>
-          </td>
-          <td id="main-right"></td>
-        </tr>
-        <tr>
-          <td id="foot-left"></td>
-          <td id="foot-main">
-            {COPYRIGHT}
-          </td>
-          <td id="foot-right"></td>
-        </tr>
-        <tr>
-          <td id="foot-btm-left"></td>
-          <td id="foot-btm"></td>
-          <td id="foot-btm-right"></td>
-        </tr>
-      </table>
-    </td>
-    <td style="width: 10%;"></td>
-    </tr>
-    </table>
-  </body>
+						</div>
+					</td>
+					<td id="main-right"></td>
+				</tr>
+				<tr>
+					<td id="foot-left"></td>
+					<td id="foot-main">
+						{COPYRIGHT}
+					</td>
+					<td id="foot-right"></td>
+				</tr>
+				<tr>
+					<td id="foot-btm-left"></td>
+					<td id="foot-btm"></td>
+					<td id="foot-btm-right"></td>
+				</tr>
+			</table>
+		</td>
+		<td style="width: 10%;"></td>
+		</tr>
+		</table>
+	</body>
 </html>
 
--- a/themes/printable/simple-header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/simple-header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,33 +1,33 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html>
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" href="{SCRIPTPATH}/themes/{THEME_ID}/css-simple/{STYLE_ID}.css" type="text/css" id="mdgCss" />
-    {JS_DYNAMIC_VARS}
-    <!-- This script automatically loads the other 15 JS files -->
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
-    {ADDITIONAL_HEADERS}
-  </head>
-  <body>
-    <table border="0" style="width: 100%; height: 100%;">
-    <tr>
-    <td style="width: 10%;"></td>
-    <td valign="middle">
-      <table id="enano-main" border="0" cellspacing="0" cellpadding="0" style="margin: 0 auto;">
-        <tr>
-          <td id="head-up-left"></td>
-          <td id="head-up"></td>
-          <td id="head-up-right"></td>
-        </tr>
-        <tr>
-          <td id="head-left"></td>
-          <td id="head-main">
-            <h1>{PAGE_NAME}</h1>
-          </td>
-          <td id="head-right"></td>
-        </tr>
-        <tr>
-          <td id="main-left"></td>
-          <td id="main-main">
-            <div id="ajaxEditContainer">
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		<link rel="stylesheet" href="{SCRIPTPATH}/themes/{THEME_ID}/css-simple/{STYLE_ID}.css" type="text/css" id="mdgCss" />
+		{JS_DYNAMIC_VARS}
+		<!-- This script automatically loads the other 15 JS files -->
+		<script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
+		{ADDITIONAL_HEADERS}
+	</head>
+	<body>
+		<table border="0" style="width: 100%; height: 100%;">
+		<tr>
+		<td style="width: 10%;"></td>
+		<td valign="middle">
+			<table id="enano-main" border="0" cellspacing="0" cellpadding="0" style="margin: 0 auto;">
+				<tr>
+					<td id="head-up-left"></td>
+					<td id="head-up"></td>
+					<td id="head-up-right"></td>
+				</tr>
+				<tr>
+					<td id="head-left"></td>
+					<td id="head-main">
+						<h1>{PAGE_NAME}</h1>
+					</td>
+					<td id="head-right"></td>
+				</tr>
+				<tr>
+					<td id="main-left"></td>
+					<td id="main-main">
+						<div id="ajaxEditContainer">
--- a/themes/printable/toolbar.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/printable/toolbar.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,49 +1,49 @@
 <!-- Stuff related to toolbars and clickable buttons.
-     Used mostly in the page toolbar on most pages.
-     -->
+ 		Used mostly in the page toolbar on most pages.
+ 		-->
 
 <!-- VAR toolbar_start -->
-  <div class="toolbar">
-  <ul>
+	<div class="toolbar">
+	<ul>
 <!-- ENDVAR toolbar_start -->
 <!-- VAR toolbar_button -->
-  <li>
-    <a title="{TITLE}" {FLAGS}>
-      <img alt="{TITLE}" src="{IMAGE}" />
-      <!-- BEGIN show_title -->
-      <span>{TITLE}</span>
-      <!-- END show_title -->
-    </a>
-  </li>
+	<li>
+		<a title="{TITLE}" {FLAGS}>
+			<img alt="{TITLE}" src="{IMAGE}" />
+			<!-- BEGIN show_title -->
+			<span>{TITLE}</span>
+			<!-- END show_title -->
+		</a>
+	</li>
 <!-- ENDVAR toolbar_button -->
 <!-- VAR toolbar_label -->
-  <li>
-    <span>{TITLE}</span>
-  </li>
+	<li>
+		<span>{TITLE}</span>
+	</li>
 <!-- ENDVAR toolbar_label -->
 <!-- VAR toolbar_end -->
-  </ul>
-  </div>
+	</ul>
+	</div>
 <!-- ENDVAR toolbar_end -->
 
 <!-- VAR toolbar_vert_start -->
-  <div class="toolbar_vert">
-  <ul>
+	<div class="toolbar_vert">
+	<ul>
 <!-- ENDVAR toolbar_vert_start -->
 <!-- VAR toolbar_vert_button -->
-  <li>
-    <a title="{TITLE}" {FLAGS}>
-      <img alt="{TITLE}" src="{IMAGE}" />
-      <span>{TITLE}</span>
-    </a>
-  </li>
+	<li>
+		<a title="{TITLE}" {FLAGS}>
+			<img alt="{TITLE}" src="{IMAGE}" />
+			<span>{TITLE}</span>
+		</a>
+	</li>
 <!-- ENDVAR toolbar_vert_button -->
 <!-- VAR toolbar_vert_label -->
-  <li>
-    <span>{TITLE}</span>
-  </li>
+	<li>
+		<span>{TITLE}</span>
+	</li>
 <!-- ENDVAR toolbar_vert_label -->
 <!-- VAR toolbar_vert_end -->
-  </ul>
-  </div>
+	</ul>
+	</div>
 <!-- ENDVAR toolbar_vert_end -->
--- a/themes/stpatty/acledit.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/acledit.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,32 +1,32 @@
 <!-- VAR acl_field_begin -->
 <div class="tblholder">
-  <table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
-    <tr>
-      <th></th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
-      <th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
-    </tr>
+	<table border="0" cellspacing="1" cellpadding="4" style="width: 100%;">
+		<tr>
+			<th></th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('i');">{lang:acl_lbl_field_inherit}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('1');">{lang:acl_lbl_field_deny}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('2');">{lang:acl_lbl_field_disallow}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('3');">{lang:acl_lbl_field_wikimode}</th>
+			<th style='cursor: pointer;' title="Click to change all columns" onclick="__aclSetAllRadios('4');">{lang:acl_lbl_field_allow}</th>
+		</tr>
 <!-- ENDVAR acl_field_begin -->
 <!-- VAR acl_field_item -->
-    <tr>
-      <td class="{ROW_CLASS}">{FIELD_DESC}</td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
-      <td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
-    </tr>
+		<tr>
+			<td class="{ROW_CLASS}">{FIELD_DESC}</td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="i" name="{FIELD_NAME}" {FIELD_INHERIT_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="1" name="{FIELD_NAME}" {FIELD_DENY_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="2" name="{FIELD_NAME}" {FIELD_DISALLOW_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="3" name="{FIELD_NAME}" {FIELD_WIKIMODE_CHECKED} /></td>
+			<td class="{ROW_CLASS}" style="text-align: center;"><input type="radio" value="4" name="{FIELD_NAME}" {FIELD_ALLOW_CHECKED} /></td>
+		</tr>
 <!-- ENDVAR acl_field_item -->
 <!-- VAR acl_field_end -->
-    <tr>
-      <td colspan="6" class="row3">
-        {lang:acl_lbl_help}
-      </td>
-    </tr>
-  </table>
+		<tr>
+			<td colspan="6" class="row3">
+				{lang:acl_lbl_help}
+			</td>
+		</tr>
+	</table>
 </div>
 <!-- ENDVAR acl_field_end -->
 
--- a/themes/stpatty/comment.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/comment.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,58 +1,58 @@
 <div class="tblholder">
-  <table border="0" width="100%" cellspacing="1" cellpadding="4">
-    <tr>
-      <th colspan="2" style="text-align: left;">{DATETIME}</th>
-    </tr>
-    <tr>
-      <td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-        <table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
-          <tr>
-            <td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              <b>{NAME}</b><br />
-              <small>{USER_LEVEL}</small>
-              <!-- BEGIN user_has_avatar -->
-              <div class="avatar">
-                <a href="{USERPAGE_LINK}">
-                  <img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
-                </a>
-              </div>
-              <!-- END user_has_avatar -->
-            </td>
-          </tr>
-          <tr>
-            <td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
-              {SEND_PM_LINK} {ADD_BUDDY_LINK}
-            </td>
-          </tr>
-        </table>
-      </td>
-      <td class="row2">
-        <b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
-      </td>
-    </tr>
-    <tr>
-      <td class="row3">
-        <div id="comment_{ID}">{DATA}</div>
-        <!-- BEGIN signature -->
-          <hr style="margin-left: 1em; width: 200px;" />
-          {SIGNATURE}
-        <!-- END signature -->
-      </td>
-    </tr>
-    <!-- BEGIN can_edit -->
-    <tr>
-      <td class="row2">
-        [ {EDIT_LINK} | {DELETE_LINK} ]
-      </td>
-    </tr>
-    <!-- END can_edit -->
-    <!-- BEGIN auth_mod -->
-    <tr>
-      <td class="row1">
-        <b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
-      </td>
-    </tr>
-    <!-- END auth_mod -->
-  </table>
+	<table border="0" width="100%" cellspacing="1" cellpadding="4">
+		<tr>
+			<th colspan="2" style="text-align: left;">{DATETIME}</th>
+		</tr>
+		<tr>
+			<td style="width: 120px; height: 100%;" rowspan="4" valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+				<table border="0" width="100%" style="height: 100%;" cellspacing="0" cellpadding="0">
+					<tr>
+						<td valign="top" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							<b>{NAME}</b><br />
+							<small>{USER_LEVEL}</small>
+							<!-- BEGIN user_has_avatar -->
+							<div class="avatar">
+								<a href="{USERPAGE_LINK}">
+									<img alt="{AVATAR_ALT}" src="{AVATAR_URL}" style="border-width: 0px;" />
+								</a>
+							</div>
+							<!-- END user_has_avatar -->
+						</td>
+					</tr>
+					<tr>
+						<td valign="bottom" class="row1<!-- BEGIN is_friend --> row1_green<!-- END is_friend --><!-- BEGIN is_foe --> row1_red<!-- END is_foe -->">
+							{SEND_PM_LINK} {ADD_BUDDY_LINK}
+						</td>
+					</tr>
+				</table>
+			</td>
+			<td class="row2">
+				<b>{lang:comment_lbl_subject}</b> <span id="subject_{ID}">{SUBJECT}</span>
+			</td>
+		</tr>
+		<tr>
+			<td class="row3">
+				<div id="comment_{ID}">{DATA}</div>
+				<!-- BEGIN signature -->
+					<hr style="margin-left: 1em; width: 200px;" />
+					{SIGNATURE}
+				<!-- END signature -->
+			</td>
+		</tr>
+		<!-- BEGIN can_edit -->
+		<tr>
+			<td class="row2">
+				[ {EDIT_LINK} | {DELETE_LINK} ]
+			</td>
+		</tr>
+		<!-- END can_edit -->
+		<!-- BEGIN auth_mod -->
+		<tr>
+			<td class="row1">
+				<b>{lang:comment_lbl_mod_options}</b> {MOD_APPROVE_LINK} {MOD_DELETE_LINK} | {MOD_IP_LINK}
+			</td>
+		</tr>
+		<!-- END auth_mod -->
+	</table>
 </div>
 <br />
--- a/themes/stpatty/css-extra/ie-fixes-shamrock.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/css-extra/ie-fixes-shamrock.css	Sun Mar 28 23:10:46 2010 -0400
@@ -7,17 +7,17 @@
  */
  
 div#bg {
-  background-image: none;
+	background-image: none;
 }
 div#title {
-  /* background-image: none; */
+	/* background-image: none; */
 }
 div#rap {
-  background-image: url(../images/rap-ie.gif);
+	background-image: url(../images/rap-ie.gif);
 }
 div#sidebar ul li a {
-  margin-bottom: -14px;
+	margin-bottom: -14px;
 }
 div#pagetools a {
-  padding: 4px 3px;
+	padding: 4px 3px;
 }
--- a/themes/stpatty/css-extra/structure.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/css-extra/structure.css	Sun Mar 28 23:10:46 2010 -0400
@@ -10,133 +10,133 @@
  */
  
 html,body {
-  margin: 0;
-  padding: 0;
-  height: 100%;
+	margin: 0;
+	padding: 0;
+	height: 100%;
 }
 body {
-  background-repeat: repeat;
-  font-family: "Lucida Sans Unicode", sans-serif;
-  font-size: 75%;
+	background-repeat: repeat;
+	font-family: "Lucida Sans Unicode", sans-serif;
+	font-size: 75%;
 }
 body#tinymce {
-  background-color: white;
-  background-image: none;
+	background-color: white;
+	background-image: none;
 }
 div#bg {
-  min-height: 500px;
-  width: 100%;
-  background-repeat: repeat-x;
+	min-height: 500px;
+	width: 100%;
+	background-repeat: repeat-x;
 }
 div#rap {
-  width: 760px;
-  padding: 0 10px;
-  margin: 0 auto;
+	width: 760px;
+	padding: 0 10px;
+	margin: 0 auto;
 }
 div#title {
-  margin: 0px;
-  padding: 0px;
-  background-repeat: repeat-x;
-  vertical-align: middle;
+	margin: 0px;
+	padding: 0px;
+	background-repeat: repeat-x;
+	vertical-align: middle;
 }
 div#title h1 {
-  margin: 0px 10px 10px 10px;
-  padding-top: 30px;
-  text-align: left;
+	margin: 0px 10px 10px 10px;
+	padding-top: 30px;
+	text-align: left;
 }
 div#title h2 {
-  margin: 0px 10px 0px 10px;
-  padding-bottom: 40px;
-  text-align: left;
+	margin: 0px 10px 0px 10px;
+	padding-bottom: 40px;
+	text-align: left;
 }
 div.straightaway {
-  clear: both;
+	clear: both;
 }
 /* Footer */
 div.footer {
-  min-height: 40px;
-  padding: 10px;
-  background-repeat: repeat-x;
+	min-height: 40px;
+	padding: 10px;
+	background-repeat: repeat-x;
 }
 
 /* Content area */
 div#maincontent {
-  padding: 10px;
-  margin-right: 140px;
+	padding: 10px;
+	margin-right: 140px;
 }
 img#ajaxloadicon {
-  margin-top: 10px;
+	margin-top: 10px;
 }
 div#maincontent h2 {
-  margin: 10px 0;
-  padding: 0;
-  font-size: 16pt;
+	margin: 10px 0;
+	padding: 0;
+	font-size: 16pt;
 }
 div#maincontent p {
-  margin-left: 1em;
+	margin-left: 1em;
 }
 div#maincontent h2#pagetitle {
-  margin: 0;
-  font-size: 18pt;
+	margin: 0;
+	font-size: 18pt;
 }
 div#maincontent a:link, div#maincontent a:visited {
-  text-decoration: none;
+	text-decoration: none;
 }
 div#maincontent ul {
-  list-style: square;
+	list-style: square;
 }
 /* Sidebar */
 div#sidebar, div.dbx-box {
-  float: right;
-  width: 135px;
+	float: right;
+	width: 135px;
 }
 div#sidebar h4, div.dbx-handle {
-  margin: 0;
-  padding: 0 5px;
-  line-height: 25px;
-  font-weight: normal;
-  text-align: right;
-  text-transform: lowercase;
+	margin: 0;
+	padding: 0 5px;
+	line-height: 25px;
+	font-weight: normal;
+	text-align: right;
+	text-transform: lowercase;
 }
 div.dbx-handle {
-  text-align: left;
-  cursor: move;
+	text-align: left;
+	cursor: move;
 }
 div#sidebar ul, .dbx-content ul {
-  margin: 0;
-  padding: 0;
-  list-style: none !important;
+	margin: 0;
+	padding: 0;
+	list-style: none !important;
 }
 div#sidebar ul li, div.dbx-content ul li {
-  padding: 0;
+	padding: 0;
 }
 div#sidebar ul li a, div.dbx-content ul li a {
-  line-height: 25px;
-  padding: 0 7px;
-  text-decoration: none;
-  display: block;
+	line-height: 25px;
+	padding: 0 7px;
+	text-decoration: none;
+	display: block;
 }
 div#sidebar div, div.dbx-content2 {
-  width: 135px;
+	width: 135px;
 }
 div#sidebar div ul {
-  margin-left: 2em;
-  list-style: square;
+	margin-left: 2em;
+	list-style: square;
 }
 div#sidebar div ul a {
-  background-color: transparent;
-  display: inline;
-  border-bottom-width: 0px;
-  padding: 0;
+	background-color: transparent;
+	display: inline;
+	border-bottom-width: 0px;
+	padding: 0;
 }
 div#sidebar div ul a:hover {
-  background-color: transparent;
-  display: inline;
+	background-color: transparent;
+	display: inline;
 }
 div#sidebar div div {
-  background: transparent;
-  border-bottom-width: 0;
-  padding: 0;
+	background: transparent;
+	border-bottom-width: 0;
+	padding: 0;
 }
 
 /*
@@ -144,79 +144,79 @@
  */
 
 div.menu, div.menu_nojs {
-  font-size: 7pt;
-  border-width: 0;
+	font-size: 7pt;
+	border-width: 0;
 }
 div.menu a, div.menu_nojs a, div.menu div.label, div.menu_nojs div.label {
-  padding: 2.5pt 5px;
-  margin-right: 3px;
-  text-decoration: none;
-  display: block;
-  float: left;
-  border-bottom-width: 0 !important;
+	padding: 2.5pt 5px;
+	margin-right: 3px;
+	text-decoration: none;
+	display: block;
+	float: left;
+	border-bottom-width: 0 !important;
 }
 div.menu div.label, div.menu_nojs div.label {
-  cursor: default;
+	cursor: default;
 }
 div.menu span.sep, div.menu_nojs span.sep {
-  display: block;
-  float: left;
-  width: 5px;
+	display: block;
+	float: left;
+	width: 5px;
 }
 div.menu div.multopts, div.menu_nojs div.multopts {
-  line-height: 17pt;
+	line-height: 17pt;
 }
 div.menu div.multopts a, div.menu_nojs div.multopts a, div.menu div.multopts div.label, div.menu_nojs div.multopts div.label {
-  float: none;
-  display: inline;
+	float: none;
+	display: inline;
 }
 div.menu input[type ^="text"], div.menu_nojs input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="password"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 4px 5px;
-  max-width: 70px;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 4px 5px;
+	max-width: 70px;
 }
 div.menu input[type ^="button"], div.menu_nojs input[type ^="button"], div.menu input[type ^="submit"], div.menu_nojs input[type ^="submit"] {
-  border-width: 0;
-  font-size: 9pt;
-  padding: 3px 5px;
-  max-width: 70px;
+	border-width: 0;
+	font-size: 9pt;
+	padding: 3px 5px;
+	max-width: 70px;
 }
 div.menu a.current, div.menu_nojs a.current, div.menu a.current:hover, div.menu_nojs a.current:hover, div.menu a.selected, div.menu_nojs a.selected, div.menu a.selected:hover, div.menu_nojs a.selected:hover {
-  font-weight: bold;
+	font-weight: bold;
 }
 div.menu ul, div.menu_nojs ul {
-  display: none;
-  position: absolute;
-  padding: 0;
-  margin: 0;
-  border-width: 0;
-  min-width: 120px;
+	display: none;
+	position: absolute;
+	padding: 0;
+	margin: 0;
+	border-width: 0;
+	min-width: 120px;
 }
 /*
 div.menu_nojs ul {
-  display: block !important;
+	display: block !important;
 }
 */
 div.menu ul li, div.menu_nojs ul li {
-  list-style: none;
+	list-style: none;
 }
 div.menu ul a, div.menu_nojs ul a {
-  float: none;
-  margin: 0;
+	float: none;
+	margin: 0;
 }
 /*
 span.menuclear {
-  font-size: 1px;
-  height: 0px;
-  width: 0px;
-  clear: left;
-  line-height: 0px;
-  display: block;
+	font-size: 1px;
+	height: 0px;
+	width: 0px;
+	clear: left;
+	line-height: 0px;
+	display: block;
 }
 */
 
 div.userpage_block {
-  clear: left;
+	clear: left;
 }
 
--- a/themes/stpatty/css-simple/shamrock.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/css-simple/shamrock.css	Sun Mar 28 23:10:46 2010 -0400
@@ -10,31 +10,31 @@
  */
 
 div#title h1 {
-  font-size: 16pt;
-  text-align: left;
-  font-weight: normal;
-  margin: 0 10px;
-  padding: 10px;
+	font-size: 16pt;
+	text-align: left;
+	font-weight: normal;
+	margin: 0 10px;
+	padding: 10px;
 }
 
 div#maincontent h2#pagetitle {
-  font-size: 12pt;
+	font-size: 12pt;
 }
 div.footer {
-  min-height: 10px;
+	min-height: 10px;
 }
 
 table#stretcher {
-  width: 100%;
-  height: 100%;
+	width: 100%;
+	height: 100%;
 }
 
 div#bg {
-  height: 100%;
+	height: 100%;
 }
 
 div#rap {
-  border-top:    1px solid #237000;
-  border-bottom: 1px solid #237000;
+	border-top:    1px solid #237000;
+	border-bottom: 1px solid #237000;
 }
 
--- a/themes/stpatty/css/shamrock.css	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/css/shamrock.css	Sun Mar 28 23:10:46 2010 -0400
@@ -10,80 +10,80 @@
  */
  
 body {
-  background-color: #101d14;
-  /* color added in 1.0.2 to fix light text in dark desktop themes */
-  color: #202020;
-  background-image: url(../images/bghatching.gif);
+	background-color: #101d14;
+	/* color added in 1.0.2 to fix light text in dark desktop themes */
+	color: #202020;
+	background-image: url(../images/bghatching.gif);
 }
 div#bg {
-  background-image: url(../images/bgfade.png);
+	background-image: url(../images/bgfade.png);
 }
 div#rap {
-  background-image: url(../images/rap.png);
+	background-image: url(../images/rap.png);
 }
 div#title {
-  background-color: #6abd2b;
-  border-bottom-color: #237000;
-  background-image: url(../images/header.gif);
+	background-color: #6abd2b;
+	border-bottom-color: #237000;
+	background-image: url(../images/header.gif);
 }
 /* Footer */
 div.footer {
-  background-image: url(../images/footer.gif);
-  background-color: #6fba38;
-  color: #FFF;
+	background-image: url(../images/footer.gif);
+	background-color: #6fba38;
+	color: #FFF;
 }
 div.footer a {
-  color: #B3FF78;
+	color: #B3FF78;
 }
 
 /* Content area */
 div#maincontent h2 {
-  color: #53a018;
+	color: #53a018;
 }
 div#maincontent h2#pagetitle {
-  border-bottom: 1px solid #73c038;
-  color: #000000;
+	border-bottom: 1px solid #73c038;
+	color: #000000;
 }
 div#maincontent a:link, div#maincontent a:visited {
-  color: #237000;
+	color: #237000;
 }
 div#maincontent a:hover {
-  color: #033000;
-  border-bottom: 1px dotted #033000;
+	color: #033000;
+	border-bottom: 1px dotted #033000;
 }
 div#maincontent a.wikilink-nonexistent {
-  color: #AA0000;
+	color: #AA0000;
 }
 div#maincontent a.wikilink-nonexistent:hover {
-  color: #BA2000;
+	color: #BA2000;
 }
 /* Sidebar */
 div#sidebar h4, div.dbx-handle {
-  color: #FFF;
-  background-color: #104715;
-  border-bottom: 1px solid #93e058;
+	color: #FFF;
+	background-color: #104715;
+	border-bottom: 1px solid #93e058;
 }
 div#sidebar ul li a, div.dbx-content ul li a {
-  color: #000;
-  background-color: #60A745;
-  border-bottom: 1px solid #93e058;
+	color: #000;
+	background-color: #60A745;
+	border-bottom: 1px solid #93e058;
 }
 div#sidebar ul li a:hover, div.dbx-content ul li a:hover {
-  background-color: #70B755;
+	background-color: #70B755;
 }
 div#sidebar div, div.dbx-content2 {
-  background-color: #60A745;
-  border-bottom: 1px solid #93e058;
+	background-color: #60A745;
+	border-bottom: 1px solid #93e058;
 }
 div#sidebar div ul a {
-  color: #134000;
+	color: #134000;
 }
 div#sidebar div ul a:hover {
-  color: #033000;
-  border-bottom: 1px dotted #033000;
+	color: #033000;
+	border-bottom: 1px dotted #033000;
 }
 div#maincontent div.dbx-box a {
-  color: #000000;
+	color: #000000;
 }
 
 /*
@@ -91,33 +91,33 @@
  */
 
 div.menu, div.menu_nojs {
-  background-color: #93e058;
+	background-color: #93e058;
 }
 div.menu a, div.menu_nojs a, div.menu div.label, div.menu_nojs div.label {
-  color: #235000;
+	color: #235000;
 }
 div.menu div.label, div.menu_nojs div.label {
-  color: #002010;
+	color: #002010;
 }
 div.menu a.liteselected, div.menu_nojs a.liteselected, div.menu a.liteselected:hover, div.menu_nojs a.liteselected:hover, div.menu a:hover, div.menu_nojs a:hover {
-  color: #235000;
-  background-color: #A3F068;
+	color: #235000;
+	background-color: #A3F068;
 }
 div.menu input[type ^="text"], div.menu_nojs input[type ^="text"], div.menu input[type ^="password"], div.menu_nojs input[type ^="password"] {
-  background-color: #A3F068;
+	background-color: #A3F068;
 }
 div.menu input[type ^="text"]:hover, div.menu_nojs input[type ^="text"]:hover, div.menu input[type ^="password"]:hover, div.menu_nojs input[type ^="password"]:hover {
-  background-color: #AAF870;
+	background-color: #AAF870;
 }
 div.menu input[type ^="text"]:focus, div.menu_nojs input[type ^="text"]:focus, div.menu input[type ^="password"]:focus, div.menu_nojs input[type ^="password"]:focus {
-  background-color: #B3FF78;
+	background-color: #B3FF78;
 }
 div.menu a.current, div.menu_nojs a.current, div.menu a.current:hover, div.menu_nojs a.current:hover, div.menu a.selected, div.menu_nojs a.selected, div.menu a.selected:hover, div.menu_nojs a.selected:hover {
-  color: #000040;
-  background-color: #f4fff7;
+	color: #000040;
+	background-color: #f4fff7;
 }
 div.menu ul, div.menu_nojs ul {
-  background-color: #93e058;
+	background-color: #93e058;
 }
 
 /* Other Enano-related stuff */
@@ -174,47 +174,47 @@
  
 /* group container(s) */
 #sbedit {
-  margin: 0;
-  padding: 0;
-  /* position:relative; /* additional outer containers must also have position:relative */
+	margin: 0;
+	padding: 0;
+	/* position:relative; /* additional outer containers must also have position:relative */
 }
 /* keyboard navigation tooltip */
 .dbx-tooltip {
-  display:block;
-  position:absolute;
-  margin:36px 0 0 125px;
-  width:185px;
-  border:1px solid #000;
-  background:#ffd;
-  color:#000;
-  font:normal normal normal 0.85em tahoma, arial, sans-serif;
-  padding:2px 4px 3px 5px;
-  text-align:left;
-  }
+	display:block;
+	position:absolute;
+	margin:36px 0 0 125px;
+	width:185px;
+	border:1px solid #000;
+	background:#ffd;
+	color:#000;
+	font:normal normal normal 0.85em tahoma, arial, sans-serif;
+	padding:2px 4px 3px 5px;
+	text-align:left;
+	}
 * html .dbx-tooltip { width:195px; }
 
 /* use CSS2 system colors in CSS2 browsers 
-   but not safari, which doesn't support them */
+ 	but not safari, which doesn't support them */
 *[class="dbx-tooltip"]:lang(en) {
-  border-color:InfoText;
-  background:InfoBackground;
-  color:InfoText;
-  font:small-caption;
-  font-weight:normal;
-  }
+	border-color:InfoText;
+	background:InfoBackground;
+	color:InfoText;
+	font:small-caption;
+	font-weight:normal;
+	}
 /* additional clone styles */
 .dbx-clone {
-  opacity: 0.8;
+	opacity: 0.8;
 }
 .dbx-content ul {
-  margin: 0; padding: 0;
-  list-style: none;
+	margin: 0; padding: 0;
+	list-style: none;
 }
 .dbx-content li a, .dbx-content li a:hover {
-  text-decoration: none;
+	text-decoration: none;
 }
 .dbx-content2 {
-  margin: 0px 1px 0px 1px;
+	margin: 0px 1px 0px 1px;
 }
 
 /* inputs */
@@ -224,17 +224,17 @@
 }
 
 input, select, textarea {
-  background-color: #539018;
-  color: #FFF;
-  padding: 2px;
-  border: 1px solid #EEE;
+	background-color: #539018;
+	color: #FFF;
+	padding: 2px;
+	border: 1px solid #EEE;
 }
 
 input:hover {
-  background-color: #73b038;
+	background-color: #73b038;
 }
 
 input:active, input:focus {
-  background-color: #83c048;
+	background-color: #83c048;
 }
 
--- a/themes/stpatty/elements.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/elements.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -17,24 +17,24 @@
 <!-- VAR sidebar_top -->
 <!-- ENDVAR sidebar_top -->
 <!-- VAR sidebar_section -->
-            <h4>
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-              {TITLE}
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-            </h4>
-            <ul>
-              {CONTENT}
-            </ul>
+						<h4>
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+							{TITLE}
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+						</h4>
+						<ul>
+							{CONTENT}
+						</ul>
 <!-- ENDVAR sidebar_section -->
 <!-- VAR sidebar_section_raw -->
-            <h4>
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
-              {TITLE}
-              <!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
-            </h4>
-            <div>
-              {CONTENT}
-            </div>
+						<h4>
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_START}<!-- END in_sidebar_admin -->
+							{TITLE}
+							<!-- BEGIN in_sidebar_admin -->{ADMIN_END}<!-- END in_sidebar_admin -->
+						</h4>
+						<div>
+							{CONTENT}
+						</div>
 <!-- ENDVAR sidebar_section_raw -->
 <!-- VAR sidebar_bottom -->
 <!-- ENDVAR sidebar_bottom -->
--- a/themes/stpatty/footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,23 +1,23 @@
-          </div>
-        </div>
-        
-        <div class="straightaway"></div>
-        <div class="footer">
-          <!-- We strongly request that you leave the notice below in its place; it helps to attract users to Enano in exchange for providing you
-                 with your CMS. Enano is still new; therefore we are looking to attract users, and we feel that this notice will help. If you refuse
-                 to include even this tiny little notice, support on the Enano forums may be affected. Thanks guys.
-                 
-                 -Dan
-                 -->
-          <b>{COPYRIGHT}</b>
-          <!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
-          <br />[[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.1</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">Valid CSS</a>&nbsp;&nbsp;|&nbsp;&nbsp;Time: [[GenTime]]s<!-- BEGIN auth_admin -->&nbsp;&nbsp;|&nbsp;&nbsp;<a href="{REPORT_URI}">[[NumQueries]] SQL</a><!-- END auth_admin -->
-          <!-- Do not remove this line or scheduled tasks will not run. -->
-          <img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
-        </div>
-      </div>
-    </div>
-    {JS_FOOTER}
-  </body>
+					</div>
+				</div>
+				
+				<div class="straightaway"></div>
+				<div class="footer">
+					<!-- We strongly request that you leave the notice below in its place; it helps to attract users to Enano in exchange for providing you
+ 								with your CMS. Enano is still new; therefore we are looking to attract users, and we feel that this notice will help. If you refuse
+ 								to include even this tiny little notice, support on the Enano forums may be affected. Thanks guys.
+ 								
+ 								-Dan
+ 								-->
+					<b>{COPYRIGHT}</b>
+					<!-- You may remove the following line, but it will affect your support from the Enano project. See: http://enanocms.org/powered-link -->
+					<br />[[EnanoPoweredLinkLong]]&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.1</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="http://jigsaw.w3.org/css-validator/validator?uri=referer">Valid CSS</a>&nbsp;&nbsp;|&nbsp;&nbsp;Time: [[GenTime]]s<!-- BEGIN auth_admin -->&nbsp;&nbsp;|&nbsp;&nbsp;<a href="{REPORT_URI}">[[NumQueries]] SQL</a><!-- END auth_admin -->
+					<!-- Do not remove this line or scheduled tasks will not run. -->
+					<img alt=" " src="{SCRIPTPATH}/cron.php" width="1" height="1" />
+				</div>
+			</div>
+		</div>
+		{JS_FOOTER}
+	</body>
 </html>
 
--- a/themes/stpatty/header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,107 +1,107 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    {JS_DYNAMIC_VARS}
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css?{ENANO_VERSION}" />
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/structure.css?{ENANO_VERSION}" />
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css?{ENANO_VERSION}" />
-    {JS_HEADER}
-    <!--[if lt IE 7]>
-    <link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/ie-fixes-{STYLE_ID}.css" />
-    <![endif]-->
-    <script type="text/javascript">
-    // <![CDATA[
-    
-      // Disable transition effects for the ACL editor
-      // (they're real slow in this theme, at least in fx/opera/IE)
-      var aclDisableTransitionFX = true;
-    
-      function ajaxRenameInline()
-      {
-        // This trick is _so_ vBulletin...
-        elem = document.getElementById('pagetitle');
-        if(!elem) return;
-        elem.style.display = 'none';
-        name = elem.firstChild.nodeValue;
-        textbox = document.createElement('input');
-        textbox.type = 'text';
-        textbox.value = name;
-        textbox.id = 'pageheading';
-        textbox.size = name.length + 7;
-        textbox.onkeyup = function(e) { if(!e) return; if(e.keyCode == 13) ajaxRenameInlineSave(); if(e.keyCode == 27) ajaxRenameInlineCancel(); };
-        elem.parentNode.insertBefore(textbox, elem);
-        document.onclick = ajaxRenameInlineCancel;
-      }
-      function ajaxRenameInlineSave()
-      {
-        elem1 = document.getElementById('pagetitle');
-        elem2 = document.getElementById('pageheading');
-        if(!elem1 || !elem2) return;
-        value = elem2.value;
-        elem2.parentNode.removeChild(elem2); // just destroy the thing
-        elem1.removeChild(elem1.firstChild);
-        elem1.appendChild(document.createTextNode(value));
-        elem1.style.display = 'block';
-        if(!value || value=='') return;
-        ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+ajaxEscape(value), function() {
-          if(ajax.readyState == 4) {
-            alert(ajax.responseText);
-          }
-        });
-      }
-      function ajaxRenameInlineCancel(e)
-      {
-        if ( !e )
-          e = window.event;
-        elem1 = document.getElementById('pagetitle');
-        elem2 = document.getElementById('pageheading');
-        if(!elem1 || !elem2) return;
-        if ( e && e.target )
-        {
-          if(e.target == elem2)
-            return;
-        }
-        //value = elem2.value;
-        elem2.parentNode.removeChild(elem2); // just destroy the thing
-        //elem1.innerHTML = value;
-        elem1.style.display = 'block';
-        document.onclick = null;
-      }
-      // ]]>
-    </script>
-    {ADDITIONAL_HEADERS}
-  </head>
-  <body>
-    <div id="bg">
-      <div id="rap">
-        <div id="title">
-          <h1>{SITE_NAME}</h1>
-          <h2>{SITE_DESC}</h2>
-        </div>
-        <div class="menu_nojs" id="pagebar_main">
-          <div class="label">{lang:onpage_lbl_pagetools}</div>
-          {TOOLBAR}
-          <ul>
-            {TOOLBAR_EXTRAS}
-          </ul>
-          <span class="menuclear">&nbsp;</span>
-        </div>
-        <div id="sidebar">
-          <!-- BEGIN sidebar_left -->
-          {SIDEBAR_LEFT}
-          <!-- END sidebar_left -->
-          <!-- BEGIN sidebar_right -->
-          <!-- BEGINNOT in_admin -->
-          {SIDEBAR_RIGHT}
-          <!-- END in_admin -->
-          <!-- END sidebar_right -->
-        </div>
-        <div id="maincontent">
-          <div style="float: right;">
-            <img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
-          </div>
-          <h2 id="pagetitle" <!-- BEGIN auth_rename --> ondblclick="ajaxRenameInline();" title="Double-click to rename this page" <!-- END auth_rename -->>{PAGE_NAME}</h2>
-          <div id="ajaxEditContainer">
-            
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		{JS_DYNAMIC_VARS}
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/includes/clientside/css/enano-shared.css?{ENANO_VERSION}" />
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/structure.css?{ENANO_VERSION}" />
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css?{ENANO_VERSION}" />
+		{JS_HEADER}
+		<!--[if lt IE 7]>
+		<link rel="stylesheet" type="text/css" href="{CDNPATH}/themes/{THEME_ID}/css-extra/ie-fixes-{STYLE_ID}.css" />
+		<![endif]-->
+		<script type="text/javascript">
+		// <![CDATA[
+		
+			// Disable transition effects for the ACL editor
+			// (they're real slow in this theme, at least in fx/opera/IE)
+			var aclDisableTransitionFX = true;
+		
+			function ajaxRenameInline()
+			{
+				// This trick is _so_ vBulletin...
+				elem = document.getElementById('pagetitle');
+				if(!elem) return;
+				elem.style.display = 'none';
+				name = elem.firstChild.nodeValue;
+				textbox = document.createElement('input');
+				textbox.type = 'text';
+				textbox.value = name;
+				textbox.id = 'pageheading';
+				textbox.size = name.length + 7;
+				textbox.onkeyup = function(e) { if(!e) return; if(e.keyCode == 13) ajaxRenameInlineSave(); if(e.keyCode == 27) ajaxRenameInlineCancel(); };
+				elem.parentNode.insertBefore(textbox, elem);
+				document.onclick = ajaxRenameInlineCancel;
+			}
+			function ajaxRenameInlineSave()
+			{
+				elem1 = document.getElementById('pagetitle');
+				elem2 = document.getElementById('pageheading');
+				if(!elem1 || !elem2) return;
+				value = elem2.value;
+				elem2.parentNode.removeChild(elem2); // just destroy the thing
+				elem1.removeChild(elem1.firstChild);
+				elem1.appendChild(document.createTextNode(value));
+				elem1.style.display = 'block';
+				if(!value || value=='') return;
+				ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+ajaxEscape(value), function() {
+					if(ajax.readyState == 4) {
+						alert(ajax.responseText);
+					}
+				});
+			}
+			function ajaxRenameInlineCancel(e)
+			{
+				if ( !e )
+					e = window.event;
+				elem1 = document.getElementById('pagetitle');
+				elem2 = document.getElementById('pageheading');
+				if(!elem1 || !elem2) return;
+				if ( e && e.target )
+				{
+					if(e.target == elem2)
+						return;
+				}
+				//value = elem2.value;
+				elem2.parentNode.removeChild(elem2); // just destroy the thing
+				//elem1.innerHTML = value;
+				elem1.style.display = 'block';
+				document.onclick = null;
+			}
+			// ]]>
+		</script>
+		{ADDITIONAL_HEADERS}
+	</head>
+	<body>
+		<div id="bg">
+			<div id="rap">
+				<div id="title">
+					<h1>{SITE_NAME}</h1>
+					<h2>{SITE_DESC}</h2>
+				</div>
+				<div class="menu_nojs" id="pagebar_main">
+					<div class="label">{lang:onpage_lbl_pagetools}</div>
+					{TOOLBAR}
+					<ul>
+						{TOOLBAR_EXTRAS}
+					</ul>
+					<span class="menuclear">&nbsp;</span>
+				</div>
+				<div id="sidebar">
+					<!-- BEGIN sidebar_left -->
+					{SIDEBAR_LEFT}
+					<!-- END sidebar_left -->
+					<!-- BEGIN sidebar_right -->
+					<!-- BEGINNOT in_admin -->
+					{SIDEBAR_RIGHT}
+					<!-- END in_admin -->
+					<!-- END sidebar_right -->
+				</div>
+				<div id="maincontent">
+					<div style="float: right;">
+						<img alt=" " src="{CDNPATH}/images/spacer.gif" id="ajaxloadicon" />
+					</div>
+					<h2 id="pagetitle" <!-- BEGIN auth_rename --> ondblclick="ajaxRenameInline();" title="Double-click to rename this page" <!-- END auth_rename -->>{PAGE_NAME}</h2>
+					<div id="ajaxEditContainer">
+						
--- a/themes/stpatty/simple-footer.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/simple-footer.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,36 +1,36 @@
-          </div>
-        </div>
-        
-        <div class="straightaway"></div>
-        <div class="footer">
-          <!-- We strongly request that you leave the notice below in its place; it helps to attract users to Enano in exchange for providing you
-                 with your CMS. Enano is still new; therefore we are looking to attract users, and we feel that this notice will help. If you refuse
-                 to include even this tiny little notice, support on the Enano forums may be affected. Thanks guys.
-                 
-                 -Dan
-                 -->
-          Powered by <a href="{CONTENTPATH}{NS_SPECIAL}About_Enano{ADMIN_SID_AUTO}">Enano</a>, copyright &copy; 2006-2007 Dan Fuhry.
-        </div>
-      </div>
-      </td></tr></table>
-    </div>
-    <div style="display: none;">
-    <h2>Your browser does not support CSS.</h2>
-     <p>If you can see this text, it means that your browser does not support Cascading Style Sheets (CSS). CSS is a fundemental aspect of XHTML, and as a result it is becoming very widely adopted by websites, including this one. You should consider switching to a more modern web browser, such as Mozilla Firefox or Opera 9.</p>
-     <p>Because of this, there are a few minor issues that you may experience while browsing this site, not the least of which is some visual elements below that would normally be hidden in most browsers. Please excuse these minor inconveniences.</p>
-    </div>
-    <div id="root2" class="jswindow" style="display: none;">
-      <div id="tb2" class="titlebar">Change style</div>
-      <div class="content" id="cn2">
-        
-      </div>
-    </div>
-    <div id="root3" class="jswindow" style="display: none;">
-      <div id="tb3" class="titlebar">Wiki formatting help</div>
-      <div class="content" id="cn3">
-        Loading...
-      </div>
-    </div>
-  </body>
+					</div>
+				</div>
+				
+				<div class="straightaway"></div>
+				<div class="footer">
+					<!-- We strongly request that you leave the notice below in its place; it helps to attract users to Enano in exchange for providing you
+ 								with your CMS. Enano is still new; therefore we are looking to attract users, and we feel that this notice will help. If you refuse
+ 								to include even this tiny little notice, support on the Enano forums may be affected. Thanks guys.
+ 								
+ 								-Dan
+ 								-->
+					Powered by <a href="{CONTENTPATH}{NS_SPECIAL}About_Enano{ADMIN_SID_AUTO}">Enano</a>, copyright &copy; 2006-2007 Dan Fuhry.
+				</div>
+			</div>
+			</td></tr></table>
+		</div>
+		<div style="display: none;">
+		<h2>Your browser does not support CSS.</h2>
+ 		<p>If you can see this text, it means that your browser does not support Cascading Style Sheets (CSS). CSS is a fundemental aspect of XHTML, and as a result it is becoming very widely adopted by websites, including this one. You should consider switching to a more modern web browser, such as Mozilla Firefox or Opera 9.</p>
+ 		<p>Because of this, there are a few minor issues that you may experience while browsing this site, not the least of which is some visual elements below that would normally be hidden in most browsers. Please excuse these minor inconveniences.</p>
+		</div>
+		<div id="root2" class="jswindow" style="display: none;">
+			<div id="tb2" class="titlebar">Change style</div>
+			<div class="content" id="cn2">
+				
+			</div>
+		</div>
+		<div id="root3" class="jswindow" style="display: none;">
+			<div id="tb3" class="titlebar">Wiki formatting help</div>
+			<div class="content" id="cn3">
+				Loading...
+			</div>
+		</div>
+	</body>
 </html>
 
--- a/themes/stpatty/simple-header.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/simple-header.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,78 +1,78 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 <html>
-  <head>
-    <title>{PAGE_NAME} &bull; {SITE_NAME}</title>
-    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    {JS_DYNAMIC_VARS}
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
-    <link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-simple/{STYLE_ID}.css" />
-    <!-- This script automatically loads the other 15 JS files -->
-    <script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
-    <!--[if IE]>
-    <link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-extra/ie-fixes.css" />
-    <![endif]-->
-    <script type="text/javascript">
-    // <![CDATA[
-      function ajaxRenameInline()
-      {
-        // This trick is _so_ vBulletin...
-        elem = document.getElementById('pagetitle');
-        if(!elem) return;
-        elem.style.display = 'none';
-        name = elem.innerHTML;
-        textbox = document.createElement('input');
-        textbox.type = 'text';
-        textbox.value = name;
-        textbox.id = 'pageheading';
-        textbox.size = name.length + 7;
-        textbox.onkeyup = function(e) { if(!e) return; if(e.keyCode == 13) ajaxRenameInlineSave(); if(e.keyCode == 27) ajaxRenameInlineCancel(); };
-        elem.parentNode.insertBefore(textbox, elem);
-      }
-      function ajaxRenameInlineSave()
-      {
-        elem1 = document.getElementById('pagetitle');
-        elem2 = document.getElementById('pageheading');
-        if(!elem1 || !elem2) return;
-        value = elem2.value;
-        elem2.parentNode.removeChild(elem2); // just destroy the thing
-        elem1.innerHTML = value;
-        elem1.style.display = 'block';
-        if(!value || value=='') return;
-        ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+escape(value), function() {
-          if(ajax.readyState == 4) {
-            alert(ajax.responseText);
-          }
-        });
-      }
-      function ajaxRenameInlineCancel()
-      {
-        elem1 = document.getElementById('pagetitle');
-        elem2 = document.getElementById('pageheading');
-        if(!elem1 || !elem2) return;
-        //value = elem2.value;
-        elem2.parentNode.removeChild(elem2); // just destroy the thing
-        //elem1.innerHTML = value;
-        elem1.style.display = 'block';
-        if(!value || value=='') return;
-      }
-      // ]]>
-    </script>
-    {ADDITIONAL_HEADERS}
-  </head>
-  <body>
-    <div id="bg">
-      <table border="0" id="stretcher" cellspacing="0" cellpadding="0"><tr><td valign="middle" id="stretcher-main">
-      <div id="rap">
-        <div id="title">
-          <img id="clover" src="{SCRIPTPATH}/themes/{THEME_ID}/images/clover.png" alt=" " />
-          <h1>{PAGE_NAME}</h1>
-        </div>
-        <div id="sidebar">
-          
-        </div>
-        <div id="maincontent">
-          <div style="float: right;">
-            <img alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
-          </div>
-          <div id="ajaxEditContainer">
-            
+	<head>
+		<title>{PAGE_NAME} &bull; {SITE_NAME}</title>
+		<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+		{JS_DYNAMIC_VARS}
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css/{STYLE_ID}.css" />
+		<link id="mdgCss" rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-simple/{STYLE_ID}.css" />
+		<!-- This script automatically loads the other 15 JS files -->
+		<script type="text/javascript" src="{SCRIPTPATH}/includes/clientside/static/enano-lib-basic.js"></script>
+		<!--[if IE]>
+		<link rel="stylesheet" type="text/css" href="{SCRIPTPATH}/themes/{THEME_ID}/css-extra/ie-fixes.css" />
+		<![endif]-->
+		<script type="text/javascript">
+		// <![CDATA[
+			function ajaxRenameInline()
+			{
+				// This trick is _so_ vBulletin...
+				elem = document.getElementById('pagetitle');
+				if(!elem) return;
+				elem.style.display = 'none';
+				name = elem.innerHTML;
+				textbox = document.createElement('input');
+				textbox.type = 'text';
+				textbox.value = name;
+				textbox.id = 'pageheading';
+				textbox.size = name.length + 7;
+				textbox.onkeyup = function(e) { if(!e) return; if(e.keyCode == 13) ajaxRenameInlineSave(); if(e.keyCode == 27) ajaxRenameInlineCancel(); };
+				elem.parentNode.insertBefore(textbox, elem);
+			}
+			function ajaxRenameInlineSave()
+			{
+				elem1 = document.getElementById('pagetitle');
+				elem2 = document.getElementById('pageheading');
+				if(!elem1 || !elem2) return;
+				value = elem2.value;
+				elem2.parentNode.removeChild(elem2); // just destroy the thing
+				elem1.innerHTML = value;
+				elem1.style.display = 'block';
+				if(!value || value=='') return;
+				ajaxPost(stdAjaxPrefix+'&_mode=rename', 'newtitle='+escape(value), function() {
+					if(ajax.readyState == 4) {
+						alert(ajax.responseText);
+					}
+				});
+			}
+			function ajaxRenameInlineCancel()
+			{
+				elem1 = document.getElementById('pagetitle');
+				elem2 = document.getElementById('pageheading');
+				if(!elem1 || !elem2) return;
+				//value = elem2.value;
+				elem2.parentNode.removeChild(elem2); // just destroy the thing
+				//elem1.innerHTML = value;
+				elem1.style.display = 'block';
+				if(!value || value=='') return;
+			}
+			// ]]>
+		</script>
+		{ADDITIONAL_HEADERS}
+	</head>
+	<body>
+		<div id="bg">
+			<table border="0" id="stretcher" cellspacing="0" cellpadding="0"><tr><td valign="middle" id="stretcher-main">
+			<div id="rap">
+				<div id="title">
+					<img id="clover" src="{SCRIPTPATH}/themes/{THEME_ID}/images/clover.png" alt=" " />
+					<h1>{PAGE_NAME}</h1>
+				</div>
+				<div id="sidebar">
+					
+				</div>
+				<div id="maincontent">
+					<div style="float: right;">
+						<img alt=" " src="{SCRIPTPATH}/images/spacer.gif" id="ajaxloadicon" />
+					</div>
+					<div id="ajaxEditContainer">
+						
--- a/themes/stpatty/toolbar.tpl	Sun Mar 28 21:49:26 2010 -0400
+++ b/themes/stpatty/toolbar.tpl	Sun Mar 28 23:10:46 2010 -0400
@@ -1,49 +1,49 @@
 <!-- Stuff related to toolbars and clickable buttons.
-     Used mostly in the page toolbar on most pages.
-     -->
+ 		Used mostly in the page toolbar on most pages.
+ 		-->
 
 <!-- VAR toolbar_start -->
-  <div class="toolbar">
-  <ul>
+	<div class="toolbar">
+	<ul>
 <!-- ENDVAR toolbar_start -->
 <!-- VAR toolbar_button -->
-  <li>
-    <a title="{TITLE}" {FLAGS}>
-      <img alt="{TITLE}" src="{IMAGE}" />
-      <!-- BEGIN show_title -->
-      <span>{TITLE}</span>
-      <!-- END show_title -->
-    </a>
-  </li>
+	<li>
+		<a title="{TITLE}" {FLAGS}>
+			<img alt="{TITLE}" src="{IMAGE}" />
+			<!-- BEGIN show_title -->
+			<span>{TITLE}</span>
+			<!-- END show_title -->
+		</a>
+	</li>
 <!-- ENDVAR toolbar_button -->
 <!-- VAR toolbar_label -->
-  <li>
-    <span>{TITLE}</span>
-  </li>
+	<li>
+		<span>{TITLE}</span>
+	</li>
 <!-- ENDVAR toolbar_label -->
 <!-- VAR toolbar_end -->
-  </ul>
-  </div>
+	</ul>
+	</div>
 <!-- ENDVAR toolbar_end -->
 
 <!-- VAR toolbar_vert_start -->
-  <div class="toolbar_vert">
-  <ul>
+	<div class="toolbar_vert">
+	<ul>
 <!-- ENDVAR toolbar_vert_start -->
 <!-- VAR toolbar_vert_button -->
-  <li>
-    <a title="{TITLE}" {FLAGS}>
-      <img alt="{TITLE}" src="{IMAGE}" />
-      <span>{TITLE}</span>
-    </a>
-  </li>
+	<li>
+		<a title="{TITLE}" {FLAGS}>
+			<img alt="{TITLE}" src="{IMAGE}" />
+			<span>{TITLE}</span>
+		</a>
+	</li>
 <!-- ENDVAR toolbar_vert_button -->
 <!-- VAR toolbar_vert_label -->
-  <li>
-    <span>{TITLE}</span>
-  </li>
+	<li>
+		<span>{TITLE}</span>
+	</li>
 <!-- ENDVAR toolbar_vert_label -->
 <!-- VAR toolbar_vert_end -->
-  </ul>
-  </div>
+	</ul>
+	</div>
 <!-- ENDVAR toolbar_vert_end -->